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