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