]> git.sur5r.net Git - openldap/blob - servers/slapd/saslauthz.c
725f4de0749c2f0a3c4f3391f67357993bb5cee7
[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
565 #ifdef NEW_LOGGING
566                 LDAP_LOG( TRANSPORT, DETAIL1,
567                         "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
568 #else
569                 Debug( LDAP_DEBUG_TRACE,
570                         "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
571 #endif
572                 return -1;
573         }
574
575         ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
576         return 0;
577 }
578
579
580 typedef struct smatch_info {
581         struct berval *dn;
582         int match;
583 } smatch_info;
584
585 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
586 {
587         smatch_info *sm = o->o_callback->sc_private;
588
589         if (rs->sr_type != REP_SEARCH) return 0;
590
591         if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
592                 sm->match = 1;
593                 return -1;      /* short-circuit the search */
594         }
595
596         return 1;
597 }
598
599 /*
600  * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
601  * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
602  * the rule must be used as an internal search for entries. If that search
603  * returns the *assertDN entry, the match is successful.
604  *
605  * The assertDN should not have the dn: prefix
606  */
607
608 static
609 int slap_sasl_match( Operation *opx, struct berval *rule,
610         struct berval *assertDN, struct berval *authc )
611 {
612         int rc; 
613         regex_t reg;
614         smatch_info sm;
615         slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
616         Operation op = {0};
617         SlapReply rs = {REP_RESULT};
618
619 #ifdef NEW_LOGGING
620         LDAP_LOG( TRANSPORT, ENTRY, 
621                 "slap_sasl_match: comparing DN %s to rule %s\n", 
622                 assertDN->bv_val, rule->bv_val,0 );
623 #else
624         Debug( LDAP_DEBUG_TRACE,
625            "===>slap_sasl_match: comparing DN %s to rule %s\n",
626                 assertDN->bv_val, rule->bv_val, 0 );
627 #endif
628
629         rc = slap_parseURI( opx, rule, &op.o_req_dn,
630                 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
631                 &op.ors_filterstr );
632         if( rc != LDAP_SUCCESS ) goto CONCLUDED;
633
634         /* Massive shortcut: search scope == base */
635         switch ( op.oq_search.rs_scope ) {
636         case LDAP_SCOPE_BASE:
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_CHILDREN:
666                                         rc = LDAP_SUCCESS;
667                                         break;
668
669                                 case LDAP_X_SCOPE_ONELEVEL:
670                                 {
671                                         struct berval   pdn;
672
673                                         dnParent( assertDN, &pdn );
674                                         /* the common portion of the DN
675                                          * already matches, so only check
676                                          * if parent DN of assertedDN 
677                                          * is all the pattern */
678                                         if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
679                                                 rc = LDAP_SUCCESS;
680                                         }
681                                         break;
682                                 }
683                                 default:
684                                         /* at present, impossible */
685                                         assert( 0 );
686                                 }
687                         }
688                 }
689                 goto CONCLUDED;
690         }
691
692         case LDAP_X_SCOPE_REGEX:
693                 rc = regcomp(&reg, op.o_req_ndn.bv_val,
694                         REG_EXTENDED|REG_ICASE|REG_NOSUB);
695                 if ( rc == 0 ) {
696                         rc = regexec(&reg, assertDN->bv_val, 0, NULL, 0);
697                         regfree( &reg );
698                 }
699                 if ( rc == 0 ) {
700                         rc = LDAP_SUCCESS;
701                 } else {
702                         rc = LDAP_INAPPROPRIATE_AUTH;
703                 }
704                 goto CONCLUDED;
705
706         default:
707                 break;
708         }
709
710         /* Must run an internal search. */
711         if ( op.oq_search.rs_filter == NULL ) {
712                 rc = LDAP_FILTER_ERROR;
713                 goto CONCLUDED;
714         }
715
716 #ifdef NEW_LOGGING
717         LDAP_LOG( TRANSPORT, DETAIL1, 
718                 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
719                 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
720 #else
721         Debug( LDAP_DEBUG_TRACE,
722            "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
723            op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
724 #endif
725
726         op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
727         if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
728                 rc = LDAP_INAPPROPRIATE_AUTH;
729                 goto CONCLUDED;
730         }
731
732         sm.dn = assertDN;
733         sm.match = 0;
734         cb.sc_private = &sm;
735
736         op.o_tag = LDAP_REQ_SEARCH;
737         op.o_protocol = LDAP_VERSION3;
738         op.o_ndn = *authc;
739         op.o_callback = &cb;
740         op.o_time = slap_get_time();
741         op.o_do_not_cache = 1;
742         op.o_is_auth_check = 1;
743         op.o_threadctx = opx->o_threadctx;
744         op.o_tmpmemctx = opx->o_tmpmemctx;
745         op.o_tmpmfuncs = opx->o_tmpmfuncs;
746 #ifdef LDAP_SLAPI
747         op.o_pb = opx->o_pb;
748 #endif
749         op.o_conn = opx->o_conn;
750         op.o_connid = opx->o_connid;
751         op.o_req_dn = op.o_req_ndn;
752
753         op.o_bd->be_search( &op, &rs );
754
755         if (sm.match) {
756                 rc = LDAP_SUCCESS;
757         } else {
758                 rc = LDAP_INAPPROPRIATE_AUTH;
759         }
760
761 CONCLUDED:
762         if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
763         if( op.o_req_ndn.bv_len ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
764         if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
765         if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
766
767 #ifdef NEW_LOGGING
768         LDAP_LOG( TRANSPORT, ENTRY, 
769                 "slap_sasl_match: comparison returned %d\n", rc, 0, 0 );
770 #else
771         Debug( LDAP_DEBUG_TRACE,
772            "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
773 #endif
774
775         return( rc );
776 }
777
778
779 /*
780  * This function answers the question, "Can this ID authorize to that ID?",
781  * based on authorization rules. The rules are stored in the *searchDN, in the
782  * attribute named by *attr. If any of those rules map to the *assertDN, the
783  * authorization is approved.
784  *
785  * The DNs should not have the dn: prefix
786  */
787 static int
788 slap_sasl_check_authz( Operation *op,
789         struct berval *searchDN,
790         struct berval *assertDN,
791         AttributeDescription *ad,
792         struct berval *authc )
793 {
794         int i, rc;
795         BerVarray vals=NULL;
796
797 #ifdef NEW_LOGGING
798         LDAP_LOG( TRANSPORT, ENTRY, 
799                 "slap_sasl_check_authz: does %s match %s rule in %s?\n",
800             assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
801 #else
802         Debug( LDAP_DEBUG_TRACE,
803            "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
804            assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
805 #endif
806
807         rc = backend_attribute( op, NULL,
808                 searchDN, ad, &vals );
809         if( rc != LDAP_SUCCESS ) goto COMPLETE;
810
811         /* Check if the *assertDN matches any **vals */
812         if( vals != NULL ) {
813                 for( i=0; vals[i].bv_val != NULL; i++ ) {
814                         rc = slap_sasl_match( op, &vals[i], assertDN, authc );
815                         if ( rc == LDAP_SUCCESS ) goto COMPLETE;
816                 }
817         }
818         rc = LDAP_INAPPROPRIATE_AUTH;
819
820 COMPLETE:
821         if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
822
823 #ifdef NEW_LOGGING
824         LDAP_LOG( TRANSPORT, RESULTS, 
825                 "slap_sasl_check_authz: %s check returning %s\n", 
826                 ad->ad_cname.bv_val, rc, 0 );
827 #else
828         Debug( LDAP_DEBUG_TRACE,
829            "<==slap_sasl_check_authz: %s check returning %d\n",
830                 ad->ad_cname.bv_val, rc, 0);
831 #endif
832
833         return( rc );
834 }
835
836 /*
837  * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
838  * return the LDAP DN to which it matches. The SASL regexp rules in the config
839  * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
840  * search with scope=base), just return the URI (or its searchbase). Otherwise
841  * an internal search must be done, and if that search returns exactly one
842  * entry, return the DN of that one entry.
843  */
844 void slap_sasl2dn( Operation *opx,
845         struct berval *saslname, struct berval *sasldn, int flags )
846 {
847         int rc;
848         slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
849         Operation op = {0};
850         SlapReply rs = {REP_RESULT};
851         struct berval regout = { 0, NULL };
852
853 #ifdef NEW_LOGGING
854         LDAP_LOG( TRANSPORT, ENTRY, 
855                 "slap_sasl2dn: converting SASL name %s to DN.\n",
856                 saslname->bv_val, 0, 0 );
857 #else
858         Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
859                 "converting SASL name %s to a DN\n",
860                 saslname->bv_val, 0,0 );
861 #endif
862
863         sasldn->bv_val = NULL;
864         sasldn->bv_len = 0;
865         cb.sc_private = sasldn;
866
867         /* Convert the SASL name into a minimal URI */
868         if( !slap_sasl_regexp( saslname, &regout, flags, opx->o_tmpmemctx ) ) {
869                 goto FINISHED;
870         }
871
872         rc = slap_parseURI( opx, &regout, &op.o_req_dn,
873                 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
874                 &op.ors_filterstr );
875         if( regout.bv_val ) sl_free( regout.bv_val, opx->o_tmpmemctx );
876         if( rc != LDAP_SUCCESS ) {
877                 goto FINISHED;
878         }
879
880         /* Must do an internal search */
881         op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
882
883         /* Massive shortcut: search scope == base */
884         switch ( op.oq_search.rs_scope ) {
885         case LDAP_SCOPE_BASE:
886         case LDAP_X_SCOPE_EXACT:
887                 *sasldn = op.o_req_ndn;
888                 op.o_req_ndn.bv_len = 0;
889                 op.o_req_ndn.bv_val = NULL;
890                 /* intentionally continue to next case */
891
892         case LDAP_X_SCOPE_REGEX:
893         case LDAP_X_SCOPE_SUBTREE:
894         case LDAP_X_SCOPE_CHILDREN:
895         case LDAP_X_SCOPE_ONELEVEL:
896                 /* correctly parsed, but illegal */
897                 goto FINISHED;
898
899         case LDAP_SCOPE_ONELEVEL:
900         case LDAP_SCOPE_SUBTREE:
901         case LDAP_SCOPE_SUBORDINATE:
902                 /* do a search */
903                 break;
904
905         default:
906                 /* catch unhandled cases (there shouldn't be) */
907                 assert( 0 );
908         }
909
910 #ifdef NEW_LOGGING
911         LDAP_LOG( TRANSPORT, DETAIL1, 
912                 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
913                 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
914 #else
915         Debug( LDAP_DEBUG_TRACE,
916                 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
917                 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
918 #endif
919
920         if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
921                 goto FINISHED;
922         }
923
924         op.o_conn = opx->o_conn;
925         op.o_connid = opx->o_connid;
926         op.o_tag = LDAP_REQ_SEARCH;
927         op.o_protocol = LDAP_VERSION3;
928         op.o_ndn = opx->o_conn->c_ndn;
929         op.o_callback = &cb;
930         op.o_time = slap_get_time();
931         op.o_do_not_cache = 1;
932         op.o_is_auth_check = 1;
933         op.o_threadctx = opx->o_threadctx;
934         op.o_tmpmemctx = opx->o_tmpmemctx;
935         op.o_tmpmfuncs = opx->o_tmpmfuncs;
936 #ifdef LDAP_SLAPI
937         op.o_pb = opx->o_pb;
938 #endif
939         op.oq_search.rs_deref = LDAP_DEREF_NEVER;
940         op.oq_search.rs_slimit = 1;
941         op.oq_search.rs_attrsonly = 1;
942         op.o_req_dn = op.o_req_ndn;
943
944         op.o_bd->be_search( &op, &rs );
945         
946 FINISHED:
947         if( sasldn->bv_len ) {
948                 opx->o_conn->c_authz_backend = op.o_bd;
949         }
950         if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
951         if( op.o_req_ndn.bv_len ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
952         if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
953         if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
954
955 #ifdef NEW_LOGGING
956         LDAP_LOG( TRANSPORT, ENTRY, 
957                 "slap_sasl2dn: Converted SASL name to %s\n",
958                 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
959 #else
960         Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
961                 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
962 #endif
963
964         return;
965 }
966
967
968 /* Check if a bind can SASL authorize to another identity.
969  * The DNs should not have the dn: prefix
970  */
971
972 int slap_sasl_authorized( Operation *op,
973         struct berval *authcDN, struct berval *authzDN )
974 {
975         int rc = LDAP_INAPPROPRIATE_AUTH;
976
977         /* User binding as anonymous */
978         if ( authzDN == NULL ) {
979                 rc = LDAP_SUCCESS;
980                 goto DONE;
981         }
982
983 #ifdef NEW_LOGGING
984         LDAP_LOG( TRANSPORT, ENTRY, 
985                 "slap_sasl_authorized: can %s become %s?\n", 
986                 authcDN->bv_val, authzDN->bv_val, 0 );
987 #else
988         Debug( LDAP_DEBUG_TRACE,
989            "==>slap_sasl_authorized: can %s become %s?\n",
990                 authcDN->bv_val, authzDN->bv_val, 0 );
991 #endif
992
993         /* If person is authorizing to self, succeed */
994         if ( dn_match( authcDN, authzDN ) ) {
995                 rc = LDAP_SUCCESS;
996                 goto DONE;
997         }
998
999         /* Allow the manager to authorize as any DN. */
1000         if( op->o_conn->c_authz_backend && be_isroot( op->o_conn->c_authz_backend, authcDN )) {
1001                 rc = LDAP_SUCCESS;
1002                 goto DONE;
1003         }
1004
1005         /* Check source rules */
1006         if( authz_policy & SASL_AUTHZ_TO ) {
1007                 rc = slap_sasl_check_authz( op, authcDN, authzDN,
1008                         slap_schema.si_ad_saslAuthzTo, authcDN );
1009                 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
1010                         goto DONE;
1011                 }
1012         }
1013
1014         /* Check destination rules */
1015         if( authz_policy & SASL_AUTHZ_FROM ) {
1016                 rc = slap_sasl_check_authz( op, authzDN, authcDN,
1017                         slap_schema.si_ad_saslAuthzFrom, authcDN );
1018                 if( rc == LDAP_SUCCESS ) {
1019                         goto DONE;
1020                 }
1021         }
1022
1023         rc = LDAP_INAPPROPRIATE_AUTH;
1024
1025 DONE:
1026
1027 #ifdef NEW_LOGGING
1028         LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );
1029 #else
1030         Debug( LDAP_DEBUG_TRACE,
1031                 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );
1032 #endif
1033
1034         return( rc );
1035 }