2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1998-2003 The OpenLDAP Foundation.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted only as authorized by the OpenLDAP
11 * A copy of this license is available in the file LICENSE in the
12 * top-level directory of the distribution or, alternatively, at
13 * <http://www.OpenLDAP.org/license.html>.
15 /* Portions Copyright (c) 2000, Mark Adamson, Carnegie Mellon.
16 * All rights reserved.
17 * This software is not subject to any license of Carnegie Mellon University.
19 * Redistribution and use in source and binary forms are permitted without
20 * restriction or fee of any kind as long as this notice is preserved.
22 * The name "Carnegie Mellon" must not be used to endorse or promote
23 * products derived from this software without prior written permission.
30 #include <ac/stdlib.h>
31 #include <ac/string.h>
39 #define SASLREGEX_REPLACE 10
41 #define LDAP_X_SCOPE_EXACT ((ber_int_t) 0x0010)
42 #define LDAP_X_SCOPE_REGEX ((ber_int_t) 0x0020)
43 #define LDAP_X_SCOPE_EXACTREGEX (LDAP_X_SCOPE_EXACT|LDAP_X_SCOPE_REGEX)
46 * IDs in DN form can now have a type specifier, that influences
47 * how they are used in related operations.
51 * dn[.{exact|regex}]:<val>
53 * dn.exact: the value must pass normalization and is used
55 * dn.regex: the value is treated as a regular expression
56 * in matching DN values in saslAuthz{To|From}
58 * dn: for backwards compatibility reasons, the value
59 * is treated as a regular expression, and thus
60 * it is not normalized nor validated; it is used
61 * in exact or regex comparisons based on the
65 typedef struct sasl_regexp {
66 char *sr_match; /* regexp match pattern */
67 char *sr_replace; /* regexp replace pattern */
68 regex_t sr_workspace; /* workspace for regexp engine */
69 int sr_offset[SASLREGEX_REPLACE+2]; /* offsets of $1,$2... in *replace */
72 static int nSaslRegexp = 0;
73 static SaslRegexp_t *SaslRegexp = NULL;
75 /* What SASL proxy authorization policies are allowed? */
76 #define SASL_AUTHZ_NONE 0
77 #define SASL_AUTHZ_FROM 1
78 #define SASL_AUTHZ_TO 2
80 static int authz_policy = SASL_AUTHZ_NONE;
82 int slap_sasl_setpolicy( const char *arg )
84 int rc = LDAP_SUCCESS;
86 if ( strcasecmp( arg, "none" ) == 0 ) {
87 authz_policy = SASL_AUTHZ_NONE;
88 } else if ( strcasecmp( arg, "from" ) == 0 ) {
89 authz_policy = SASL_AUTHZ_FROM;
90 } else if ( strcasecmp( arg, "to" ) == 0 ) {
91 authz_policy = SASL_AUTHZ_TO;
92 } else if ( strcasecmp( arg, "both" ) == 0 ) {
93 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO;
100 static int slap_parseURI( Operation *op, struct berval *uri,
101 struct berval *base, struct berval *nbase,
102 int *scope, Filter **filter, struct berval *fstr )
108 assert( uri != NULL && uri->bv_val != NULL );
111 nbase->bv_val = NULL;
119 LDAP_LOG( TRANSPORT, ENTRY,
120 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
122 Debug( LDAP_DEBUG_TRACE,
123 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
126 /* If it does not look like a URI, assume it is a DN */
127 /* explicitly set to exact: will skip regcomp/regexec */
128 if( !strncasecmp( uri->bv_val, "dn.exact:", sizeof("dn.exact:")-1 ) ) {
129 bv.bv_val = uri->bv_val + sizeof("dn.exact:")-1;
130 bv.bv_val += strspn( bv.bv_val, " " );
132 is_dn_exact: bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
134 rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
135 if( rc == LDAP_SUCCESS ) {
136 *scope = LDAP_X_SCOPE_EXACT;
141 /* unqualified: to be liberal, it will be regcomp'd/regexec'd */
142 } else if( !strncasecmp( uri->bv_val, "dn:", sizeof("dn:")-1 ) ) {
143 bv.bv_val = uri->bv_val + sizeof("dn:")-1;
144 bv.bv_val += strspn( bv.bv_val, " " );
146 is_dn: bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
148 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
149 *scope = LDAP_X_SCOPE_EXACTREGEX;
152 /* explicitly set to regex: it will be regcomp'd/regexec'd */
153 } else if ( !strncasecmp( uri->bv_val, "dn.regex:", sizeof("dn.regex:")-1 ) ) {
154 bv.bv_val = uri->bv_val + sizeof("dn.regex:")-1;
155 bv.bv_val += strspn( bv.bv_val, " " );
157 is_dn_regex: bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
159 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
160 *scope = LDAP_X_SCOPE_REGEX;
164 rc = ldap_url_parse( uri->bv_val, &ludp );
165 if ( rc == LDAP_URL_ERR_BADSCHEME ) {
166 bv.bv_val = uri->bv_val;
170 if ( rc != LDAP_URL_SUCCESS ) {
171 return LDAP_PROTOCOL_ERROR;
174 if (( ludp->lud_host && *ludp->lud_host )
175 || ludp->lud_attrs || ludp->lud_exts )
177 /* host part must be empty */
178 /* attrs and extensions parts must be empty */
179 rc = LDAP_PROTOCOL_ERROR;
184 *scope = ludp->lud_scope;
186 /* Grab the filter */
187 if ( ludp->lud_filter ) {
188 *filter = str2filter_x( op, ludp->lud_filter );
189 if ( *filter == NULL ) {
190 rc = LDAP_PROTOCOL_ERROR;
193 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
196 /* Grab the searchbase */
197 ber_str2bv( ludp->lud_dn, 0, 0, base );
198 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
201 if( rc != LDAP_SUCCESS ) {
202 if( *filter ) filter_free_x( op, *filter );
208 /* Don't free these, return them to caller */
209 ludp->lud_filter = NULL;
213 ldap_free_urldesc( ludp );
217 static int slap_sasl_rx_off(char *rep, int *off)
222 /* Precompile replace pattern. Find the $<n> placeholders */
225 for ( c = rep; *c; c++ ) {
226 if ( *c == '\\' && c[1] ) {
231 if ( n == SASLREGEX_REPLACE ) {
233 LDAP_LOG( TRANSPORT, ERR,
234 "slap_sasl_rx_off: \"%s\" has too many $n "
235 "placeholders (max %d)\n", rep, SASLREGEX_REPLACE, 0 );
237 Debug( LDAP_DEBUG_ANY,
238 "SASL replace pattern %s has too many $n "
239 "placeholders (max %d)\n",
240 rep, SASLREGEX_REPLACE, 0 );
243 return( LDAP_OTHER );
250 /* Final placeholder, after the last $n */
254 return( LDAP_SUCCESS );
257 int slap_sasl_regexp_config( const char *match, const char *replace )
262 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
263 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
265 reg = &SaslRegexp[nSaslRegexp];
267 reg->sr_match = ch_strdup( match );
268 reg->sr_replace = ch_strdup( replace );
270 /* Precompile matching pattern */
271 rc = regcomp( ®->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
274 LDAP_LOG( TRANSPORT, ERR,
275 "slap_sasl_regexp_config: \"%s\" could not be compiled.\n",
276 reg->sr_match, 0, 0 );
278 Debug( LDAP_DEBUG_ANY,
279 "SASL match pattern %s could not be compiled by regexp engine\n",
280 reg->sr_match, 0, 0 );
283 return( LDAP_OTHER );
286 rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
287 if ( rc != LDAP_SUCCESS ) return rc;
290 return( LDAP_SUCCESS );
294 /* Perform replacement on regexp matches */
295 static void slap_sasl_rx_exp(
299 const char *saslname,
303 int i, n, len, insert;
305 /* Get the total length of the final URI */
309 while( off[n] >= 0 ) {
310 /* Len of next section from replacement string (x,y,z above) */
311 len += off[n] - off[n-1] - 2;
315 /* Len of string from saslname that matched next $i (b,d above) */
316 i = rep[ off[n] + 1 ] - '0';
317 len += str[i].rm_eo - str[i].rm_so;
320 out->bv_val = sl_malloc( len + 1, ctx );
323 /* Fill in URI with replace string, replacing $i as we go */
326 while( off[n] >= 0) {
327 /* Paste in next section from replacement string (x,y,z above) */
328 len = off[n] - off[n-1] - 2;
329 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
334 /* Paste in string from saslname that matched next $i (b,d above) */
335 i = rep[ off[n] + 1 ] - '0';
336 len = str[i].rm_eo - str[i].rm_so;
337 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
343 out->bv_val[insert] = '\0';
346 /* Take the passed in SASL name and attempt to convert it into an
347 LDAP URI to find the matching LDAP entry, using the pattern matching
348 strings given in the saslregexp config file directive(s) */
350 static int slap_sasl_regexp( struct berval *in, struct berval *out, void *ctx )
352 char *saslname = in->bv_val;
354 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
357 memset( out, 0, sizeof( *out ) );
360 LDAP_LOG( TRANSPORT, ENTRY,
361 "slap_sasl_regexp: converting SASL name %s\n", saslname, 0, 0 );
363 Debug( LDAP_DEBUG_TRACE, "slap_sasl_regexp: converting SASL name %s\n",
367 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
371 /* Match the normalized SASL name to the saslregexp patterns */
372 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
373 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE,
374 sr_strings, 0) == 0 )
378 if( i >= nSaslRegexp ) return( 0 );
381 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
382 * replace pattern of the form "x$1y$2z". The returned string needs
383 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
385 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
386 sr_strings, saslname, out, ctx );
389 LDAP_LOG( TRANSPORT, ENTRY,
390 "slap_sasl_regexp: converted SASL name to %s\n",
391 out->bv_len ? out->bv_val : "", 0, 0 );
393 Debug( LDAP_DEBUG_TRACE,
394 "slap_sasl_regexp: converted SASL name to %s\n",
395 out->bv_len ? out->bv_val : "", 0, 0 );
401 /* This callback actually does some work...*/
402 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
404 struct berval *ndn = o->o_callback->sc_private;
406 if (rs->sr_type != REP_SEARCH) return 0;
408 /* We only want to be called once */
410 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
414 LDAP_LOG( TRANSPORT, DETAIL1,
415 "slap_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
417 Debug( LDAP_DEBUG_TRACE,
418 "slap_sasl2dn: search DN returned more than 1 entry\n", 0,0,0 );
423 ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
428 typedef struct smatch_info {
433 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
435 smatch_info *sm = o->o_callback->sc_private;
437 if (rs->sr_type != REP_SEARCH) return 0;
439 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
441 return -1; /* short-circuit the search */
448 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
449 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
450 * the rule must be used as an internal search for entries. If that search
451 * returns the *assertDN entry, the match is successful.
453 * The assertDN should not have the dn: prefix
457 int slap_sasl_match( Operation *opx, struct berval *rule,
458 struct berval *assertDN, struct berval *authc )
463 slap_callback cb = { sasl_sc_smatch, NULL };
465 SlapReply rs = {REP_RESULT};
468 LDAP_LOG( TRANSPORT, ENTRY,
469 "slap_sasl_match: comparing DN %s to rule %s\n",
470 assertDN->bv_val, rule->bv_val,0 );
472 Debug( LDAP_DEBUG_TRACE,
473 "===>slap_sasl_match: comparing DN %s to rule %s\n",
474 assertDN->bv_val, rule->bv_val, 0 );
477 rc = slap_parseURI( opx, rule, &op.o_req_dn,
478 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
480 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
482 /* Massive shortcut: search scope == base */
483 switch ( op.oq_search.rs_scope ) {
484 case LDAP_SCOPE_BASE:
485 case LDAP_X_SCOPE_EXACT:
486 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
489 rc = LDAP_INAPPROPRIATE_AUTH;
493 case LDAP_X_SCOPE_REGEX:
494 case LDAP_X_SCOPE_EXACTREGEX:
495 rc = regcomp(®, op.o_req_ndn.bv_val,
496 REG_EXTENDED|REG_ICASE|REG_NOSUB);
498 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
504 rc = LDAP_INAPPROPRIATE_AUTH;
512 /* Must run an internal search. */
513 if ( op.oq_search.rs_filter == NULL ) {
514 rc = LDAP_FILTER_ERROR;
519 LDAP_LOG( TRANSPORT, DETAIL1,
520 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
521 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
523 Debug( LDAP_DEBUG_TRACE,
524 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
525 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
528 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
529 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
530 rc = LDAP_INAPPROPRIATE_AUTH;
538 op.o_tag = LDAP_REQ_SEARCH;
539 op.o_protocol = LDAP_VERSION3;
542 op.o_time = slap_get_time();
543 op.o_do_not_cache = 1;
544 op.o_is_auth_check = 1;
545 op.o_threadctx = opx->o_threadctx;
546 op.o_tmpmemctx = opx->o_tmpmemctx;
547 op.o_tmpmfuncs = opx->o_tmpmfuncs;
551 op.o_conn = opx->o_conn;
552 op.o_connid = opx->o_connid;
553 op.o_req_dn = op.o_req_ndn;
555 op.o_bd->be_search( &op, &rs );
560 rc = LDAP_INAPPROPRIATE_AUTH;
564 if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
565 if( op.o_req_ndn.bv_len ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
566 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
567 if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
570 LDAP_LOG( TRANSPORT, ENTRY,
571 "slap_sasl_match: comparison returned %d\n", rc, 0, 0 );
573 Debug( LDAP_DEBUG_TRACE,
574 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
582 * This function answers the question, "Can this ID authorize to that ID?",
583 * based on authorization rules. The rules are stored in the *searchDN, in the
584 * attribute named by *attr. If any of those rules map to the *assertDN, the
585 * authorization is approved.
587 * The DNs should not have the dn: prefix
590 slap_sasl_check_authz( Operation *op,
591 struct berval *searchDN,
592 struct berval *assertDN,
593 AttributeDescription *ad,
594 struct berval *authc )
600 LDAP_LOG( TRANSPORT, ENTRY,
601 "slap_sasl_check_authz: does %s match %s rule in %s?\n",
602 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
604 Debug( LDAP_DEBUG_TRACE,
605 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
606 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
609 rc = backend_attribute( op, NULL,
610 searchDN, ad, &vals );
611 if( rc != LDAP_SUCCESS ) goto COMPLETE;
613 /* Check if the *assertDN matches any **vals */
615 for( i=0; vals[i].bv_val != NULL; i++ ) {
616 rc = slap_sasl_match( op, &vals[i], assertDN, authc );
617 if ( rc == LDAP_SUCCESS ) goto COMPLETE;
620 rc = LDAP_INAPPROPRIATE_AUTH;
623 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
626 LDAP_LOG( TRANSPORT, RESULTS,
627 "slap_sasl_check_authz: %s check returning %s\n",
628 ad->ad_cname.bv_val, rc, 0 );
630 Debug( LDAP_DEBUG_TRACE,
631 "<==slap_sasl_check_authz: %s check returning %d\n",
632 ad->ad_cname.bv_val, rc, 0);
639 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
640 * return the LDAP DN to which it matches. The SASL regexp rules in the config
641 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
642 * search with scope=base), just return the URI (or its searchbase). Otherwise
643 * an internal search must be done, and if that search returns exactly one
644 * entry, return the DN of that one entry.
646 void slap_sasl2dn( Operation *opx,
647 struct berval *saslname, struct berval *sasldn )
650 slap_callback cb = { sasl_sc_sasl2dn, NULL };
652 SlapReply rs = {REP_RESULT};
653 struct berval regout = { 0, NULL };
656 LDAP_LOG( TRANSPORT, ENTRY,
657 "slap_sasl2dn: converting SASL name %s to DN.\n",
658 saslname->bv_val, 0, 0 );
660 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
661 "converting SASL name %s to a DN\n",
662 saslname->bv_val, 0,0 );
665 sasldn->bv_val = NULL;
667 cb.sc_private = sasldn;
669 /* Convert the SASL name into a minimal URI */
670 if( !slap_sasl_regexp( saslname, ®out, opx->o_tmpmemctx ) ) {
674 rc = slap_parseURI( opx, ®out, &op.o_req_dn,
675 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
677 if( regout.bv_val ) sl_free( regout.bv_val, opx->o_tmpmemctx );
678 if( rc != LDAP_SUCCESS ) {
682 /* Must do an internal search */
683 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
685 /* Massive shortcut: search scope == base */
686 switch ( op.oq_search.rs_scope ) {
687 case LDAP_SCOPE_BASE:
688 case LDAP_X_SCOPE_EXACT:
689 case LDAP_X_SCOPE_EXACTREGEX:
690 *sasldn = op.o_req_ndn;
691 op.o_req_ndn.bv_len = 0;
692 op.o_req_ndn.bv_val = NULL;
693 /* intentionally continue to next case */
695 case LDAP_X_SCOPE_REGEX:
704 LDAP_LOG( TRANSPORT, DETAIL1,
705 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
706 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
708 Debug( LDAP_DEBUG_TRACE,
709 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
710 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
713 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
717 op.o_conn = opx->o_conn;
718 op.o_connid = opx->o_connid;
719 op.o_tag = LDAP_REQ_SEARCH;
720 op.o_protocol = LDAP_VERSION3;
721 op.o_ndn = opx->o_conn->c_ndn;
723 op.o_time = slap_get_time();
724 op.o_do_not_cache = 1;
725 op.o_is_auth_check = 1;
726 op.o_threadctx = opx->o_threadctx;
727 op.o_tmpmemctx = opx->o_tmpmemctx;
728 op.o_tmpmfuncs = opx->o_tmpmfuncs;
732 op.oq_search.rs_deref = LDAP_DEREF_NEVER;
733 op.oq_search.rs_slimit = 1;
734 op.oq_search.rs_attrsonly = 1;
735 op.o_req_dn = op.o_req_ndn;
737 op.o_bd->be_search( &op, &rs );
740 if( sasldn->bv_len ) {
741 opx->o_conn->c_authz_backend = op.o_bd;
743 if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
744 if( op.o_req_ndn.bv_len ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
745 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
746 if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
749 LDAP_LOG( TRANSPORT, ENTRY,
750 "slap_sasl2dn: Converted SASL name to %s\n",
751 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
753 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
754 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
761 /* Check if a bind can SASL authorize to another identity.
762 * The DNs should not have the dn: prefix
765 int slap_sasl_authorized( Operation *op,
766 struct berval *authcDN, struct berval *authzDN )
768 int rc = LDAP_INAPPROPRIATE_AUTH;
770 /* User binding as anonymous */
771 if ( authzDN == NULL ) {
777 LDAP_LOG( TRANSPORT, ENTRY,
778 "slap_sasl_authorized: can %s become %s?\n",
779 authcDN->bv_val, authzDN->bv_val, 0 );
781 Debug( LDAP_DEBUG_TRACE,
782 "==>slap_sasl_authorized: can %s become %s?\n",
783 authcDN->bv_val, authzDN->bv_val, 0 );
786 /* If person is authorizing to self, succeed */
787 if ( dn_match( authcDN, authzDN ) ) {
792 /* Allow the manager to authorize as any DN. */
793 if( op->o_conn->c_authz_backend && be_isroot( op->o_conn->c_authz_backend, authcDN )) {
798 /* Check source rules */
799 if( authz_policy & SASL_AUTHZ_TO ) {
800 rc = slap_sasl_check_authz( op, authcDN, authzDN,
801 slap_schema.si_ad_saslAuthzTo, authcDN );
802 if( rc == LDAP_SUCCESS ) {
807 /* Check destination rules */
808 if( authz_policy & SASL_AUTHZ_FROM ) {
809 rc = slap_sasl_check_authz( op, authzDN, authcDN,
810 slap_schema.si_ad_saslAuthzFrom, authcDN );
811 if( rc == LDAP_SUCCESS ) {
816 rc = LDAP_INAPPROPRIATE_AUTH;
821 LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );
823 Debug( LDAP_DEBUG_TRACE,
824 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );