]> git.sur5r.net Git - openldap/blob - servers/slapd/saslauthz.c
ef0b67b899ca324da735a0a7d3651204a92d1421
[openldap] / servers / slapd / saslauthz.c
1 /*
2  * Copyright (c) 2000, Mark Adamson, Carnegie Mellon.  All rights reserved.
3  * This software is not subject to any license of Carnegie Mellon University.
4  *
5  * Redistribution and use in source and binary forms are permitted without 
6  * restriction or fee of any kind as long as this notice is preserved.
7  *
8  * The name "Carnegie Mellon" must not be used to endorse or promote
9  * products derived from this software without prior written permission.
10  *
11  */
12
13 #include "portable.h"
14
15 #include <stdio.h>
16
17 #include <ac/stdlib.h>
18 #include <ac/string.h>
19
20 #include "slap.h"
21
22 #ifdef HAVE_CYRUS_SASL
23 #include <limits.h>
24
25 #ifdef HAVE_SASL_SASL_H
26 #include <sasl/sasl.h>
27 #else
28 #include <sasl.h>
29 #endif
30
31 #include <ldap_pvt.h>
32
33 /* URI format: ldap://<host>/<base>[?[<attrs>][?[<scope>][?[<filter>]]]] */
34
35 static int slap_parseURI( struct berval *uri,
36         struct berval *searchbase, int *scope, Filter **filter,
37         struct berval *fstr )
38 {
39         struct berval bv;
40         int rc;
41         LDAPURLDesc *ludp;
42
43         assert( uri != NULL && uri->bv_val != NULL );
44         searchbase->bv_val = NULL;
45         searchbase->bv_len = 0;
46         *scope = -1;
47         *filter = NULL;
48
49         if ( fstr ) {
50                 fstr->bv_val = NULL;
51                 fstr->bv_len = 0;
52         }
53
54 #ifdef NEW_LOGGING
55         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
56                 "slap_parseURI: parsing %s\n", uri->bv_val ));
57 #else
58         Debug( LDAP_DEBUG_TRACE, "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
59 #endif
60
61         /* If it does not look like a URI, assume it is a DN */
62         if( !strncasecmp( uri->bv_val, "dn:", sizeof("dn:")-1 ) ) {
63                 bv.bv_val = uri->bv_val + sizeof("dn:")-1;
64                 bv.bv_val += strspn( bv.bv_val, " " );
65
66 is_dn:          bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
67                 rc = dnNormalize2( NULL, &bv, searchbase );
68                 if (rc == LDAP_SUCCESS) {
69                         *scope = LDAP_SCOPE_BASE;
70                 }
71                 return( rc );
72         }
73
74         rc = ldap_url_parse( uri->bv_val, &ludp );
75         if ( rc == LDAP_URL_ERR_BADSCHEME ) {
76                 bv.bv_val = uri->bv_val;
77                 goto is_dn;
78         }
79
80         if ( rc != LDAP_URL_SUCCESS ) {
81                 return( LDAP_PROTOCOL_ERROR );
82         }
83
84         /* could check the hostname here */
85
86         /* Grab the scope */
87         *scope = ludp->lud_scope;
88
89         /* Grab the filter */
90         if ( ludp->lud_filter ) {
91                 *filter = str2filter( ludp->lud_filter );
92                 if ( *filter == NULL )
93                         rc = LDAP_PROTOCOL_ERROR;
94                 else if ( fstr )
95                         ber_str2bv( ludp->lud_filter, 0, 1, fstr );
96         }
97
98         /* Grab the searchbase */
99         if ( rc == LDAP_URL_SUCCESS ) {
100                 bv.bv_val = ludp->lud_dn;
101                 bv.bv_len = strlen( bv.bv_val );
102                 rc = dnNormalize2( NULL, &bv, searchbase );
103         }
104
105         ldap_free_urldesc( ludp );
106
107         return( rc );
108 }
109
110 static int slap_sasl_rx_off(char *rep, int *off)
111 {
112         const char *c;
113         int n;
114
115         /* Precompile replace pattern. Find the $<n> placeholders */
116         off[0] = -2;
117         n = 1;
118         for ( c = rep;   *c;  c++ ) {
119                 if ( *c == '\\' && c[1] ) {
120                         c++;
121                         continue;
122                 }
123                 if ( *c == '$' ) {
124                         if ( n == SASLREGEX_REPLACE ) {
125 #ifdef NEW_LOGGING
126                                 LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
127                                         "slap_sasl_regexp_config: \"%s\" has too many $n "
128                                                 "placeholders (max %d)\n",
129                                         rep, SASLREGEX_REPLACE ));
130 #else
131                                 Debug( LDAP_DEBUG_ANY,
132                                         "SASL replace pattern %s has too many $n "
133                                                 "placeholders (max %d)\n",
134                                         rep, SASLREGEX_REPLACE, 0 );
135 #endif
136
137                                 return( LDAP_OPERATIONS_ERROR );
138                         }
139                         off[n] = c - rep;
140                         n++;
141                 }
142         }
143
144         /* Final placeholder, after the last $n */
145         off[n] = c - rep;
146         n++;
147         off[n] = -1;
148         return( LDAP_SUCCESS );
149 }
150 #endif /* HAVE_CYRUS_SASL */
151
152 int slap_sasl_regexp_config( const char *match, const char *replace )
153 {
154 #ifdef HAVE_CYRUS_SASL
155         const char *c;
156         int rc, n;
157         SaslRegexp_t *reg;
158         struct berval bv, nbv;
159         Filter *filter;
160
161         SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
162           (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
163         reg = &( SaslRegexp[nSaslRegexp] );
164         ber_str2bv( match, 0, 0, &bv );
165         rc = dnNormalize2( NULL, &bv, &nbv );
166         if ( rc ) {
167 #ifdef NEW_LOGGING
168                 LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
169                            "slap_sasl_regexp_config: \"%s\" could not be normalized.\n",
170                            match ));
171 #else
172                 Debug( LDAP_DEBUG_ANY,
173                 "SASL match pattern %s could not be normalized.\n",
174                 match, 0, 0 );
175 #endif
176                 return( rc );
177         }
178         reg->sr_match = nbv.bv_val;
179
180         ber_str2bv( replace, 0, 0, &bv );
181         rc = slap_parseURI( &bv, &reg->sr_replace.dn, &reg->sr_replace.scope,
182                 &filter, &reg->sr_replace.filter );
183         if ( filter ) filter_free( filter );
184         if ( rc ) {
185 #ifdef NEW_LOGGING
186                 LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
187                            "slap_sasl_regexp_config: \"%s\" could not be parsed.\n",
188                            replace ));
189 #else
190                 Debug( LDAP_DEBUG_ANY,
191                 "SASL replace pattern %s could not be parsed.\n",
192                 replace, 0, 0 );
193 #endif
194                 return( rc );
195         }
196
197         /* Precompile matching pattern */
198         rc = regcomp( &reg->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
199         if ( rc ) {
200 #ifdef NEW_LOGGING
201                 LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
202                            "slap_sasl_regexp_config: \"%s\" could not be compiled.\n",
203                            reg->sr_match ));
204 #else
205                 Debug( LDAP_DEBUG_ANY,
206                 "SASL match pattern %s could not be compiled by regexp engine\n",
207                 reg->sr_match, 0, 0 );
208 #endif
209
210                 return( LDAP_OPERATIONS_ERROR );
211         }
212
213         rc = slap_sasl_rx_off( reg->sr_replace.dn.bv_val, reg->sr_dn_offset );
214         if ( rc != LDAP_SUCCESS ) return rc;
215
216         if (reg->sr_replace.filter.bv_val ) {
217                 rc = slap_sasl_rx_off( reg->sr_replace.filter.bv_val, reg->sr_fi_offset );
218                 if ( rc != LDAP_SUCCESS ) return rc;
219         }
220
221         nSaslRegexp++;
222 #endif
223         return( LDAP_SUCCESS );
224 }
225
226
227 #ifdef HAVE_CYRUS_SASL
228
229 /* Perform replacement on regexp matches */
230 static void slap_sasl_rx_exp( char *rep, int *off, regmatch_t *str,
231         char *saslname, struct berval *out )
232 {
233         int i, n, len, insert;
234
235         /* Get the total length of the final URI */
236
237         n=1;
238         len = 0;
239         while( off[n] >= 0 ) {
240                 /* Len of next section from replacement string (x,y,z above) */
241                 len += off[n] - off[n-1] - 2;
242                 if( off[n+1] < 0)
243                         break;
244
245                 /* Len of string from saslname that matched next $i  (b,d above) */
246                 i = rep[ off[n] + 1 ]   - '0';
247                 len += str[i].rm_eo - str[i].rm_so;
248                 n++;
249         }
250         out->bv_val = ch_malloc( len + 1 );
251         out->bv_len = len;
252
253         /* Fill in URI with replace string, replacing $i as we go */
254         n=1;
255         insert = 0;
256         while( off[n] >= 0) {
257                 /* Paste in next section from replacement string (x,y,z above) */
258                 len = off[n] - off[n-1] - 2;
259                 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
260                 insert += len;
261                 if( off[n+1] < 0)
262                         break;
263
264                 /* Paste in string from saslname that matched next $i  (b,d above) */
265                 i = rep[ off[n] + 1 ]   - '0';
266                 len = str[i].rm_eo - str[i].rm_so;
267                 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
268                 insert += len;
269
270                 n++;
271         }
272
273         out->bv_val[insert] = '\0';
274 }
275
276 /* Take the passed in SASL name and attempt to convert it into an
277    LDAP URI to find the matching LDAP entry, using the pattern matching
278    strings given in the saslregexp config file directive(s) */
279
280 static int slap_sasl_regexp( struct berval *in, SaslUri_t *out )
281 {
282         char *saslname = in->bv_val;
283         char *scope[] = { "base", "one", "sub" };
284         SaslRegexp_t *reg;
285         int i;
286
287         memset( out, 0, sizeof( *out ) );
288
289 #ifdef NEW_LOGGING
290         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
291                 "slap_sasl_regexp: converting SASL name %s\n", saslname ));
292 #else
293         Debug( LDAP_DEBUG_TRACE, "slap_sasl_regexp: converting SASL name %s\n",
294            saslname, 0, 0 );
295 #endif
296
297         if (( saslname == NULL ) || ( nSaslRegexp == 0 ))
298                 return( 0 );
299
300         /* Match the normalized SASL name to the saslregexp patterns */
301         for( reg = SaslRegexp,i=0;  i<nSaslRegexp;  i++,reg++ ) {
302                 if ( regexec( &reg->sr_workspace, saslname, SASLREGEX_REPLACE,
303                   reg->sr_strings, 0)  == 0 )
304                         break;
305         }
306
307         if( i >= nSaslRegexp )
308                 return( 0 );
309
310         /*
311          * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
312          * replace pattern of the form "x$1y$2z". The returned string needs
313          * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
314          */
315         slap_sasl_rx_exp( reg->sr_replace.dn.bv_val, reg->sr_dn_offset,
316                 reg->sr_strings, saslname, &out->dn );
317
318         if ( reg->sr_replace.filter.bv_val )
319                 slap_sasl_rx_exp( reg->sr_replace.filter.bv_val,
320                         reg->sr_fi_offset, reg->sr_strings, saslname, &out->filter );
321         
322         out->scope = reg->sr_replace.scope;
323
324 #ifdef NEW_LOGGING
325         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
326                 "slap_sasl_regexp: converted SASL name to ldap:///%s??%s?%s\n",
327                 out->dn.bv_val, scope[out->scope], out->filter.bv_val ?
328                 out->filter.bv_val : "" ));
329 #else
330         Debug( LDAP_DEBUG_TRACE,
331            "slap_sasl_regexp: converted SASL name to ldap:///%s??%s?%s\n",
332                 out->dn.bv_val, scope[out->scope], out->filter.bv_val ?
333                 out->filter.bv_val : "" );
334 #endif
335
336         return( 1 );
337 }
338
339 /* Two empty callback functions to avoid sending results */
340 static void sasl_sc_r( Connection *conn, Operation *o, ber_tag_t tag,
341         ber_int_t msgid, ber_int_t err, const char *matched,
342         const char *text, BerVarray ref, const char *resoid,
343         struct berval *resdata, struct berval *sasldata, LDAPControl **c)
344 {
345 }
346
347 static void sasl_sc_s( Connection *conn, Operation *o, ber_int_t err,
348         const char *matched, const char *text, BerVarray refs, LDAPControl **c,
349         int nentries)
350 {
351 }
352
353 /* This callback actually does some work...*/
354 static int sasl_sc_sasl2dn( BackendDB *be, Connection *conn, Operation *o,
355         Entry *e, AttributeName *an, int ao, LDAPControl **c)
356 {
357         struct berval *ndn = o->o_callback->sc_private;
358
359         /* We only want to be called once */
360         if (ndn->bv_val) {
361                 free(ndn->bv_val);
362                 ndn->bv_val = NULL;
363 #ifdef NEW_LOGGING
364         LDAP_LOG(( "sasl", LDAP_LEVEL_DETAIL1,
365                    "slap_sasl2dn: search DN returned more than 1 entry\n" ));
366 #else
367         Debug( LDAP_DEBUG_TRACE,
368            "slap_sasl2dn: search DN returned more than 1 entry\n", 0,0,0 );
369 #endif
370                 return -1;
371         } else {
372                 ber_dupbv(ndn, &e->e_nname);
373                 return 0;
374         }
375 }
376
377 /*
378  * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
379  * return the LDAP DN to which it matches. The SASL regexp rules in the config
380  * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
381  * search with scope=base), just return the URI (or its searchbase). Otherwise
382  * an internal search must be done, and if that search returns exactly one
383  * entry, return the DN of that one entry.
384  */
385
386 void slap_sasl2dn( struct berval *saslname, struct berval *dn )
387 {
388         int rc;
389         Backend *be;
390         Filter *filter=NULL;
391         slap_callback cb = {sasl_sc_r, sasl_sc_s, sasl_sc_sasl2dn, NULL};
392         Operation op = {0};
393         SaslUri_t uri;
394
395 #ifdef NEW_LOGGING
396         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
397                 "slap_sasl2dn: converting SASL name %s to DN.\n", saslname->bv_val ));
398 #else
399         Debug( LDAP_DEBUG_TRACE,
400                 "==>slap_sasl2dn: Converting SASL name %s to a DN\n", saslname->bv_val, 0,0 );
401 #endif
402         dn->bv_val = NULL;
403         dn->bv_len = 0;
404         cb.sc_private = dn;
405
406         /* Convert the SASL name into a minimal URI */
407         if( !slap_sasl_regexp( saslname, &uri ) )
408                 goto FINISHED;
409
410         if ( uri.filter.bv_val )
411                 filter = str2filter( uri.filter.bv_val );
412
413         /* FIXME: move this check to after select_backend, and set
414          * the selected backend in conn->c_authz_backend, to allow
415          * passwd_extop to be used on in-directory SASL secrets.
416          *   ... when all of that gets implemented...
417          */
418         /* Massive shortcut: search scope == base */
419         if( uri.scope == LDAP_SCOPE_BASE ) {
420                 *dn = uri.dn;
421                 uri.dn.bv_len = 0;
422                 uri.dn.bv_val = NULL;
423                 goto FINISHED;
424         }
425
426         /* Must do an internal search */
427
428 #ifdef NEW_LOGGING
429         LDAP_LOG(( "sasl", LDAP_LEVEL_DETAIL1,
430                    "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
431                    uri.dn.bv_val, uri.scope ));
432 #else
433         Debug( LDAP_DEBUG_TRACE,
434            "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
435            uri.dn.bv_val, uri.scope, 0 );
436 #endif
437
438         be = select_backend( &uri.dn, 0, 1 );
439         if(( be == NULL ) || ( be->be_search == NULL))
440                 goto FINISHED;
441         suffix_alias( be, &uri.dn );
442
443         op.o_tag = LDAP_REQ_SEARCH;
444         op.o_protocol = LDAP_VERSION3;
445         op.o_ndn = *saslname;
446         op.o_callback = &cb;
447         op.o_time = slap_get_time();
448
449         (*be->be_search)( be, /*conn*/NULL, &op, /*base*/NULL, &uri.dn,
450            uri.scope, /*deref=*/1, /*sizelimit=*/1, /*time=*/0, filter, /*fstr=*/NULL,
451            /*attrs=*/NULL, /*attrsonly=*/0 );
452         
453 FINISHED:
454         if( uri.dn.bv_len ) ch_free( uri.dn.bv_val );
455         if( uri.filter.bv_len ) ch_free( uri.filter.bv_val );
456         if( filter ) filter_free( filter );
457
458 #ifdef NEW_LOGGING
459         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
460                 "slap_sasl2dn: Converted SASL name to %s\n",
461                 dn->bv_len ? dn->bv_val : "<nothing>" ));
462 #else
463         Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
464                 dn->bv_len ? dn->bv_val : "<nothing>", 0, 0 );
465 #endif
466
467         return;
468 }
469
470 typedef struct smatch_info {
471         struct berval *dn;
472         int match;
473 } smatch_info;
474
475 static int sasl_sc_smatch( BackendDB *be, Connection *conn, Operation *o,
476         Entry *e, AttributeName *an, int ao, LDAPControl **c)
477 {
478         smatch_info *sm = o->o_callback->sc_private;
479
480         if (dn_match(sm->dn, &e->e_nname)) {
481                 sm->match = 1;
482                 return -1;      /* short-circuit the search */
483         } else {
484                 return 1;
485         }
486 }
487
488 /*
489  * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
490  * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
491  * the rule must be used as an internal search for entries. If that search
492  * returns the *assertDN entry, the match is successful.
493  *
494  * The assertDN should not have the dn: prefix
495  */
496
497 static
498 int slap_sasl_match( struct berval *rule, struct berval *assertDN, struct berval *authc )
499 {
500         struct berval searchbase = {0, NULL};
501         int rc, scope;
502         Backend *be;
503         Filter *filter=NULL;
504         regex_t reg;
505         smatch_info sm;
506         slap_callback cb = { sasl_sc_r, sasl_sc_s, sasl_sc_smatch, NULL };
507         Operation op = {0};
508
509 #ifdef NEW_LOGGING
510         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
511                 "slap_sasl_match: comparing DN %s to rule %s\n", assertDN->bv_val, rule->bv_val ));
512 #else
513         Debug( LDAP_DEBUG_TRACE,
514            "===>slap_sasl_match: comparing DN %s to rule %s\n", assertDN->bv_val, rule->bv_val, 0 );
515 #endif
516
517         rc = slap_parseURI( rule, &searchbase, &scope, &filter, NULL );
518         if( rc != LDAP_SUCCESS )
519                 goto CONCLUDED;
520
521         /* Massive shortcut: search scope == base */
522         if( scope == LDAP_SCOPE_BASE ) {
523                 rc = regcomp(&reg, searchbase.bv_val,
524                         REG_EXTENDED|REG_ICASE|REG_NOSUB);
525                 if ( rc == 0 ) {
526                         rc = regexec(&reg, assertDN->bv_val, 0, NULL, 0);
527                         regfree( &reg );
528                 }
529                 if ( rc == 0 )
530                         rc = LDAP_SUCCESS;
531                 else
532                         rc = LDAP_INAPPROPRIATE_AUTH;
533                 goto CONCLUDED;
534         }
535
536         /* Must run an internal search. */
537
538 #ifdef NEW_LOGGING
539         LDAP_LOG(( "sasl", LDAP_LEVEL_DETAIL1,
540                 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
541                 searchbase.bv_val, scope ));
542 #else
543         Debug( LDAP_DEBUG_TRACE,
544            "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
545            searchbase.bv_val, scope, 0 );
546 #endif
547
548         be = select_backend( &searchbase, 0, 1 );
549         if(( be == NULL ) || ( be->be_search == NULL)) {
550                 rc = LDAP_INAPPROPRIATE_AUTH;
551                 goto CONCLUDED;
552         }
553         suffix_alias( be, &searchbase );
554
555         sm.dn = assertDN;
556         sm.match = 0;
557         cb.sc_private = &sm;
558
559         op.o_tag = LDAP_REQ_SEARCH;
560         op.o_protocol = LDAP_VERSION3;
561         op.o_ndn = *authc;
562         op.o_callback = &cb;
563         op.o_time = slap_get_time();
564
565         (*be->be_search)( be, /*conn=*/NULL, &op, /*base=*/NULL, &searchbase,
566            scope, /*deref=*/1, /*sizelimit=*/0, /*time=*/0, filter, /*fstr=*/NULL,
567            /*attrs=*/NULL, /*attrsonly=*/0 );
568
569         if (sm.match)
570                 rc = LDAP_SUCCESS;
571         else
572                 rc = LDAP_INAPPROPRIATE_AUTH;
573
574 CONCLUDED:
575         if( searchbase.bv_len ) ch_free( searchbase.bv_val );
576         if( filter ) filter_free( filter );
577 #ifdef NEW_LOGGING
578         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
579                    "slap_sasl_match: comparison returned %d\n", rc ));
580 #else
581         Debug( LDAP_DEBUG_TRACE,
582            "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
583 #endif
584
585         return( rc );
586 }
587
588
589 /*
590  * This function answers the question, "Can this ID authorize to that ID?",
591  * based on authorization rules. The rules are stored in the *searchDN, in the
592  * attribute named by *attr. If any of those rules map to the *assertDN, the
593  * authorization is approved.
594  *
595  * The DNs should not have the dn: prefix
596  */
597 static int
598 slap_sasl_check_authz(struct berval *searchDN, struct berval *assertDN, struct berval *attr, struct berval *authc)
599 {
600         const char *errmsg;
601         int i, rc;
602         BerVarray vals=NULL;
603         AttributeDescription *ad=NULL;
604
605 #ifdef NEW_LOGGING
606         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
607                    "slap_sasl_check_authz: does %s match %s rule in %s?\n",
608                    assertDN->bv_val, attr->bv_val, searchDN->bv_val ));
609 #else
610         Debug( LDAP_DEBUG_TRACE,
611            "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
612            assertDN->bv_val, attr->bv_val, searchDN->bv_val);
613 #endif
614
615         rc = slap_bv2ad( attr, &ad, &errmsg );
616         if( rc != LDAP_SUCCESS )
617                 goto COMPLETE;
618
619         rc = backend_attribute( NULL, NULL, NULL, NULL, searchDN, ad, &vals );
620         if( rc != LDAP_SUCCESS )
621                 goto COMPLETE;
622
623         /* Check if the *assertDN matches any **vals */
624         for( i=0; vals[i].bv_val != NULL; i++ ) {
625                 rc = slap_sasl_match( &vals[i], assertDN, authc );
626                 if ( rc == LDAP_SUCCESS )
627                         goto COMPLETE;
628         }
629         rc = LDAP_INAPPROPRIATE_AUTH;
630
631 COMPLETE:
632         if( vals ) ber_bvarray_free( vals );
633
634 #ifdef NEW_LOGGING
635         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
636                    "slap_sasl_check_authz: %s check returning %s\n", attr->bv_val, rc ));
637 #else
638         Debug( LDAP_DEBUG_TRACE,
639            "<==slap_sasl_check_authz: %s check returning %d\n", attr->bv_val, rc, 0);
640 #endif
641
642         return( rc );
643 }
644 #endif  /* HAVE_CYRUS_SASL */
645
646
647 /* Check if a bind can SASL authorize to another identity.
648  * The DNs should not have the dn: prefix
649  */
650
651 static struct berval sasl_authz_src = {
652         sizeof(SASL_AUTHZ_SOURCE_ATTR)-1, SASL_AUTHZ_SOURCE_ATTR };
653
654 static struct berval sasl_authz_dst = {
655         sizeof(SASL_AUTHZ_DEST_ATTR)-1, SASL_AUTHZ_DEST_ATTR };
656
657 int slap_sasl_authorized( struct berval *authcDN, struct berval *authzDN )
658 {
659         int rc = LDAP_INAPPROPRIATE_AUTH;
660
661 #ifdef HAVE_CYRUS_SASL
662         /* User binding as anonymous */
663         if ( authzDN == NULL ) {
664                 rc = LDAP_SUCCESS;
665                 goto DONE;
666         }
667
668 #ifdef NEW_LOGGING
669         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
670                 "slap_sasl_authorized: can %s become %s?\n", authcDN->bv_val, authzDN->bv_val ));
671 #else
672         Debug( LDAP_DEBUG_TRACE,
673            "==>slap_sasl_authorized: can %s become %s?\n", authcDN->bv_val, authzDN->bv_val, 0 );
674 #endif
675
676         /* If person is authorizing to self, succeed */
677         if ( dn_match( authcDN, authzDN ) ) {
678                 rc = LDAP_SUCCESS;
679                 goto DONE;
680         }
681
682         /* Check source rules */
683         rc = slap_sasl_check_authz( authcDN, authzDN, &sasl_authz_src,
684            authcDN );
685         if( rc == LDAP_SUCCESS ) {
686                 goto DONE;
687         }
688
689         /* Check destination rules */
690         rc = slap_sasl_check_authz( authzDN, authcDN, &sasl_authz_dst,
691            authcDN );
692         if( rc == LDAP_SUCCESS ) {
693                 goto DONE;
694         }
695
696         rc = LDAP_INAPPROPRIATE_AUTH;
697
698 DONE:
699 #endif
700
701 #ifdef NEW_LOGGING
702         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
703                 "slap_sasl_authorized: return %d\n", rc ));
704 #else
705         Debug( LDAP_DEBUG_TRACE,
706                 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );
707 #endif
708
709         return( rc );
710 }