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