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