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