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