]> git.sur5r.net Git - openldap/blob - servers/slapd/saslauthz.c
e08cdd9bd6ab1f7ef866d5c6fc54b31c983cff41
[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 = BER_BVNULL,
280                                 realm = BER_BVNULL,
281                                 mech = BER_BVNULL;
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_SUBTREE:
667                                 case LDAP_X_SCOPE_CHILDREN:
668                                         rc = LDAP_SUCCESS;
669                                         break;
670
671                                 case LDAP_X_SCOPE_ONELEVEL:
672                                 {
673                                         struct berval   pdn;
674
675                                         dnParent( assertDN, &pdn );
676                                         /* the common portion of the DN
677                                          * already matches, so only check
678                                          * if parent DN of assertedDN 
679                                          * is all the pattern */
680                                         if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
681                                                 rc = LDAP_SUCCESS;
682                                         }
683                                         break;
684                                 }
685                                 default:
686                                         /* at present, impossible */
687                                         assert( 0 );
688                                 }
689                         }
690                 }
691                 goto CONCLUDED;
692         }
693
694         case LDAP_X_SCOPE_REGEX:
695                 rc = regcomp(&reg, op.o_req_ndn.bv_val,
696                         REG_EXTENDED|REG_ICASE|REG_NOSUB);
697                 if ( rc == 0 ) {
698                         rc = regexec(&reg, assertDN->bv_val, 0, NULL, 0);
699                         regfree( &reg );
700                 }
701                 if ( rc == 0 ) {
702                         rc = LDAP_SUCCESS;
703                 } else {
704                         rc = LDAP_INAPPROPRIATE_AUTH;
705                 }
706                 goto CONCLUDED;
707
708         default:
709                 break;
710         }
711
712         /* Must run an internal search. */
713         if ( op.oq_search.rs_filter == NULL ) {
714                 rc = LDAP_FILTER_ERROR;
715                 goto CONCLUDED;
716         }
717
718 #ifdef NEW_LOGGING
719         LDAP_LOG( TRANSPORT, DETAIL1, 
720                 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
721                 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
722 #else
723         Debug( LDAP_DEBUG_TRACE,
724            "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
725            op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
726 #endif
727
728         op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
729         if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
730                 rc = LDAP_INAPPROPRIATE_AUTH;
731                 goto CONCLUDED;
732         }
733
734         sm.dn = assertDN;
735         sm.match = 0;
736         cb.sc_private = &sm;
737
738         op.o_tag = LDAP_REQ_SEARCH;
739         op.o_protocol = LDAP_VERSION3;
740         op.o_ndn = *authc;
741         op.o_callback = &cb;
742         op.o_time = slap_get_time();
743         op.o_do_not_cache = 1;
744         op.o_is_auth_check = 1;
745         op.o_threadctx = opx->o_threadctx;
746         op.o_tmpmemctx = opx->o_tmpmemctx;
747         op.o_tmpmfuncs = opx->o_tmpmfuncs;
748 #ifdef LDAP_SLAPI
749         op.o_pb = opx->o_pb;
750 #endif
751         op.o_conn = opx->o_conn;
752         op.o_connid = opx->o_connid;
753         op.o_req_dn = op.o_req_ndn;
754         op.oq_search.rs_slimit = 1;
755         op.oq_search.rs_tlimit = -1;
756         op.o_sync_slog_size = -1;
757
758         op.o_bd->be_search( &op, &rs );
759
760         if (sm.match) {
761                 rc = LDAP_SUCCESS;
762         } else {
763                 rc = LDAP_INAPPROPRIATE_AUTH;
764         }
765
766 CONCLUDED:
767         if( op.o_req_dn.bv_val && op.o_req_dn.bv_val != op.o_req_ndn.bv_val ) ch_free( op.o_req_dn.bv_val );
768         if( op.o_req_ndn.bv_val ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
769         if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
770         if( op.ors_filterstr.bv_val ) ch_free( op.ors_filterstr.bv_val );
771
772 #ifdef NEW_LOGGING
773         LDAP_LOG( TRANSPORT, ENTRY, 
774                 "slap_sasl_match: comparison returned %d\n", rc, 0, 0 );
775 #else
776         Debug( LDAP_DEBUG_TRACE,
777            "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
778 #endif
779
780         return( rc );
781 }
782
783
784 /*
785  * This function answers the question, "Can this ID authorize to that ID?",
786  * based on authorization rules. The rules are stored in the *searchDN, in the
787  * attribute named by *attr. If any of those rules map to the *assertDN, the
788  * authorization is approved.
789  *
790  * The DNs should not have the dn: prefix
791  */
792 static int
793 slap_sasl_check_authz( Operation *op,
794         struct berval *searchDN,
795         struct berval *assertDN,
796         AttributeDescription *ad,
797         struct berval *authc )
798 {
799         int i, rc;
800         BerVarray vals=NULL;
801
802 #ifdef NEW_LOGGING
803         LDAP_LOG( TRANSPORT, ENTRY, 
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 #else
807         Debug( LDAP_DEBUG_TRACE,
808            "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
809            assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
810 #endif
811
812         rc = backend_attribute( op, NULL,
813                 searchDN, ad, &vals );
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         /* Massive shortcut: search scope == base */
889         switch ( op.oq_search.rs_scope ) {
890         case LDAP_SCOPE_BASE:
891         case LDAP_X_SCOPE_EXACT:
892                 *sasldn = op.o_req_ndn;
893                 op.o_req_ndn.bv_len = 0;
894                 op.o_req_ndn.bv_val = NULL;
895                 /* intentionally continue to next case */
896
897         case LDAP_X_SCOPE_REGEX:
898         case LDAP_X_SCOPE_SUBTREE:
899         case LDAP_X_SCOPE_CHILDREN:
900         case LDAP_X_SCOPE_ONELEVEL:
901                 /* correctly parsed, but illegal */
902                 goto FINISHED;
903
904         case LDAP_SCOPE_ONELEVEL:
905         case LDAP_SCOPE_SUBTREE:
906 #ifdef LDAP_SCOPE_SUBORDINATE
907         case LDAP_SCOPE_SUBORDINATE:
908 #endif
909                 /* do a search */
910                 break;
911
912         default:
913                 /* catch unhandled cases (there shouldn't be) */
914                 assert( 0 );
915         }
916
917 #ifdef NEW_LOGGING
918         LDAP_LOG( TRANSPORT, DETAIL1, 
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 #else
922         Debug( LDAP_DEBUG_TRACE,
923                 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
924                 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
925 #endif
926
927         if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
928                 goto FINISHED;
929         }
930
931         op.o_conn = opx->o_conn;
932         op.o_connid = opx->o_connid;
933         op.o_tag = LDAP_REQ_SEARCH;
934         op.o_protocol = LDAP_VERSION3;
935         op.o_ndn = opx->o_conn->c_ndn;
936         op.o_callback = &cb;
937         op.o_time = slap_get_time();
938         op.o_do_not_cache = 1;
939         op.o_is_auth_check = 1;
940         op.o_threadctx = opx->o_threadctx;
941         op.o_tmpmemctx = opx->o_tmpmemctx;
942         op.o_tmpmfuncs = opx->o_tmpmfuncs;
943 #ifdef LDAP_SLAPI
944         op.o_pb = opx->o_pb;
945 #endif
946         op.oq_search.rs_deref = LDAP_DEREF_NEVER;
947         op.oq_search.rs_slimit = 1;
948         op.oq_search.rs_tlimit = -1;
949         op.oq_search.rs_attrsonly = 1;
950         op.o_req_dn = op.o_req_ndn;
951
952         op.o_bd->be_search( &op, &rs );
953         
954 FINISHED:
955         if( sasldn->bv_len ) {
956                 opx->o_conn->c_authz_backend = op.o_bd;
957         }
958         if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
959         if( op.o_req_ndn.bv_len ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
960         if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
961         if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
962
963 #ifdef NEW_LOGGING
964         LDAP_LOG( TRANSPORT, ENTRY, 
965                 "slap_sasl2dn: Converted SASL name to %s\n",
966                 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
967 #else
968         Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
969                 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
970 #endif
971
972         return;
973 }
974
975
976 /* Check if a bind can SASL authorize to another identity.
977  * The DNs should not have the dn: prefix
978  */
979
980 int slap_sasl_authorized( Operation *op,
981         struct berval *authcDN, struct berval *authzDN )
982 {
983         int rc = LDAP_INAPPROPRIATE_AUTH;
984
985         /* User binding as anonymous */
986         if ( authzDN == NULL ) {
987                 rc = LDAP_SUCCESS;
988                 goto DONE;
989         }
990
991 #ifdef NEW_LOGGING
992         LDAP_LOG( TRANSPORT, ENTRY, 
993                 "slap_sasl_authorized: can %s become %s?\n", 
994                 authcDN->bv_val, authzDN->bv_val, 0 );
995 #else
996         Debug( LDAP_DEBUG_TRACE,
997            "==>slap_sasl_authorized: can %s become %s?\n",
998                 authcDN->bv_val, authzDN->bv_val, 0 );
999 #endif
1000
1001         /* If person is authorizing to self, succeed */
1002         if ( dn_match( authcDN, authzDN ) ) {
1003                 rc = LDAP_SUCCESS;
1004                 goto DONE;
1005         }
1006
1007         /* Allow the manager to authorize as any DN. */
1008         if( op->o_conn->c_authz_backend &&
1009                 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
1010         {
1011                 rc = LDAP_SUCCESS;
1012                 goto DONE;
1013         }
1014
1015         /* Check source rules */
1016         if( authz_policy & SASL_AUTHZ_TO ) {
1017                 rc = slap_sasl_check_authz( op, authcDN, authzDN,
1018                         slap_schema.si_ad_saslAuthzTo, authcDN );
1019                 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
1020                         goto DONE;
1021                 }
1022         }
1023
1024         /* Check destination rules */
1025         if( authz_policy & SASL_AUTHZ_FROM ) {
1026                 rc = slap_sasl_check_authz( op, authzDN, authcDN,
1027                         slap_schema.si_ad_saslAuthzFrom, authcDN );
1028                 if( rc == LDAP_SUCCESS ) {
1029                         goto DONE;
1030                 }
1031         }
1032
1033         rc = LDAP_INAPPROPRIATE_AUTH;
1034
1035 DONE:
1036
1037 #ifdef NEW_LOGGING
1038         LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );
1039 #else
1040         Debug( LDAP_DEBUG_TRACE,
1041                 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );
1042 #endif
1043
1044         return( rc );
1045 }