2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1998-2003 The OpenLDAP Foundation.
5 * Portions Copyright 2000 Mark Adamson, Carnegie Mellon.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
21 #include <ac/stdlib.h>
22 #include <ac/string.h>
30 #define SASLREGEX_REPLACE 10
32 #define LDAP_X_SCOPE_EXACT ((ber_int_t) 0x0010)
33 #define LDAP_X_SCOPE_REGEX ((ber_int_t) 0x0020)
34 #define LDAP_X_SCOPE_CHILDREN ((ber_int_t) 0x0030)
35 #define LDAP_X_SCOPE_SUBTREE ((ber_int_t) 0x0040)
38 * IDs in DNauthzid form can now have a type specifier, that
39 * influences how they are used in related operations.
41 * syntax: dn[.{exact|regex}]:<val>
43 * dn.exact: the value must pass normalization and is used
45 * dn.regex: the value is treated as a regular expression
46 * in matching DN values in saslAuthz{To|From}
48 * dn: for backwards compatibility reasons, the value
49 * is treated as a regular expression, and thus
50 * it is not normalized nor validated; it is used
51 * in exact or regex comparisons based on the
54 * IDs in DNauthzid form can now have a type specifier, that
55 * influences how they are used in related operations.
57 * syntax: u[.mech[/realm]]:<val>
59 * where mech is a SIMPLE, AUTHZ, or a SASL mechanism name
60 * and realm is mechanism specific realm (separate to those
61 * which are representable as part of the principal).
64 typedef struct sasl_regexp {
65 char *sr_match; /* regexp match pattern */
66 char *sr_replace; /* regexp replace pattern */
67 regex_t sr_workspace; /* workspace for regexp engine */
68 int sr_offset[SASLREGEX_REPLACE+2]; /* offsets of $1,$2... in *replace */
71 static int nSaslRegexp = 0;
72 static SaslRegexp_t *SaslRegexp = NULL;
74 /* What SASL proxy authorization policies are allowed? */
75 #define SASL_AUTHZ_NONE 0
76 #define SASL_AUTHZ_FROM 1
77 #define SASL_AUTHZ_TO 2
79 static int authz_policy = SASL_AUTHZ_NONE;
81 int slap_sasl_setpolicy( const char *arg )
83 int rc = LDAP_SUCCESS;
85 if ( strcasecmp( arg, "none" ) == 0 ) {
86 authz_policy = SASL_AUTHZ_NONE;
87 } else if ( strcasecmp( arg, "from" ) == 0 ) {
88 authz_policy = SASL_AUTHZ_FROM;
89 } else if ( strcasecmp( arg, "to" ) == 0 ) {
90 authz_policy = SASL_AUTHZ_TO;
91 } else if ( strcasecmp( arg, "both" ) == 0 ) {
92 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO;
99 int slap_parse_user( struct berval *id, struct berval *user,
100 struct berval *realm, struct berval *mech )
105 assert( id->bv_val );
112 if ( u != 'u' && u != 'U' ) {
113 /* called with something other than u: */
114 return LDAP_PROTOCOL_ERROR;
118 * u[.mech[/realm]]:user
121 user->bv_val = strchr( id->bv_val, ':' );
122 if ( user->bv_val == NULL ) {
123 return LDAP_PROTOCOL_ERROR;
125 user->bv_val[ 0 ] = '\0';
127 user->bv_len = id->bv_len - ( user->bv_val - id->bv_val );
129 mech->bv_val = strchr( id->bv_val, '.' );
130 if ( mech->bv_val != NULL ) {
131 mech->bv_val[ 0 ] = '\0';
134 realm->bv_val = strchr( mech->bv_val, '/' );
136 if ( realm->bv_val ) {
137 realm->bv_val[ 0 ] = '\0';
139 mech->bv_len = realm->bv_val - mech->bv_val - 1;
140 realm->bv_len = user->bv_val - realm->bv_val - 1;
142 mech->bv_len = user->bv_val - mech->bv_val - 1;
146 realm->bv_val = NULL;
149 if ( id->bv_val[ 1 ] != '\0' ) {
150 return LDAP_PROTOCOL_ERROR;
153 if ( mech->bv_val != NULL ) {
154 assert( mech->bv_val == id->bv_val + 2 );
156 AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
160 if ( realm->bv_val ) {
161 assert( realm->bv_val >= id->bv_val + 2 );
163 AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
167 /* leave "u:" before user */
170 user->bv_val[ 0 ] = u;
171 user->bv_val[ 1 ] = ':';
176 static int slap_parseURI( Operation *op, struct berval *uri,
177 struct berval *base, struct berval *nbase,
178 int *scope, Filter **filter, struct berval *fstr )
184 assert( uri != NULL && uri->bv_val != NULL );
187 nbase->bv_val = NULL;
195 LDAP_LOG( TRANSPORT, ENTRY,
196 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
198 Debug( LDAP_DEBUG_TRACE,
199 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
202 rc = LDAP_PROTOCOL_ERROR;
203 if ( !strncasecmp( uri->bv_val, "dn", sizeof( "dn" ) - 1 ) ) {
204 bv.bv_val = uri->bv_val + sizeof( "dn" ) - 1;
206 if ( bv.bv_val[ 0 ] == '.' ) {
209 if ( !strncasecmp( bv.bv_val, "exact:", sizeof( "exact:" ) - 1 ) ) {
210 bv.bv_val += sizeof( "exact" ) - 1;
211 *scope = LDAP_X_SCOPE_EXACT;
213 } else if ( !strncasecmp( bv.bv_val, "regex:", sizeof( "regex:" ) - 1 ) ) {
214 bv.bv_val += sizeof( "regex" ) - 1;
215 *scope = LDAP_X_SCOPE_REGEX;
217 } else if ( !strncasecmp( bv.bv_val, "children:", sizeof( "chldren:" ) - 1 ) ) {
218 bv.bv_val += sizeof( "children" ) - 1;
219 *scope = LDAP_X_SCOPE_CHILDREN;
221 } else if ( !strncasecmp( bv.bv_val, "subtree:", sizeof( "subtree:" ) - 1 ) ) {
222 bv.bv_val += sizeof( "subtree" ) - 1;
223 *scope = LDAP_X_SCOPE_SUBTREE;
226 return LDAP_PROTOCOL_ERROR;
230 if ( bv.bv_val[ 0 ] != ':' ) {
231 return LDAP_PROTOCOL_ERROR;
235 bv.bv_val += strspn( bv.bv_val, " " );
236 /* jump here in case no type specification was present
237 * and uir was not an URI... HEADS-UP: assuming EXACT */
238 is_dn: bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
241 case LDAP_X_SCOPE_EXACT:
242 case LDAP_X_SCOPE_CHILDREN:
243 case LDAP_X_SCOPE_SUBTREE:
244 rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
245 if( rc != LDAP_SUCCESS ) {
250 case LDAP_X_SCOPE_REGEX:
251 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
262 } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
263 && ( uri->bv_val[ 1 ] == ':'
264 || uri->bv_val[ 1 ] == '/'
265 || uri->bv_val[ 1 ] == '.' ) )
267 Connection c = *op->o_conn;
268 char buf[ SLAP_LDAPDN_MAXLEN ];
269 struct berval id = { uri->bv_len, (char *)buf },
274 if ( sizeof( buf ) <= uri->bv_len ) {
275 return LDAP_INVALID_SYNTAX;
278 strncpy( buf, uri->bv_val, sizeof( buf ) );
280 rc = slap_parse_user( &id, &user, &realm, &mech );
281 if ( rc != LDAP_SUCCESS ) {
286 c.c_sasl_bind_mech = mech;
288 c.c_sasl_bind_mech.bv_val = "AUTHZ";
289 c.c_sasl_bind_mech.bv_len = sizeof( "AUTHZ" ) - 1;
292 rc = slap_sasl_getdn( &c, op, user.bv_val, user.bv_len,
293 realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
295 if ( rc == LDAP_SUCCESS ) {
296 *scope = LDAP_X_SCOPE_EXACT;
302 rc = ldap_url_parse( uri->bv_val, &ludp );
303 if ( rc == LDAP_URL_ERR_BADSCHEME ) {
304 /* last chance: assume it's a(n exact) DN ... */
305 bv.bv_val = uri->bv_val;
306 *scope = LDAP_X_SCOPE_EXACT;
310 if ( rc != LDAP_URL_SUCCESS ) {
311 return LDAP_PROTOCOL_ERROR;
314 if (( ludp->lud_host && *ludp->lud_host )
315 || ludp->lud_attrs || ludp->lud_exts )
317 /* host part must be empty */
318 /* attrs and extensions parts must be empty */
319 rc = LDAP_PROTOCOL_ERROR;
324 *scope = ludp->lud_scope;
326 /* Grab the filter */
327 if ( ludp->lud_filter ) {
328 *filter = str2filter_x( op, ludp->lud_filter );
329 if ( *filter == NULL ) {
330 rc = LDAP_PROTOCOL_ERROR;
333 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
336 /* Grab the searchbase */
337 ber_str2bv( ludp->lud_dn, 0, 0, base );
338 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
341 if( rc != LDAP_SUCCESS ) {
342 if( *filter ) filter_free_x( op, *filter );
348 /* Don't free these, return them to caller */
349 ludp->lud_filter = NULL;
353 ldap_free_urldesc( ludp );
357 static int slap_sasl_rx_off(char *rep, int *off)
362 /* Precompile replace pattern. Find the $<n> placeholders */
365 for ( c = rep; *c; c++ ) {
366 if ( *c == '\\' && c[1] ) {
371 if ( n == SASLREGEX_REPLACE ) {
373 LDAP_LOG( TRANSPORT, ERR,
374 "slap_sasl_rx_off: \"%s\" has too many $n "
375 "placeholders (max %d)\n", rep, SASLREGEX_REPLACE, 0 );
377 Debug( LDAP_DEBUG_ANY,
378 "SASL replace pattern %s has too many $n "
379 "placeholders (max %d)\n",
380 rep, SASLREGEX_REPLACE, 0 );
383 return( LDAP_OTHER );
390 /* Final placeholder, after the last $n */
394 return( LDAP_SUCCESS );
397 int slap_sasl_regexp_config( const char *match, const char *replace )
402 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
403 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
405 reg = &SaslRegexp[nSaslRegexp];
407 reg->sr_match = ch_strdup( match );
408 reg->sr_replace = ch_strdup( replace );
410 /* Precompile matching pattern */
411 rc = regcomp( ®->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
414 LDAP_LOG( TRANSPORT, ERR,
415 "slap_sasl_regexp_config: \"%s\" could not be compiled.\n",
416 reg->sr_match, 0, 0 );
418 Debug( LDAP_DEBUG_ANY,
419 "SASL match pattern %s could not be compiled by regexp engine\n",
420 reg->sr_match, 0, 0 );
423 return( LDAP_OTHER );
426 rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
427 if ( rc != LDAP_SUCCESS ) return rc;
430 return( LDAP_SUCCESS );
434 /* Perform replacement on regexp matches */
435 static void slap_sasl_rx_exp(
439 const char *saslname,
443 int i, n, len, insert;
445 /* Get the total length of the final URI */
449 while( off[n] >= 0 ) {
450 /* Len of next section from replacement string (x,y,z above) */
451 len += off[n] - off[n-1] - 2;
455 /* Len of string from saslname that matched next $i (b,d above) */
456 i = rep[ off[n] + 1 ] - '0';
457 len += str[i].rm_eo - str[i].rm_so;
460 out->bv_val = sl_malloc( len + 1, ctx );
463 /* Fill in URI with replace string, replacing $i as we go */
466 while( off[n] >= 0) {
467 /* Paste in next section from replacement string (x,y,z above) */
468 len = off[n] - off[n-1] - 2;
469 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
474 /* Paste in string from saslname that matched next $i (b,d above) */
475 i = rep[ off[n] + 1 ] - '0';
476 len = str[i].rm_eo - str[i].rm_so;
477 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
483 out->bv_val[insert] = '\0';
486 /* Take the passed in SASL name and attempt to convert it into an
487 LDAP URI to find the matching LDAP entry, using the pattern matching
488 strings given in the saslregexp config file directive(s) */
490 static int slap_sasl_regexp( struct berval *in, struct berval *out, void *ctx )
492 char *saslname = in->bv_val;
494 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
497 memset( out, 0, sizeof( *out ) );
500 LDAP_LOG( TRANSPORT, ENTRY,
501 "slap_sasl_regexp: converting SASL name %s\n", saslname, 0, 0 );
503 Debug( LDAP_DEBUG_TRACE, "slap_sasl_regexp: converting SASL name %s\n",
507 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
511 /* Match the normalized SASL name to the saslregexp patterns */
512 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
513 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE,
514 sr_strings, 0) == 0 )
518 if( i >= nSaslRegexp ) return( 0 );
521 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
522 * replace pattern of the form "x$1y$2z". The returned string needs
523 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
525 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
526 sr_strings, saslname, out, ctx );
529 LDAP_LOG( TRANSPORT, ENTRY,
530 "slap_sasl_regexp: converted SASL name to %s\n",
531 out->bv_len ? out->bv_val : "", 0, 0 );
533 Debug( LDAP_DEBUG_TRACE,
534 "slap_sasl_regexp: converted SASL name to %s\n",
535 out->bv_len ? out->bv_val : "", 0, 0 );
541 /* This callback actually does some work...*/
542 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
544 struct berval *ndn = o->o_callback->sc_private;
546 if (rs->sr_type != REP_SEARCH) return 0;
548 /* We only want to be called once */
550 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
554 LDAP_LOG( TRANSPORT, DETAIL1,
555 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
557 Debug( LDAP_DEBUG_TRACE,
558 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
563 ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
568 typedef struct smatch_info {
573 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
575 smatch_info *sm = o->o_callback->sc_private;
577 if (rs->sr_type != REP_SEARCH) return 0;
579 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
581 return -1; /* short-circuit the search */
588 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
589 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
590 * the rule must be used as an internal search for entries. If that search
591 * returns the *assertDN entry, the match is successful.
593 * The assertDN should not have the dn: prefix
597 int slap_sasl_match( Operation *opx, struct berval *rule,
598 struct berval *assertDN, struct berval *authc )
603 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
605 SlapReply rs = {REP_RESULT};
608 LDAP_LOG( TRANSPORT, ENTRY,
609 "slap_sasl_match: comparing DN %s to rule %s\n",
610 assertDN->bv_val, rule->bv_val,0 );
612 Debug( LDAP_DEBUG_TRACE,
613 "===>slap_sasl_match: comparing DN %s to rule %s\n",
614 assertDN->bv_val, rule->bv_val, 0 );
617 rc = slap_parseURI( opx, rule, &op.o_req_dn,
618 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
620 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
622 /* Massive shortcut: search scope == base */
623 switch ( op.oq_search.rs_scope ) {
624 case LDAP_SCOPE_BASE:
625 case LDAP_X_SCOPE_EXACT:
627 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
630 rc = LDAP_INAPPROPRIATE_AUTH;
634 case LDAP_X_SCOPE_CHILDREN:
635 case LDAP_X_SCOPE_SUBTREE:
637 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
639 rc = LDAP_INAPPROPRIATE_AUTH;
641 if ( d == 0 && op.oq_search.rs_scope == LDAP_X_SCOPE_SUBTREE ) {
644 } else if ( d > 0 ) {
645 struct berval bv = { op.o_req_ndn.bv_len, assertDN->bv_val + d };
647 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
654 case LDAP_X_SCOPE_REGEX:
655 rc = regcomp(®, op.o_req_ndn.bv_val,
656 REG_EXTENDED|REG_ICASE|REG_NOSUB);
658 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
664 rc = LDAP_INAPPROPRIATE_AUTH;
672 /* Must run an internal search. */
673 if ( op.oq_search.rs_filter == NULL ) {
674 rc = LDAP_FILTER_ERROR;
679 LDAP_LOG( TRANSPORT, DETAIL1,
680 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
681 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
683 Debug( LDAP_DEBUG_TRACE,
684 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
685 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
688 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
689 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
690 rc = LDAP_INAPPROPRIATE_AUTH;
698 op.o_tag = LDAP_REQ_SEARCH;
699 op.o_protocol = LDAP_VERSION3;
702 op.o_time = slap_get_time();
703 op.o_do_not_cache = 1;
704 op.o_is_auth_check = 1;
705 op.o_threadctx = opx->o_threadctx;
706 op.o_tmpmemctx = opx->o_tmpmemctx;
707 op.o_tmpmfuncs = opx->o_tmpmfuncs;
711 op.o_conn = opx->o_conn;
712 op.o_connid = opx->o_connid;
713 op.o_req_dn = op.o_req_ndn;
715 op.o_bd->be_search( &op, &rs );
720 rc = LDAP_INAPPROPRIATE_AUTH;
724 if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
725 if( op.o_req_ndn.bv_len ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
726 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
727 if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
730 LDAP_LOG( TRANSPORT, ENTRY,
731 "slap_sasl_match: comparison returned %d\n", rc, 0, 0 );
733 Debug( LDAP_DEBUG_TRACE,
734 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
742 * This function answers the question, "Can this ID authorize to that ID?",
743 * based on authorization rules. The rules are stored in the *searchDN, in the
744 * attribute named by *attr. If any of those rules map to the *assertDN, the
745 * authorization is approved.
747 * The DNs should not have the dn: prefix
750 slap_sasl_check_authz( Operation *op,
751 struct berval *searchDN,
752 struct berval *assertDN,
753 AttributeDescription *ad,
754 struct berval *authc )
760 LDAP_LOG( TRANSPORT, ENTRY,
761 "slap_sasl_check_authz: does %s match %s rule in %s?\n",
762 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
764 Debug( LDAP_DEBUG_TRACE,
765 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
766 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
769 rc = backend_attribute( op, NULL,
770 searchDN, ad, &vals );
771 if( rc != LDAP_SUCCESS ) goto COMPLETE;
773 /* Check if the *assertDN matches any **vals */
775 for( i=0; vals[i].bv_val != NULL; i++ ) {
776 rc = slap_sasl_match( op, &vals[i], assertDN, authc );
777 if ( rc == LDAP_SUCCESS ) goto COMPLETE;
780 rc = LDAP_INAPPROPRIATE_AUTH;
783 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
786 LDAP_LOG( TRANSPORT, RESULTS,
787 "slap_sasl_check_authz: %s check returning %s\n",
788 ad->ad_cname.bv_val, rc, 0 );
790 Debug( LDAP_DEBUG_TRACE,
791 "<==slap_sasl_check_authz: %s check returning %d\n",
792 ad->ad_cname.bv_val, rc, 0);
799 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
800 * return the LDAP DN to which it matches. The SASL regexp rules in the config
801 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
802 * search with scope=base), just return the URI (or its searchbase). Otherwise
803 * an internal search must be done, and if that search returns exactly one
804 * entry, return the DN of that one entry.
806 void slap_sasl2dn( Operation *opx,
807 struct berval *saslname, struct berval *sasldn )
810 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
812 SlapReply rs = {REP_RESULT};
813 struct berval regout = { 0, NULL };
816 LDAP_LOG( TRANSPORT, ENTRY,
817 "slap_sasl2dn: converting SASL name %s to DN.\n",
818 saslname->bv_val, 0, 0 );
820 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
821 "converting SASL name %s to a DN\n",
822 saslname->bv_val, 0,0 );
825 sasldn->bv_val = NULL;
827 cb.sc_private = sasldn;
829 /* Convert the SASL name into a minimal URI */
830 if( !slap_sasl_regexp( saslname, ®out, opx->o_tmpmemctx ) ) {
834 rc = slap_parseURI( opx, ®out, &op.o_req_dn,
835 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
837 if( regout.bv_val ) sl_free( regout.bv_val, opx->o_tmpmemctx );
838 if( rc != LDAP_SUCCESS ) {
842 /* Must do an internal search */
843 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
845 /* Massive shortcut: search scope == base */
846 switch ( op.oq_search.rs_scope ) {
847 case LDAP_SCOPE_BASE:
848 case LDAP_X_SCOPE_EXACT:
849 *sasldn = op.o_req_ndn;
850 op.o_req_ndn.bv_len = 0;
851 op.o_req_ndn.bv_val = NULL;
852 /* intentionally continue to next case */
854 case LDAP_X_SCOPE_REGEX:
855 case LDAP_X_SCOPE_SUBTREE:
856 case LDAP_X_SCOPE_CHILDREN:
857 /* correctly parsed, but illegal */
860 case LDAP_SCOPE_ONELEVEL:
861 case LDAP_SCOPE_SUBTREE:
866 /* catch unhandled cases (there shouldn't be) */
871 LDAP_LOG( TRANSPORT, DETAIL1,
872 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
873 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
875 Debug( LDAP_DEBUG_TRACE,
876 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
877 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
880 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
884 op.o_conn = opx->o_conn;
885 op.o_connid = opx->o_connid;
886 op.o_tag = LDAP_REQ_SEARCH;
887 op.o_protocol = LDAP_VERSION3;
888 op.o_ndn = opx->o_conn->c_ndn;
890 op.o_time = slap_get_time();
891 op.o_do_not_cache = 1;
892 op.o_is_auth_check = 1;
893 op.o_threadctx = opx->o_threadctx;
894 op.o_tmpmemctx = opx->o_tmpmemctx;
895 op.o_tmpmfuncs = opx->o_tmpmfuncs;
899 op.oq_search.rs_deref = LDAP_DEREF_NEVER;
900 op.oq_search.rs_slimit = 1;
901 op.oq_search.rs_attrsonly = 1;
902 op.o_req_dn = op.o_req_ndn;
904 op.o_bd->be_search( &op, &rs );
907 if( sasldn->bv_len ) {
908 opx->o_conn->c_authz_backend = op.o_bd;
910 if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
911 if( op.o_req_ndn.bv_len ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
912 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
913 if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
916 LDAP_LOG( TRANSPORT, ENTRY,
917 "slap_sasl2dn: Converted SASL name to %s\n",
918 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
920 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
921 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
928 /* Check if a bind can SASL authorize to another identity.
929 * The DNs should not have the dn: prefix
932 int slap_sasl_authorized( Operation *op,
933 struct berval *authcDN, struct berval *authzDN )
935 int rc = LDAP_INAPPROPRIATE_AUTH;
937 /* User binding as anonymous */
938 if ( authzDN == NULL ) {
944 LDAP_LOG( TRANSPORT, ENTRY,
945 "slap_sasl_authorized: can %s become %s?\n",
946 authcDN->bv_val, authzDN->bv_val, 0 );
948 Debug( LDAP_DEBUG_TRACE,
949 "==>slap_sasl_authorized: can %s become %s?\n",
950 authcDN->bv_val, authzDN->bv_val, 0 );
953 /* If person is authorizing to self, succeed */
954 if ( dn_match( authcDN, authzDN ) ) {
959 /* Allow the manager to authorize as any DN. */
960 if( op->o_conn->c_authz_backend && be_isroot( op->o_conn->c_authz_backend, authcDN )) {
965 /* Check source rules */
966 if( authz_policy & SASL_AUTHZ_TO ) {
967 rc = slap_sasl_check_authz( op, authcDN, authzDN,
968 slap_schema.si_ad_saslAuthzTo, authcDN );
969 if( rc == LDAP_SUCCESS ) {
974 /* Check destination rules */
975 if( authz_policy & SASL_AUTHZ_FROM ) {
976 rc = slap_sasl_check_authz( op, authzDN, authcDN,
977 slap_schema.si_ad_saslAuthzFrom, authcDN );
978 if( rc == LDAP_SUCCESS ) {
983 rc = LDAP_INAPPROPRIATE_AUTH;
988 LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );
990 Debug( LDAP_DEBUG_TRACE,
991 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );