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