]> git.sur5r.net Git - openldap/blob - servers/slapd/saslauthz.c
swap be_isroot and be_isroot_dn symbols
[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                 }
237
238                 if ( bv.bv_val[ 0 ] != ':' ) {
239                         return LDAP_PROTOCOL_ERROR;
240                 }
241                 bv.bv_val++;
242
243                 bv.bv_val += strspn( bv.bv_val, " " );
244                 /* jump here in case no type specification was present
245                  * and uir was not an URI... HEADS-UP: assuming EXACT */
246 is_dn:          bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
247
248                 switch ( *scope ) {
249                 case LDAP_X_SCOPE_EXACT:
250                 case LDAP_X_SCOPE_CHILDREN:
251                 case LDAP_X_SCOPE_SUBTREE:
252                 case LDAP_X_SCOPE_ONELEVEL:
253                         rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
254                         if( rc != LDAP_SUCCESS ) {
255                                 *scope = -1;
256                         }
257                         break;
258
259                 case LDAP_X_SCOPE_REGEX:
260                         ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
261                         rc = LDAP_SUCCESS;
262                         break;
263
264                 default:
265                         *scope = -1;
266                         break;
267                 }
268
269                 return rc;
270
271         } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
272                         && ( uri->bv_val[ 1 ] == ':' 
273                                 || uri->bv_val[ 1 ] == '/' 
274                                 || uri->bv_val[ 1 ] == '.' ) )
275         {
276                 Connection      c = *op->o_conn;
277                 char            buf[ SLAP_LDAPDN_MAXLEN ];
278                 struct berval   id,
279                                 user = { 0, NULL },
280                                 realm = { 0, NULL },
281                                 mech = { 0, NULL };
282
283                 if ( sizeof( buf ) <= uri->bv_len ) {
284                         return LDAP_INVALID_SYNTAX;
285                 }
286
287                 id.bv_len = uri->bv_len;
288                 id.bv_val = buf;
289                 strncpy( buf, uri->bv_val, sizeof( buf ) );
290
291                 rc = slap_parse_user( &id, &user, &realm, &mech );
292                 if ( rc != LDAP_SUCCESS ) {
293                         return rc;
294                 }
295
296                 if ( mech.bv_val ) {
297                         c.c_sasl_bind_mech = mech;
298                 } else {
299                         c.c_sasl_bind_mech.bv_val = "AUTHZ";
300                         c.c_sasl_bind_mech.bv_len = sizeof( "AUTHZ" ) - 1;
301                 }
302                 
303                 rc = slap_sasl_getdn( &c, op, user.bv_val, user.bv_len,
304                                 realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
305
306                 if ( rc == LDAP_SUCCESS ) {
307                         *scope = LDAP_X_SCOPE_EXACT;
308                 }
309
310                 return rc;
311         }
312                 
313         rc = ldap_url_parse( uri->bv_val, &ludp );
314         if ( rc == LDAP_URL_ERR_BADSCHEME ) {
315                 /* last chance: assume it's a(n exact) DN ... */
316                 bv.bv_val = uri->bv_val;
317                 *scope = LDAP_X_SCOPE_EXACT;
318                 goto is_dn;
319         }
320
321         if ( rc != LDAP_URL_SUCCESS ) {
322                 return LDAP_PROTOCOL_ERROR;
323         }
324
325         if (( ludp->lud_host && *ludp->lud_host )
326                 || ludp->lud_attrs || ludp->lud_exts )
327         {
328                 /* host part must be empty */
329                 /* attrs and extensions parts must be empty */
330                 rc = LDAP_PROTOCOL_ERROR;
331                 goto done;
332         }
333
334         /* Grab the scope */
335         *scope = ludp->lud_scope;
336
337         /* Grab the filter */
338         if ( ludp->lud_filter ) {
339                 *filter = str2filter_x( op, ludp->lud_filter );
340                 if ( *filter == NULL ) {
341                         rc = LDAP_PROTOCOL_ERROR;
342                         goto done;
343                 }
344                 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
345         }
346
347         /* Grab the searchbase */
348         ber_str2bv( ludp->lud_dn, 0, 0, base );
349         rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
350
351 done:
352         if( rc != LDAP_SUCCESS ) {
353                 if( *filter ) filter_free_x( op, *filter );
354                 base->bv_val = NULL;
355                 base->bv_len = 0;
356                 fstr->bv_val = NULL;
357                 fstr->bv_len = 0;
358         } else {
359                 /* Don't free these, return them to caller */
360                 ludp->lud_filter = NULL;
361                 ludp->lud_dn = NULL;
362         }
363
364         ldap_free_urldesc( ludp );
365         return( rc );
366 }
367
368 static int slap_sasl_rx_off(char *rep, int *off)
369 {
370         const char *c;
371         int n;
372
373         /* Precompile replace pattern. Find the $<n> placeholders */
374         off[0] = -2;
375         n = 1;
376         for ( c = rep;   *c;  c++ ) {
377                 if ( *c == '\\' && c[1] ) {
378                         c++;
379                         continue;
380                 }
381                 if ( *c == '$' ) {
382                         if ( n == SASLREGEX_REPLACE ) {
383 #ifdef NEW_LOGGING
384                                 LDAP_LOG( TRANSPORT, ERR, 
385                                         "slap_sasl_rx_off: \"%s\" has too many $n "
386                                         "placeholders (max %d)\n", rep, SASLREGEX_REPLACE, 0  );
387 #else
388                                 Debug( LDAP_DEBUG_ANY,
389                                         "SASL replace pattern %s has too many $n "
390                                                 "placeholders (max %d)\n",
391                                         rep, SASLREGEX_REPLACE, 0 );
392 #endif
393
394                                 return( LDAP_OTHER );
395                         }
396                         off[n] = c - rep;
397                         n++;
398                 }
399         }
400
401         /* Final placeholder, after the last $n */
402         off[n] = c - rep;
403         n++;
404         off[n] = -1;
405         return( LDAP_SUCCESS );
406 }
407
408 int slap_sasl_regexp_config( const char *match, const char *replace )
409 {
410         int rc;
411         SaslRegexp_t *reg;
412
413         SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
414           (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
415
416         reg = &SaslRegexp[nSaslRegexp];
417
418         reg->sr_match = ch_strdup( match );
419         reg->sr_replace = ch_strdup( replace );
420
421         /* Precompile matching pattern */
422         rc = regcomp( &reg->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
423         if ( rc ) {
424 #ifdef NEW_LOGGING
425                 LDAP_LOG( TRANSPORT, ERR, 
426                         "slap_sasl_regexp_config: \"%s\" could not be compiled.\n",
427                         reg->sr_match, 0, 0 );
428 #else
429                 Debug( LDAP_DEBUG_ANY,
430                 "SASL match pattern %s could not be compiled by regexp engine\n",
431                 reg->sr_match, 0, 0 );
432 #endif
433
434                 return( LDAP_OTHER );
435         }
436
437         rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
438         if ( rc != LDAP_SUCCESS ) return rc;
439
440         nSaslRegexp++;
441         return( LDAP_SUCCESS );
442 }
443
444
445 /* Perform replacement on regexp matches */
446 static void slap_sasl_rx_exp(
447         const char *rep,
448         const int *off,
449         regmatch_t *str,
450         const char *saslname,
451         struct berval *out,
452         void *ctx )
453 {
454         int i, n, len, insert;
455
456         /* Get the total length of the final URI */
457
458         n=1;
459         len = 0;
460         while( off[n] >= 0 ) {
461                 /* Len of next section from replacement string (x,y,z above) */
462                 len += off[n] - off[n-1] - 2;
463                 if( off[n+1] < 0)
464                         break;
465
466                 /* Len of string from saslname that matched next $i  (b,d above) */
467                 i = rep[ off[n] + 1 ]   - '0';
468                 len += str[i].rm_eo - str[i].rm_so;
469                 n++;
470         }
471         out->bv_val = sl_malloc( len + 1, ctx );
472         out->bv_len = len;
473
474         /* Fill in URI with replace string, replacing $i as we go */
475         n=1;
476         insert = 0;
477         while( off[n] >= 0) {
478                 /* Paste in next section from replacement string (x,y,z above) */
479                 len = off[n] - off[n-1] - 2;
480                 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
481                 insert += len;
482                 if( off[n+1] < 0)
483                         break;
484
485                 /* Paste in string from saslname that matched next $i  (b,d above) */
486                 i = rep[ off[n] + 1 ]   - '0';
487                 len = str[i].rm_eo - str[i].rm_so;
488                 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
489                 insert += len;
490
491                 n++;
492         }
493
494         out->bv_val[insert] = '\0';
495 }
496
497 /* Take the passed in SASL name and attempt to convert it into an
498    LDAP URI to find the matching LDAP entry, using the pattern matching
499    strings given in the saslregexp config file directive(s) */
500
501 static int slap_sasl_regexp( struct berval *in, struct berval *out,
502                 int flags, void *ctx )
503 {
504         char *saslname = in->bv_val;
505         SaslRegexp_t *reg;
506         regmatch_t sr_strings[SASLREGEX_REPLACE];       /* strings matching $1,$2 ... */
507         int i;
508
509         memset( out, 0, sizeof( *out ) );
510
511 #ifdef NEW_LOGGING
512         LDAP_LOG( TRANSPORT, ENTRY, 
513                 "slap_sasl_regexp: converting SASL name %s\n", saslname, 0, 0 );
514 #else
515         Debug( LDAP_DEBUG_TRACE, "slap_sasl_regexp: converting SASL name %s\n",
516            saslname, 0, 0 );
517 #endif
518
519         if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
520                 return( 0 );
521         }
522
523         /* Match the normalized SASL name to the saslregexp patterns */
524         for( reg = SaslRegexp,i=0;  i<nSaslRegexp;  i++,reg++ ) {
525                 if ( regexec( &reg->sr_workspace, saslname, SASLREGEX_REPLACE,
526                   sr_strings, 0)  == 0 )
527                         break;
528         }
529
530         if( i >= nSaslRegexp ) return( 0 );
531
532         /*
533          * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
534          * replace pattern of the form "x$1y$2z". The returned string needs
535          * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
536          */
537         slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
538                 sr_strings, saslname, out, ctx );
539
540 #ifdef NEW_LOGGING
541         LDAP_LOG( TRANSPORT, ENTRY, 
542                 "slap_sasl_regexp: converted SASL name to %s\n",
543                 out->bv_len ? out->bv_val : "", 0, 0 );
544 #else
545         Debug( LDAP_DEBUG_TRACE,
546                 "slap_sasl_regexp: converted SASL name to %s\n",
547                 out->bv_len ? out->bv_val : "", 0, 0 );
548 #endif
549
550         return( 1 );
551 }
552
553 /* This callback actually does some work...*/
554 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
555 {
556         struct berval *ndn = o->o_callback->sc_private;
557
558         if (rs->sr_type != REP_SEARCH) return 0;
559
560         /* We only want to be called once */
561         if( ndn->bv_val ) {
562                 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
563                 ndn->bv_val = NULL;
564                 ndn->bv_len = 0;
565
566 #ifdef NEW_LOGGING
567                 LDAP_LOG( TRANSPORT, DETAIL1,
568                         "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
569 #else
570                 Debug( LDAP_DEBUG_TRACE,
571                         "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
572 #endif
573                 return -1;
574         }
575
576         ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
577         return 0;
578 }
579
580
581 typedef struct smatch_info {
582         struct berval *dn;
583         int match;
584 } smatch_info;
585
586 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
587 {
588         smatch_info *sm = o->o_callback->sc_private;
589
590         if (rs->sr_type != REP_SEARCH) return 0;
591
592         if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
593                 sm->match = 1;
594                 return -1;      /* short-circuit the search */
595         }
596
597         return 1;
598 }
599
600 /*
601  * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
602  * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
603  * the rule must be used as an internal search for entries. If that search
604  * returns the *assertDN entry, the match is successful.
605  *
606  * The assertDN should not have the dn: prefix
607  */
608
609 static
610 int slap_sasl_match( Operation *opx, struct berval *rule,
611         struct berval *assertDN, struct berval *authc )
612 {
613         int rc; 
614         regex_t reg;
615         smatch_info sm;
616         slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
617         Operation op = {0};
618         SlapReply rs = {REP_RESULT};
619
620 #ifdef NEW_LOGGING
621         LDAP_LOG( TRANSPORT, ENTRY, 
622                 "slap_sasl_match: comparing DN %s to rule %s\n", 
623                 assertDN->bv_val, rule->bv_val,0 );
624 #else
625         Debug( LDAP_DEBUG_TRACE,
626            "===>slap_sasl_match: comparing DN %s to rule %s\n",
627                 assertDN->bv_val, rule->bv_val, 0 );
628 #endif
629
630         rc = slap_parseURI( opx, rule, &op.o_req_dn,
631                 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
632                 &op.ors_filterstr );
633         if( rc != LDAP_SUCCESS ) goto CONCLUDED;
634
635         /* Massive shortcut: search scope == base */
636         switch ( op.oq_search.rs_scope ) {
637         case LDAP_SCOPE_BASE:
638         case LDAP_X_SCOPE_EXACT:
639 exact_match:
640                 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
641                         rc = LDAP_SUCCESS;
642                 } else {
643                         rc = LDAP_INAPPROPRIATE_AUTH;
644                 }
645                 goto CONCLUDED;
646
647         case LDAP_X_SCOPE_CHILDREN:
648         case LDAP_X_SCOPE_SUBTREE:
649         case LDAP_X_SCOPE_ONELEVEL:
650         {
651                 int     d = assertDN->bv_len - op.o_req_ndn.bv_len;
652
653                 rc = LDAP_INAPPROPRIATE_AUTH;
654
655                 if ( d == 0 && op.oq_search.rs_scope == LDAP_X_SCOPE_SUBTREE ) {
656                         goto exact_match;
657
658                 } else if ( d > 0 ) {
659                         struct berval bv;
660
661                         bv.bv_len = op.o_req_ndn.bv_len;
662                         bv.bv_val = assertDN->bv_val + d;
663
664                         if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
665                                 switch ( op.oq_search.rs_scope ) {
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         op.o_req_dn = op.o_req_ndn;
753
754         op.o_bd->be_search( &op, &rs );
755
756         if (sm.match) {
757                 rc = LDAP_SUCCESS;
758         } else {
759                 rc = LDAP_INAPPROPRIATE_AUTH;
760         }
761
762 CONCLUDED:
763         if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
764         if( op.o_req_ndn.bv_len ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
765         if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
766         if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
767
768 #ifdef NEW_LOGGING
769         LDAP_LOG( TRANSPORT, ENTRY, 
770                 "slap_sasl_match: comparison returned %d\n", rc, 0, 0 );
771 #else
772         Debug( LDAP_DEBUG_TRACE,
773            "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
774 #endif
775
776         return( rc );
777 }
778
779
780 /*
781  * This function answers the question, "Can this ID authorize to that ID?",
782  * based on authorization rules. The rules are stored in the *searchDN, in the
783  * attribute named by *attr. If any of those rules map to the *assertDN, the
784  * authorization is approved.
785  *
786  * The DNs should not have the dn: prefix
787  */
788 static int
789 slap_sasl_check_authz( Operation *op,
790         struct berval *searchDN,
791         struct berval *assertDN,
792         AttributeDescription *ad,
793         struct berval *authc )
794 {
795         int i, rc;
796         BerVarray vals=NULL;
797
798 #ifdef NEW_LOGGING
799         LDAP_LOG( TRANSPORT, ENTRY, 
800                 "slap_sasl_check_authz: does %s match %s rule in %s?\n",
801             assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
802 #else
803         Debug( LDAP_DEBUG_TRACE,
804            "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
805            assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
806 #endif
807
808         rc = backend_attribute( op, NULL,
809                 searchDN, ad, &vals );
810         if( rc != LDAP_SUCCESS ) goto COMPLETE;
811
812         /* Check if the *assertDN matches any **vals */
813         if( vals != NULL ) {
814                 for( i=0; vals[i].bv_val != NULL; i++ ) {
815                         rc = slap_sasl_match( op, &vals[i], assertDN, authc );
816                         if ( rc == LDAP_SUCCESS ) goto COMPLETE;
817                 }
818         }
819         rc = LDAP_INAPPROPRIATE_AUTH;
820
821 COMPLETE:
822         if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
823
824 #ifdef NEW_LOGGING
825         LDAP_LOG( TRANSPORT, RESULTS, 
826                 "slap_sasl_check_authz: %s check returning %s\n", 
827                 ad->ad_cname.bv_val, rc, 0 );
828 #else
829         Debug( LDAP_DEBUG_TRACE,
830            "<==slap_sasl_check_authz: %s check returning %d\n",
831                 ad->ad_cname.bv_val, rc, 0);
832 #endif
833
834         return( rc );
835 }
836
837 /*
838  * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
839  * return the LDAP DN to which it matches. The SASL regexp rules in the config
840  * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
841  * search with scope=base), just return the URI (or its searchbase). Otherwise
842  * an internal search must be done, and if that search returns exactly one
843  * entry, return the DN of that one entry.
844  */
845 void slap_sasl2dn( Operation *opx,
846         struct berval *saslname, struct berval *sasldn, int flags )
847 {
848         int rc;
849         slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
850         Operation op = {0};
851         SlapReply rs = {REP_RESULT};
852         struct berval regout = { 0, NULL };
853
854 #ifdef NEW_LOGGING
855         LDAP_LOG( TRANSPORT, ENTRY, 
856                 "slap_sasl2dn: converting SASL name %s to DN.\n",
857                 saslname->bv_val, 0, 0 );
858 #else
859         Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
860                 "converting SASL name %s to a DN\n",
861                 saslname->bv_val, 0,0 );
862 #endif
863
864         sasldn->bv_val = NULL;
865         sasldn->bv_len = 0;
866         cb.sc_private = sasldn;
867
868         /* Convert the SASL name into a minimal URI */
869         if( !slap_sasl_regexp( saslname, &regout, flags, opx->o_tmpmemctx ) ) {
870                 goto FINISHED;
871         }
872
873         rc = slap_parseURI( opx, &regout, &op.o_req_dn,
874                 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
875                 &op.ors_filterstr );
876         if( regout.bv_val ) sl_free( regout.bv_val, opx->o_tmpmemctx );
877         if( rc != LDAP_SUCCESS ) {
878                 goto FINISHED;
879         }
880
881         /* Must do an internal search */
882         op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
883
884         /* Massive shortcut: search scope == base */
885         switch ( op.oq_search.rs_scope ) {
886         case LDAP_SCOPE_BASE:
887         case LDAP_X_SCOPE_EXACT:
888                 *sasldn = op.o_req_ndn;
889                 op.o_req_ndn.bv_len = 0;
890                 op.o_req_ndn.bv_val = NULL;
891                 /* intentionally continue to next case */
892
893         case LDAP_X_SCOPE_REGEX:
894         case LDAP_X_SCOPE_SUBTREE:
895         case LDAP_X_SCOPE_CHILDREN:
896         case LDAP_X_SCOPE_ONELEVEL:
897                 /* correctly parsed, but illegal */
898                 goto FINISHED;
899
900         case LDAP_SCOPE_ONELEVEL:
901         case LDAP_SCOPE_SUBTREE:
902 #ifdef LDAP_SCOPE_SUBORDINATE
903         case LDAP_SCOPE_SUBORDINATE:
904 #endif
905                 /* do a search */
906                 break;
907
908         default:
909                 /* catch unhandled cases (there shouldn't be) */
910                 assert( 0 );
911         }
912
913 #ifdef NEW_LOGGING
914         LDAP_LOG( TRANSPORT, DETAIL1, 
915                 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
916                 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
917 #else
918         Debug( LDAP_DEBUG_TRACE,
919                 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
920                 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
921 #endif
922
923         if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
924                 goto FINISHED;
925         }
926
927         op.o_conn = opx->o_conn;
928         op.o_connid = opx->o_connid;
929         op.o_tag = LDAP_REQ_SEARCH;
930         op.o_protocol = LDAP_VERSION3;
931         op.o_ndn = opx->o_conn->c_ndn;
932         op.o_callback = &cb;
933         op.o_time = slap_get_time();
934         op.o_do_not_cache = 1;
935         op.o_is_auth_check = 1;
936         op.o_threadctx = opx->o_threadctx;
937         op.o_tmpmemctx = opx->o_tmpmemctx;
938         op.o_tmpmfuncs = opx->o_tmpmfuncs;
939 #ifdef LDAP_SLAPI
940         op.o_pb = opx->o_pb;
941 #endif
942         op.oq_search.rs_deref = LDAP_DEREF_NEVER;
943         op.oq_search.rs_slimit = 1;
944         op.oq_search.rs_attrsonly = 1;
945         op.o_req_dn = op.o_req_ndn;
946
947         op.o_bd->be_search( &op, &rs );
948         
949 FINISHED:
950         if( sasldn->bv_len ) {
951                 opx->o_conn->c_authz_backend = op.o_bd;
952         }
953         if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
954         if( op.o_req_ndn.bv_len ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
955         if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
956         if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
957
958 #ifdef NEW_LOGGING
959         LDAP_LOG( TRANSPORT, ENTRY, 
960                 "slap_sasl2dn: Converted SASL name to %s\n",
961                 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
962 #else
963         Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
964                 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
965 #endif
966
967         return;
968 }
969
970
971 /* Check if a bind can SASL authorize to another identity.
972  * The DNs should not have the dn: prefix
973  */
974
975 int slap_sasl_authorized( Operation *op,
976         struct berval *authcDN, struct berval *authzDN )
977 {
978         int rc = LDAP_INAPPROPRIATE_AUTH;
979
980         /* User binding as anonymous */
981         if ( authzDN == NULL ) {
982                 rc = LDAP_SUCCESS;
983                 goto DONE;
984         }
985
986 #ifdef NEW_LOGGING
987         LDAP_LOG( TRANSPORT, ENTRY, 
988                 "slap_sasl_authorized: can %s become %s?\n", 
989                 authcDN->bv_val, authzDN->bv_val, 0 );
990 #else
991         Debug( LDAP_DEBUG_TRACE,
992            "==>slap_sasl_authorized: can %s become %s?\n",
993                 authcDN->bv_val, authzDN->bv_val, 0 );
994 #endif
995
996         /* If person is authorizing to self, succeed */
997         if ( dn_match( authcDN, authzDN ) ) {
998                 rc = LDAP_SUCCESS;
999                 goto DONE;
1000         }
1001
1002         /* Allow the manager to authorize as any DN. */
1003         if( op->o_conn->c_authz_backend &&
1004                 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
1005         {
1006                 rc = LDAP_SUCCESS;
1007                 goto DONE;
1008         }
1009
1010         /* Check source rules */
1011         if( authz_policy & SASL_AUTHZ_TO ) {
1012                 rc = slap_sasl_check_authz( op, authcDN, authzDN,
1013                         slap_schema.si_ad_saslAuthzTo, authcDN );
1014                 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
1015                         goto DONE;
1016                 }
1017         }
1018
1019         /* Check destination rules */
1020         if( authz_policy & SASL_AUTHZ_FROM ) {
1021                 rc = slap_sasl_check_authz( op, authzDN, authcDN,
1022                         slap_schema.si_ad_saslAuthzFrom, authcDN );
1023                 if( rc == LDAP_SUCCESS ) {
1024                         goto DONE;
1025                 }
1026         }
1027
1028         rc = LDAP_INAPPROPRIATE_AUTH;
1029
1030 DONE:
1031
1032 #ifdef NEW_LOGGING
1033         LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );
1034 #else
1035         Debug( LDAP_DEBUG_TRACE,
1036                 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );
1037 #endif
1038
1039         return( rc );
1040 }