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