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