]> git.sur5r.net Git - openldap/blob - servers/slapd/saslauthz.c
dfae18f99e7cfdab77b1aeae1d48cc579f5d661a
[openldap] / servers / slapd / saslauthz.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2004 The OpenLDAP Foundation.
5  * Portions Copyright 2000 Mark Adamson, Carnegie Mellon.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20
21 #include <ac/stdlib.h>
22 #include <ac/string.h>
23
24 #include "slap.h"
25
26 #include <limits.h>
27
28 #include <ldap_pvt.h>
29
30 #define SASLREGEX_REPLACE 10
31
32 #define LDAP_X_SCOPE_EXACT      ((ber_int_t) 0x0010)
33 #define LDAP_X_SCOPE_REGEX      ((ber_int_t) 0x0020)
34 #define LDAP_X_SCOPE_CHILDREN   ((ber_int_t) 0x0030)
35 #define LDAP_X_SCOPE_SUBTREE    ((ber_int_t) 0x0040)
36 #define LDAP_X_SCOPE_ONELEVEL   ((ber_int_t) 0x0050)
37
38 /*
39  * IDs in DNauthzid form can now have a type specifier, that
40  * influences how they are used in related operations.
41  *
42  * syntax: dn[.{exact|regex}]:<val>
43  *
44  * dn.exact:    the value must pass normalization and is used 
45  *              in exact DN match.
46  * dn.regex:    the value is treated as a regular expression 
47  *              in matching DN values in saslAuthz{To|From}
48  *              attributes.
49  * dn:          for backwards compatibility reasons, the value 
50  *              is treated as a regular expression, and thus 
51  *              it is not normalized nor validated; it is used
52  *              in exact or regex comparisons based on the 
53  *              context.
54  *
55  * IDs in DNauthzid form can now have a type specifier, that
56  * influences how they are used in related operations.
57  *
58  * syntax: u[.mech[/realm]]:<val>
59  * 
60  * where mech is a SIMPLE, AUTHZ, or a SASL mechanism name
61  * and realm is mechanism specific realm (separate to those
62  * which are representable as part of the principal).
63  */
64
65 typedef struct sasl_regexp {
66   char *sr_match;                                               /* regexp match pattern */
67   char *sr_replace;                                     /* regexp replace pattern */
68   regex_t sr_workspace;                                 /* workspace for regexp engine */
69   int sr_offset[SASLREGEX_REPLACE+2];   /* offsets of $1,$2... in *replace */
70 } SaslRegexp_t;
71
72 static int nSaslRegexp = 0;
73 static SaslRegexp_t *SaslRegexp = NULL;
74
75 /* What SASL proxy authorization policies are allowed? */
76 #define SASL_AUTHZ_NONE 0x00
77 #define SASL_AUTHZ_FROM 0x01
78 #define SASL_AUTHZ_TO   0x02
79 #define SASL_AUTHZ_AND  0x10
80
81 static int authz_policy = SASL_AUTHZ_NONE;
82
83 int slap_sasl_setpolicy( const char *arg )
84 {
85         int rc = LDAP_SUCCESS;
86
87         if ( strcasecmp( arg, "none" ) == 0 ) {
88                 authz_policy = SASL_AUTHZ_NONE;
89         } else if ( strcasecmp( arg, "from" ) == 0 ) {
90                 authz_policy = SASL_AUTHZ_FROM;
91         } else if ( strcasecmp( arg, "to" ) == 0 ) {
92                 authz_policy = SASL_AUTHZ_TO;
93         } else if ( strcasecmp( arg, "both" ) == 0 || strcasecmp( arg, "any" ) == 0 ) {
94                 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO;
95         } else if ( strcasecmp( arg, "all" ) == 0 ) {
96                 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND;
97         } else {
98                 rc = LDAP_OTHER;
99         }
100         return rc;
101 }
102
103 int slap_parse_user( struct berval *id, struct berval *user,
104                 struct berval *realm, struct berval *mech )
105 {
106         char    u;
107         
108         assert( id );
109         assert( id->bv_val );
110         assert( user );
111         assert( realm );
112         assert( mech );
113
114         u = id->bv_val[ 0 ];
115         
116         if ( u != 'u' && u != 'U' ) {
117                 /* called with something other than u: */
118                 return LDAP_PROTOCOL_ERROR;
119         }
120
121         /* uauthzid form:
122          *              u[.mech[/realm]]:user
123          */
124         
125         user->bv_val = strchr( id->bv_val, ':' );
126         if ( user->bv_val == NULL ) {
127                 return LDAP_PROTOCOL_ERROR;
128         }
129         user->bv_val[ 0 ] = '\0';
130         user->bv_val++;
131         user->bv_len = id->bv_len - ( user->bv_val - id->bv_val );
132
133         mech->bv_val = strchr( id->bv_val, '.' );
134         if ( mech->bv_val != NULL ) {
135                 mech->bv_val[ 0 ] = '\0';
136                 mech->bv_val++;
137
138                 realm->bv_val = strchr( mech->bv_val, '/' );
139
140                 if ( realm->bv_val ) {
141                         realm->bv_val[ 0 ] = '\0';
142                         realm->bv_val++;
143                         mech->bv_len = realm->bv_val - mech->bv_val - 1;
144                         realm->bv_len = user->bv_val - realm->bv_val - 1;
145                 } else {
146                         mech->bv_len = user->bv_val - mech->bv_val - 1;
147                 }
148
149         } else {
150                 realm->bv_val = NULL;
151         }
152
153         if ( id->bv_val[ 1 ] != '\0' ) {
154                 return LDAP_PROTOCOL_ERROR;
155         }
156
157         if ( mech->bv_val != NULL ) {
158                 assert( mech->bv_val == id->bv_val + 2 );
159
160                 AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
161                 mech->bv_val -= 2;
162         }
163
164         if ( realm->bv_val ) {
165                 assert( realm->bv_val >= id->bv_val + 2 );
166
167                 AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
168                 realm->bv_val -= 2;
169         }
170
171         /* leave "u:" before user */
172         user->bv_val -= 2;
173         user->bv_len += 2;
174         user->bv_val[ 0 ] = u;
175         user->bv_val[ 1 ] = ':';
176
177         return LDAP_SUCCESS;
178 }
179
180 static int slap_parseURI( Operation *op, struct berval *uri,
181         struct berval *base, struct berval *nbase,
182         int *scope, Filter **filter, struct berval *fstr )
183 {
184         struct berval bv;
185         int rc;
186         LDAPURLDesc *ludp;
187
188         assert( uri != NULL && uri->bv_val != NULL );
189         base->bv_val = NULL;
190         base->bv_len = 0;
191         nbase->bv_val = NULL;
192         nbase->bv_len = 0;
193         fstr->bv_val = NULL;
194         fstr->bv_len = 0;
195         *scope = -1;
196         *filter = NULL;
197
198 #ifdef NEW_LOGGING
199         LDAP_LOG( TRANSPORT, ENTRY, 
200                 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
201 #else
202         Debug( LDAP_DEBUG_TRACE,
203                 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
204 #endif
205
206         rc = LDAP_PROTOCOL_ERROR;
207         if ( !strncasecmp( uri->bv_val, "dn", sizeof( "dn" ) - 1 ) ) {
208                 bv.bv_val = uri->bv_val + sizeof( "dn" ) - 1;
209
210                 if ( bv.bv_val[ 0 ] == '.' ) {
211                         bv.bv_val++;
212
213                         if ( !strncasecmp( bv.bv_val, "exact:", sizeof( "exact:" ) - 1 ) ) {
214                                 bv.bv_val += sizeof( "exact:" ) - 1;
215                                 *scope = LDAP_X_SCOPE_EXACT;
216
217                         } else if ( !strncasecmp( bv.bv_val, "regex:", sizeof( "regex:" ) - 1 ) ) {
218                                 bv.bv_val += sizeof( "regex:" ) - 1;
219                                 *scope = LDAP_X_SCOPE_REGEX;
220
221                         } else if ( !strncasecmp( bv.bv_val, "children:", sizeof( "chldren:" ) - 1 ) ) {
222                                 bv.bv_val += sizeof( "children:" ) - 1;
223                                 *scope = LDAP_X_SCOPE_CHILDREN;
224
225                         } else if ( !strncasecmp( bv.bv_val, "subtree:", sizeof( "subtree:" ) - 1 ) ) {
226                                 bv.bv_val += sizeof( "subtree:" ) - 1;
227                                 *scope = LDAP_X_SCOPE_SUBTREE;
228
229                         } else if ( !strncasecmp( bv.bv_val, "onelevel:", sizeof( "onelevel:" ) - 1 ) ) {
230                                 bv.bv_val += sizeof( "onelevel:" ) - 1;
231                                 *scope = LDAP_X_SCOPE_ONELEVEL;
232
233                         } else {
234                                 return LDAP_PROTOCOL_ERROR;
235                         }
236                 } else {
237                         if ( bv.bv_val[ 0 ] != ':' ) {
238                                 return LDAP_PROTOCOL_ERROR;
239                         }
240                         *scope = LDAP_X_SCOPE_EXACT;
241                         bv.bv_val++;
242                 }
243
244                 bv.bv_val += strspn( bv.bv_val, " " );
245                 /* jump here in case no type specification was present
246                  * and uir was not an URI... HEADS-UP: assuming EXACT */
247 is_dn:          bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
248
249                 switch ( *scope ) {
250                 case LDAP_X_SCOPE_EXACT:
251                 case LDAP_X_SCOPE_CHILDREN:
252                 case LDAP_X_SCOPE_SUBTREE:
253                 case LDAP_X_SCOPE_ONELEVEL:
254                         rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
255                         if( rc != LDAP_SUCCESS ) {
256                                 *scope = -1;
257                         }
258                         break;
259
260                 case LDAP_X_SCOPE_REGEX:
261                         ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
262                         rc = LDAP_SUCCESS;
263                         break;
264
265                 default:
266                         *scope = -1;
267                         break;
268                 }
269
270                 return rc;
271
272         } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
273                         && ( uri->bv_val[ 1 ] == ':' 
274                                 || uri->bv_val[ 1 ] == '/' 
275                                 || uri->bv_val[ 1 ] == '.' ) )
276         {
277                 Connection      c = *op->o_conn;
278                 char            buf[ SLAP_LDAPDN_MAXLEN ];
279                 struct berval   id,
280                                 user = BER_BVNULL,
281                                 realm = BER_BVNULL,
282                                 mech = BER_BVNULL;
283
284                 if ( sizeof( buf ) <= uri->bv_len ) {
285                         return LDAP_INVALID_SYNTAX;
286                 }
287
288                 id.bv_len = uri->bv_len;
289                 id.bv_val = buf;
290                 strncpy( buf, uri->bv_val, sizeof( buf ) );
291
292                 rc = slap_parse_user( &id, &user, &realm, &mech );
293                 if ( rc != LDAP_SUCCESS ) {
294                         return rc;
295                 }
296
297                 if ( mech.bv_val ) {
298                         c.c_sasl_bind_mech = mech;
299                 } else {
300                         c.c_sasl_bind_mech.bv_val = "AUTHZ";
301                         c.c_sasl_bind_mech.bv_len = sizeof( "AUTHZ" ) - 1;
302                 }
303                 
304                 rc = slap_sasl_getdn( &c, op, user.bv_val, user.bv_len,
305                                 realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
306
307                 if ( rc == LDAP_SUCCESS ) {
308                         *scope = LDAP_X_SCOPE_EXACT;
309                 }
310
311                 return rc;
312         }
313                 
314         rc = ldap_url_parse( uri->bv_val, &ludp );
315         if ( rc == LDAP_URL_ERR_BADSCHEME ) {
316                 /* last chance: assume it's a(n exact) DN ... */
317                 bv.bv_val = uri->bv_val;
318                 *scope = LDAP_X_SCOPE_EXACT;
319                 goto is_dn;
320         }
321
322         if ( rc != LDAP_URL_SUCCESS ) {
323                 return LDAP_PROTOCOL_ERROR;
324         }
325
326         if (( ludp->lud_host && *ludp->lud_host )
327                 || ludp->lud_attrs || ludp->lud_exts )
328         {
329                 /* host part must be empty */
330                 /* attrs and extensions parts must be empty */
331                 rc = LDAP_PROTOCOL_ERROR;
332                 goto done;
333         }
334
335         /* Grab the scope */
336         *scope = ludp->lud_scope;
337
338         /* Grab the filter */
339         if ( ludp->lud_filter ) {
340                 *filter = str2filter_x( op, ludp->lud_filter );
341                 if ( *filter == NULL ) {
342                         rc = LDAP_PROTOCOL_ERROR;
343                         goto done;
344                 }
345                 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
346         }
347
348         /* Grab the searchbase */
349         ber_str2bv( ludp->lud_dn, 0, 0, base );
350         rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
351
352 done:
353         if( rc != LDAP_SUCCESS ) {
354                 if( *filter ) filter_free_x( op, *filter );
355                 base->bv_val = NULL;
356                 base->bv_len = 0;
357                 fstr->bv_val = NULL;
358                 fstr->bv_len = 0;
359         } else {
360                 /* Don't free these, return them to caller */
361                 ludp->lud_filter = NULL;
362                 ludp->lud_dn = NULL;
363         }
364
365         ldap_free_urldesc( ludp );
366         return( rc );
367 }
368
369 static int slap_sasl_rx_off(char *rep, int *off)
370 {
371         const char *c;
372         int n;
373
374         /* Precompile replace pattern. Find the $<n> placeholders */
375         off[0] = -2;
376         n = 1;
377         for ( c = rep;   *c;  c++ ) {
378                 if ( *c == '\\' && c[1] ) {
379                         c++;
380                         continue;
381                 }
382                 if ( *c == '$' ) {
383                         if ( n == SASLREGEX_REPLACE ) {
384 #ifdef NEW_LOGGING
385                                 LDAP_LOG( TRANSPORT, ERR, 
386                                         "slap_sasl_rx_off: \"%s\" has too many $n "
387                                         "placeholders (max %d)\n", rep, SASLREGEX_REPLACE, 0  );
388 #else
389                                 Debug( LDAP_DEBUG_ANY,
390                                         "SASL replace pattern %s has too many $n "
391                                                 "placeholders (max %d)\n",
392                                         rep, SASLREGEX_REPLACE, 0 );
393 #endif
394
395                                 return( LDAP_OTHER );
396                         }
397                         off[n] = c - rep;
398                         n++;
399                 }
400         }
401
402         /* Final placeholder, after the last $n */
403         off[n] = c - rep;
404         n++;
405         off[n] = -1;
406         return( LDAP_SUCCESS );
407 }
408
409 int slap_sasl_regexp_config( const char *match, const char *replace )
410 {
411         int rc;
412         SaslRegexp_t *reg;
413
414         SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
415           (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
416
417         reg = &SaslRegexp[nSaslRegexp];
418
419         reg->sr_match = ch_strdup( match );
420         reg->sr_replace = ch_strdup( replace );
421
422         /* Precompile matching pattern */
423         rc = regcomp( &reg->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
424         if ( rc ) {
425 #ifdef NEW_LOGGING
426                 LDAP_LOG( TRANSPORT, ERR, 
427                         "slap_sasl_regexp_config: \"%s\" could not be compiled.\n",
428                         reg->sr_match, 0, 0 );
429 #else
430                 Debug( LDAP_DEBUG_ANY,
431                 "SASL match pattern %s could not be compiled by regexp engine\n",
432                 reg->sr_match, 0, 0 );
433 #endif
434
435                 return( LDAP_OTHER );
436         }
437
438         rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
439         if ( rc != LDAP_SUCCESS ) return rc;
440
441         nSaslRegexp++;
442         return( LDAP_SUCCESS );
443 }
444
445
446 /* Perform replacement on regexp matches */
447 static void slap_sasl_rx_exp(
448         const char *rep,
449         const int *off,
450         regmatch_t *str,
451         const char *saslname,
452         struct berval *out,
453         void *ctx )
454 {
455         int i, n, len, insert;
456
457         /* Get the total length of the final URI */
458
459         n=1;
460         len = 0;
461         while( off[n] >= 0 ) {
462                 /* Len of next section from replacement string (x,y,z above) */
463                 len += off[n] - off[n-1] - 2;
464                 if( off[n+1] < 0)
465                         break;
466
467                 /* Len of string from saslname that matched next $i  (b,d above) */
468                 i = rep[ off[n] + 1 ]   - '0';
469                 len += str[i].rm_eo - str[i].rm_so;
470                 n++;
471         }
472         out->bv_val = sl_malloc( len + 1, ctx );
473         out->bv_len = len;
474
475         /* Fill in URI with replace string, replacing $i as we go */
476         n=1;
477         insert = 0;
478         while( off[n] >= 0) {
479                 /* Paste in next section from replacement string (x,y,z above) */
480                 len = off[n] - off[n-1] - 2;
481                 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
482                 insert += len;
483                 if( off[n+1] < 0)
484                         break;
485
486                 /* Paste in string from saslname that matched next $i  (b,d above) */
487                 i = rep[ off[n] + 1 ]   - '0';
488                 len = str[i].rm_eo - str[i].rm_so;
489                 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
490                 insert += len;
491
492                 n++;
493         }
494
495         out->bv_val[insert] = '\0';
496 }
497
498 /* Take the passed in SASL name and attempt to convert it into an
499    LDAP URI to find the matching LDAP entry, using the pattern matching
500    strings given in the saslregexp config file directive(s) */
501
502 static int slap_sasl_regexp( struct berval *in, struct berval *out,
503                 int flags, void *ctx )
504 {
505         char *saslname = in->bv_val;
506         SaslRegexp_t *reg;
507         regmatch_t sr_strings[SASLREGEX_REPLACE];       /* strings matching $1,$2 ... */
508         int i;
509
510         memset( out, 0, sizeof( *out ) );
511
512 #ifdef NEW_LOGGING
513         LDAP_LOG( TRANSPORT, ENTRY, 
514                 "slap_sasl_regexp: converting SASL name %s\n", saslname, 0, 0 );
515 #else
516         Debug( LDAP_DEBUG_TRACE, "slap_sasl_regexp: converting SASL name %s\n",
517            saslname, 0, 0 );
518 #endif
519
520         if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
521                 return( 0 );
522         }
523
524         /* Match the normalized SASL name to the saslregexp patterns */
525         for( reg = SaslRegexp,i=0;  i<nSaslRegexp;  i++,reg++ ) {
526                 if ( regexec( &reg->sr_workspace, saslname, SASLREGEX_REPLACE,
527                   sr_strings, 0)  == 0 )
528                         break;
529         }
530
531         if( i >= nSaslRegexp ) return( 0 );
532
533         /*
534          * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
535          * replace pattern of the form "x$1y$2z". The returned string needs
536          * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
537          */
538         slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
539                 sr_strings, saslname, out, ctx );
540
541 #ifdef NEW_LOGGING
542         LDAP_LOG( TRANSPORT, ENTRY, 
543                 "slap_sasl_regexp: converted SASL name to %s\n",
544                 out->bv_len ? out->bv_val : "", 0, 0 );
545 #else
546         Debug( LDAP_DEBUG_TRACE,
547                 "slap_sasl_regexp: converted SASL name to %s\n",
548                 out->bv_len ? out->bv_val : "", 0, 0 );
549 #endif
550
551         return( 1 );
552 }
553
554 /* This callback actually does some work...*/
555 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
556 {
557         struct berval *ndn = o->o_callback->sc_private;
558
559         if (rs->sr_type != REP_SEARCH) return 0;
560
561         /* We only want to be called once */
562         if( ndn->bv_val ) {
563                 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
564                 ndn->bv_val = NULL;
565                 ndn->bv_len = 0;
566
567 #ifdef NEW_LOGGING
568                 LDAP_LOG( TRANSPORT, DETAIL1,
569                         "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
570 #else
571                 Debug( LDAP_DEBUG_TRACE,
572                         "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
573 #endif
574                 return -1;
575         }
576
577         ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
578         return 0;
579 }
580
581
582 typedef struct smatch_info {
583         struct berval *dn;
584         int match;
585 } smatch_info;
586
587 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
588 {
589         smatch_info *sm = o->o_callback->sc_private;
590
591         if (rs->sr_type != REP_SEARCH) return 0;
592
593         if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
594                 sm->match = 1;
595                 return -1;      /* short-circuit the search */
596         }
597
598         return 1;
599 }
600
601 /*
602  * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
603  * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
604  * the rule must be used as an internal search for entries. If that search
605  * returns the *assertDN entry, the match is successful.
606  *
607  * The assertDN should not have the dn: prefix
608  */
609
610 static
611 int slap_sasl_match( Operation *opx, struct berval *rule,
612         struct berval *assertDN, struct berval *authc )
613 {
614         int rc; 
615         regex_t reg;
616         smatch_info sm;
617         slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
618         Operation op = {0};
619         SlapReply rs = {REP_RESULT};
620
621 #ifdef NEW_LOGGING
622         LDAP_LOG( TRANSPORT, ENTRY, 
623                 "slap_sasl_match: comparing DN %s to rule %s\n", 
624                 assertDN->bv_val, rule->bv_val,0 );
625 #else
626         Debug( LDAP_DEBUG_TRACE,
627            "===>slap_sasl_match: comparing DN %s to rule %s\n",
628                 assertDN->bv_val, rule->bv_val, 0 );
629 #endif
630
631         rc = slap_parseURI( opx, rule, &op.o_req_dn,
632                 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
633                 &op.ors_filterstr );
634         if( rc != LDAP_SUCCESS ) goto CONCLUDED;
635
636         switch ( op.oq_search.rs_scope ) {
637         case LDAP_X_SCOPE_EXACT:
638 exact_match:
639                 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
640                         rc = LDAP_SUCCESS;
641                 } else {
642                         rc = LDAP_INAPPROPRIATE_AUTH;
643                 }
644                 goto CONCLUDED;
645
646         case LDAP_X_SCOPE_CHILDREN:
647         case LDAP_X_SCOPE_SUBTREE:
648         case LDAP_X_SCOPE_ONELEVEL:
649         {
650                 int     d = assertDN->bv_len - op.o_req_ndn.bv_len;
651
652                 rc = LDAP_INAPPROPRIATE_AUTH;
653
654                 if ( d == 0 && op.oq_search.rs_scope == LDAP_X_SCOPE_SUBTREE ) {
655                         goto exact_match;
656
657                 } else if ( d > 0 ) {
658                         struct berval bv;
659
660                         bv.bv_len = op.o_req_ndn.bv_len;
661                         bv.bv_val = assertDN->bv_val + d;
662
663                         if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
664                                 switch ( op.oq_search.rs_scope ) {
665                                 case LDAP_X_SCOPE_SUBTREE:
666                                 case LDAP_X_SCOPE_CHILDREN:
667                                         rc = LDAP_SUCCESS;
668                                         break;
669
670                                 case LDAP_X_SCOPE_ONELEVEL:
671                                 {
672                                         struct berval   pdn;
673
674                                         dnParent( assertDN, &pdn );
675                                         /* the common portion of the DN
676                                          * already matches, so only check
677                                          * if parent DN of assertedDN 
678                                          * is all the pattern */
679                                         if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
680                                                 rc = LDAP_SUCCESS;
681                                         }
682                                         break;
683                                 }
684                                 default:
685                                         /* at present, impossible */
686                                         assert( 0 );
687                                 }
688                         }
689                 }
690                 goto CONCLUDED;
691         }
692
693         case LDAP_X_SCOPE_REGEX:
694                 rc = regcomp(&reg, op.o_req_ndn.bv_val,
695                         REG_EXTENDED|REG_ICASE|REG_NOSUB);
696                 if ( rc == 0 ) {
697                         rc = regexec(&reg, assertDN->bv_val, 0, NULL, 0);
698                         regfree( &reg );
699                 }
700                 if ( rc == 0 ) {
701                         rc = LDAP_SUCCESS;
702                 } else {
703                         rc = LDAP_INAPPROPRIATE_AUTH;
704                 }
705                 goto CONCLUDED;
706
707         default:
708                 break;
709         }
710
711         /* Must run an internal search. */
712         if ( op.oq_search.rs_filter == NULL ) {
713                 rc = LDAP_FILTER_ERROR;
714                 goto CONCLUDED;
715         }
716
717 #ifdef NEW_LOGGING
718         LDAP_LOG( TRANSPORT, DETAIL1, 
719                 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
720                 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
721 #else
722         Debug( LDAP_DEBUG_TRACE,
723            "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
724            op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
725 #endif
726
727         op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
728         if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
729                 rc = LDAP_INAPPROPRIATE_AUTH;
730                 goto CONCLUDED;
731         }
732
733         sm.dn = assertDN;
734         sm.match = 0;
735         cb.sc_private = &sm;
736
737         op.o_tag = LDAP_REQ_SEARCH;
738         op.o_protocol = LDAP_VERSION3;
739         op.o_ndn = *authc;
740         op.o_callback = &cb;
741         op.o_time = slap_get_time();
742         op.o_do_not_cache = 1;
743         op.o_is_auth_check = 1;
744         op.o_threadctx = opx->o_threadctx;
745         op.o_tmpmemctx = opx->o_tmpmemctx;
746         op.o_tmpmfuncs = opx->o_tmpmfuncs;
747 #ifdef LDAP_SLAPI
748         op.o_pb = opx->o_pb;
749 #endif
750         op.o_conn = opx->o_conn;
751         op.o_connid = opx->o_connid;
752         /* use req_ndn as req_dn instead of non-pretty base of uri */
753         if( !BER_BVISNULL( &op.o_req_dn ) ) ch_free( op.o_req_dn.bv_val );
754         ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
755         op.oq_search.rs_slimit = 1;
756         op.oq_search.rs_tlimit = SLAP_NO_LIMIT;
757         op.o_sync_slog_size = -1;
758
759         op.o_bd->be_search( &op, &rs );
760
761         if (sm.match) {
762                 rc = LDAP_SUCCESS;
763         } else {
764                 rc = LDAP_INAPPROPRIATE_AUTH;
765         }
766
767 CONCLUDED:
768         if( !BER_BVISNULL( &op.o_req_dn ) ) sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
769         if( !BER_BVISNULL( &op.o_req_ndn ) ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
770         if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
771         if( op.ors_filterstr.bv_val ) ch_free( op.ors_filterstr.bv_val );
772
773 #ifdef NEW_LOGGING
774         LDAP_LOG( TRANSPORT, ENTRY, 
775                 "slap_sasl_match: comparison returned %d\n", rc, 0, 0 );
776 #else
777         Debug( LDAP_DEBUG_TRACE,
778            "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
779 #endif
780
781         return( rc );
782 }
783
784
785 /*
786  * This function answers the question, "Can this ID authorize to that ID?",
787  * based on authorization rules. The rules are stored in the *searchDN, in the
788  * attribute named by *attr. If any of those rules map to the *assertDN, the
789  * authorization is approved.
790  *
791  * The DNs should not have the dn: prefix
792  */
793 static int
794 slap_sasl_check_authz( Operation *op,
795         struct berval *searchDN,
796         struct berval *assertDN,
797         AttributeDescription *ad,
798         struct berval *authc )
799 {
800         int i, rc;
801         BerVarray vals=NULL;
802
803 #ifdef NEW_LOGGING
804         LDAP_LOG( TRANSPORT, ENTRY, 
805                 "slap_sasl_check_authz: does %s match %s rule in %s?\n",
806             assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
807 #else
808         Debug( LDAP_DEBUG_TRACE,
809            "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
810            assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
811 #endif
812
813         rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
814         if( rc != LDAP_SUCCESS ) goto COMPLETE;
815
816         /* Check if the *assertDN matches any **vals */
817         if( vals != NULL ) {
818                 for( i=0; vals[i].bv_val != NULL; i++ ) {
819                         rc = slap_sasl_match( op, &vals[i], assertDN, authc );
820                         if ( rc == LDAP_SUCCESS ) goto COMPLETE;
821                 }
822         }
823         rc = LDAP_INAPPROPRIATE_AUTH;
824
825 COMPLETE:
826         if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
827
828 #ifdef NEW_LOGGING
829         LDAP_LOG( TRANSPORT, RESULTS, 
830                 "slap_sasl_check_authz: %s check returning %s\n", 
831                 ad->ad_cname.bv_val, rc, 0 );
832 #else
833         Debug( LDAP_DEBUG_TRACE,
834            "<==slap_sasl_check_authz: %s check returning %d\n",
835                 ad->ad_cname.bv_val, rc, 0);
836 #endif
837
838         return( rc );
839 }
840
841 /*
842  * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
843  * return the LDAP DN to which it matches. The SASL regexp rules in the config
844  * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
845  * search with scope=base), just return the URI (or its searchbase). Otherwise
846  * an internal search must be done, and if that search returns exactly one
847  * entry, return the DN of that one entry.
848  */
849 void slap_sasl2dn( Operation *opx,
850         struct berval *saslname, struct berval *sasldn, int flags )
851 {
852         int rc;
853         slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
854         Operation op = {0};
855         SlapReply rs = {REP_RESULT};
856         struct berval regout = BER_BVNULL;
857
858 #ifdef NEW_LOGGING
859         LDAP_LOG( TRANSPORT, ENTRY, 
860                 "slap_sasl2dn: converting SASL name %s to DN.\n",
861                 saslname->bv_val, 0, 0 );
862 #else
863         Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
864                 "converting SASL name %s to a DN\n",
865                 saslname->bv_val, 0,0 );
866 #endif
867
868         sasldn->bv_val = NULL;
869         sasldn->bv_len = 0;
870         cb.sc_private = sasldn;
871
872         /* Convert the SASL name into a minimal URI */
873         if( !slap_sasl_regexp( saslname, &regout, flags, opx->o_tmpmemctx ) ) {
874                 goto FINISHED;
875         }
876
877         rc = slap_parseURI( opx, &regout, &op.o_req_dn,
878                 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
879                 &op.ors_filterstr );
880         if( regout.bv_val ) sl_free( regout.bv_val, opx->o_tmpmemctx );
881         if( rc != LDAP_SUCCESS ) {
882                 goto FINISHED;
883         }
884
885         /* Must do an internal search */
886         op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
887
888         switch ( op.oq_search.rs_scope ) {
889         case LDAP_X_SCOPE_EXACT:
890                 *sasldn = op.o_req_ndn;
891                 op.o_req_ndn.bv_len = 0;
892                 op.o_req_ndn.bv_val = NULL;
893                 /* intentionally continue to next case */
894
895         case LDAP_X_SCOPE_REGEX:
896         case LDAP_X_SCOPE_SUBTREE:
897         case LDAP_X_SCOPE_CHILDREN:
898         case LDAP_X_SCOPE_ONELEVEL:
899                 /* correctly parsed, but illegal */
900                 goto FINISHED;
901
902         case LDAP_SCOPE_BASE:
903         case LDAP_SCOPE_ONELEVEL:
904         case LDAP_SCOPE_SUBTREE:
905 #ifdef LDAP_SCOPE_SUBORDINATE
906         case LDAP_SCOPE_SUBORDINATE:
907 #endif
908                 /* do a search */
909                 break;
910
911         default:
912                 /* catch unhandled cases (there shouldn't be) */
913                 assert( 0 );
914         }
915
916 #ifdef NEW_LOGGING
917         LDAP_LOG( TRANSPORT, DETAIL1, 
918                 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
919                 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
920 #else
921         Debug( LDAP_DEBUG_TRACE,
922                 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
923                 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
924 #endif
925
926         if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
927                 goto FINISHED;
928         }
929
930         op.o_conn = opx->o_conn;
931         op.o_connid = opx->o_connid;
932         op.o_tag = LDAP_REQ_SEARCH;
933         op.o_protocol = LDAP_VERSION3;
934         op.o_ndn = opx->o_conn->c_ndn;
935         op.o_callback = &cb;
936         op.o_time = slap_get_time();
937         op.o_do_not_cache = 1;
938         op.o_is_auth_check = 1;
939         op.o_threadctx = opx->o_threadctx;
940         op.o_tmpmemctx = opx->o_tmpmemctx;
941         op.o_tmpmfuncs = opx->o_tmpmfuncs;
942 #ifdef LDAP_SLAPI
943         op.o_pb = opx->o_pb;
944 #endif
945         op.oq_search.rs_deref = LDAP_DEREF_NEVER;
946         op.oq_search.rs_slimit = 1;
947         op.oq_search.rs_tlimit = SLAP_NO_LIMIT;
948         op.oq_search.rs_attrsonly = 1;
949         /* use req_ndn as req_dn instead of non-pretty base of uri */
950         if( !BER_BVISNULL( &op.o_req_dn ) ) ch_free( op.o_req_dn.bv_val );
951         ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
952
953         op.o_bd->be_search( &op, &rs );
954         
955 FINISHED:
956         if( sasldn->bv_len ) {
957                 opx->o_conn->c_authz_backend = op.o_bd;
958         }
959         if( !BER_BVISNULL( &op.o_req_dn ) ) sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
960         if( !BER_BVISNULL( &op.o_req_ndn ) ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
961         if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
962         if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
963
964 #ifdef NEW_LOGGING
965         LDAP_LOG( TRANSPORT, ENTRY, 
966                 "slap_sasl2dn: Converted SASL name to %s\n",
967                 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
968 #else
969         Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
970                 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
971 #endif
972
973         return;
974 }
975
976
977 /* Check if a bind can SASL authorize to another identity.
978  * The DNs should not have the dn: prefix
979  */
980
981 int slap_sasl_authorized( Operation *op,
982         struct berval *authcDN, struct berval *authzDN )
983 {
984         int rc = LDAP_INAPPROPRIATE_AUTH;
985
986         /* User binding as anonymous */
987         if ( authzDN == NULL ) {
988                 rc = LDAP_SUCCESS;
989                 goto DONE;
990         }
991
992 #ifdef NEW_LOGGING
993         LDAP_LOG( TRANSPORT, ENTRY, 
994                 "slap_sasl_authorized: can %s become %s?\n", 
995                 authcDN->bv_val, authzDN->bv_val, 0 );
996 #else
997         Debug( LDAP_DEBUG_TRACE,
998            "==>slap_sasl_authorized: can %s become %s?\n",
999                 authcDN->bv_val, authzDN->bv_val, 0 );
1000 #endif
1001
1002         /* If person is authorizing to self, succeed */
1003         if ( dn_match( authcDN, authzDN ) ) {
1004                 rc = LDAP_SUCCESS;
1005                 goto DONE;
1006         }
1007
1008         /* Allow the manager to authorize as any DN. */
1009         if( op->o_conn->c_authz_backend &&
1010                 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
1011         {
1012                 rc = LDAP_SUCCESS;
1013                 goto DONE;
1014         }
1015
1016         /* Check source rules */
1017         if( authz_policy & SASL_AUTHZ_TO ) {
1018                 rc = slap_sasl_check_authz( op, authcDN, authzDN,
1019                         slap_schema.si_ad_saslAuthzTo, authcDN );
1020                 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
1021                         goto DONE;
1022                 }
1023         }
1024
1025         /* Check destination rules */
1026         if( authz_policy & SASL_AUTHZ_FROM ) {
1027                 rc = slap_sasl_check_authz( op, authzDN, authcDN,
1028                         slap_schema.si_ad_saslAuthzFrom, authcDN );
1029                 if( rc == LDAP_SUCCESS ) {
1030                         goto DONE;
1031                 }
1032         }
1033
1034         rc = LDAP_INAPPROPRIATE_AUTH;
1035
1036 DONE:
1037
1038 #ifdef NEW_LOGGING
1039         LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );
1040 #else
1041         Debug( LDAP_DEBUG_TRACE,
1042                 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );
1043 #endif
1044
1045         return( rc );
1046 }