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