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