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( id->bv_val, '/' );
136 if ( realm->bv_val ) {
137 mech->bv_len = realm->bv_val - mech->bv_val - 1;
138 realm->bv_len = user->bv_val - realm->bv_val - 1;
140 mech->bv_len = user->bv_val - mech->bv_val - 1;
144 realm->bv_val = NULL;
147 if ( id->bv_val[ 1 ] != '\0' ) {
148 return LDAP_PROTOCOL_ERROR;
151 if ( mech->bv_val != NULL ) {
152 assert( mech->bv_val == id->bv_val + 2 );
154 AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
158 if ( realm->bv_val ) {
159 assert( realm->bv_val >= id->bv_val + 2 );
161 AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
165 /* leave "u:" before user */
168 user->bv_val[ 0 ] = u;
169 user->bv_val[ 1 ] = ':';
174 static int slap_parseURI( Operation *op, struct berval *uri,
175 struct berval *base, struct berval *nbase,
176 int *scope, Filter **filter, struct berval *fstr )
182 assert( uri != NULL && uri->bv_val != NULL );
185 nbase->bv_val = NULL;
193 LDAP_LOG( TRANSPORT, ENTRY,
194 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
196 Debug( LDAP_DEBUG_TRACE,
197 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
200 rc = LDAP_PROTOCOL_ERROR;
201 if ( !strncasecmp( uri->bv_val, "dn", sizeof( "dn" ) - 1 ) ) {
202 bv.bv_val = uri->bv_val + sizeof( "dn" ) - 1;
204 if ( bv.bv_val[ 0 ] == '.' ) {
207 if ( !strncasecmp( bv.bv_val, "exact:", sizeof( "exact:" ) - 1 ) ) {
208 bv.bv_val += sizeof( "exact" ) - 1;
209 *scope = LDAP_X_SCOPE_EXACT;
211 } else if ( !strncasecmp( bv.bv_val, "regex:", sizeof( "regex:" ) - 1 ) ) {
212 bv.bv_val += sizeof( "regex" ) - 1;
213 *scope = LDAP_X_SCOPE_REGEX;
215 } else if ( !strncasecmp( bv.bv_val, "children:", sizeof( "chldren:" ) - 1 ) ) {
216 bv.bv_val += sizeof( "children" ) - 1;
217 *scope = LDAP_X_SCOPE_CHILDREN;
219 } else if ( !strncasecmp( bv.bv_val, "subtree:", sizeof( "subtree:" ) - 1 ) ) {
220 bv.bv_val += sizeof( "subtree" ) - 1;
221 *scope = LDAP_X_SCOPE_SUBTREE;
224 return LDAP_PROTOCOL_ERROR;
228 if ( bv.bv_val[ 0 ] != ':' ) {
229 return LDAP_PROTOCOL_ERROR;
233 bv.bv_val += strspn( bv.bv_val, " " );
234 /* jump here in case no type specification was present
235 * and uir was not an URI... HEADS-UP: assuming EXACT */
236 is_dn: bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
239 case LDAP_X_SCOPE_EXACT:
240 case LDAP_X_SCOPE_CHILDREN:
241 case LDAP_X_SCOPE_SUBTREE:
242 rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
243 if( rc != LDAP_SUCCESS ) {
248 case LDAP_X_SCOPE_REGEX:
249 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
260 } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
261 && ( uri->bv_val[ 1 ] == ':'
262 || uri->bv_val[ 1 ] == '/'
263 || uri->bv_val[ 1 ] == '.' ) )
265 Connection c = *op->o_conn;
266 char buf[ SLAP_LDAPDN_MAXLEN ];
267 struct berval id = { uri->bv_len, (char *)buf },
272 if ( sizeof( buf ) <= uri->bv_len ) {
273 return LDAP_INVALID_SYNTAX;
276 strncpy( buf, uri->bv_val, sizeof( buf ) );
278 rc = slap_parse_user( &id, &user, &realm, &mech );
279 if ( rc != LDAP_SUCCESS ) {
284 c.c_sasl_bind_mech = mech;
286 c.c_sasl_bind_mech.bv_val = "AUTHZ";
287 c.c_sasl_bind_mech.bv_len = sizeof( "AUTHZ" ) - 1;
290 rc = slap_sasl_getdn( &c, op, user.bv_val, user.bv_len,
291 realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
293 if ( rc == LDAP_SUCCESS ) {
294 *scope = LDAP_X_SCOPE_EXACT;
300 rc = ldap_url_parse( uri->bv_val, &ludp );
301 if ( rc == LDAP_URL_ERR_BADSCHEME ) {
302 /* last chance: assume it's a(n exact) DN ... */
303 bv.bv_val = uri->bv_val;
304 *scope = LDAP_X_SCOPE_EXACT;
308 if ( rc != LDAP_URL_SUCCESS ) {
309 return LDAP_PROTOCOL_ERROR;
312 if (( ludp->lud_host && *ludp->lud_host )
313 || ludp->lud_attrs || ludp->lud_exts )
315 /* host part must be empty */
316 /* attrs and extensions parts must be empty */
317 rc = LDAP_PROTOCOL_ERROR;
322 *scope = ludp->lud_scope;
324 /* Grab the filter */
325 if ( ludp->lud_filter ) {
326 *filter = str2filter_x( op, ludp->lud_filter );
327 if ( *filter == NULL ) {
328 rc = LDAP_PROTOCOL_ERROR;
331 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
334 /* Grab the searchbase */
335 ber_str2bv( ludp->lud_dn, 0, 0, base );
336 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
339 if( rc != LDAP_SUCCESS ) {
340 if( *filter ) filter_free_x( op, *filter );
346 /* Don't free these, return them to caller */
347 ludp->lud_filter = NULL;
351 ldap_free_urldesc( ludp );
355 static int slap_sasl_rx_off(char *rep, int *off)
360 /* Precompile replace pattern. Find the $<n> placeholders */
363 for ( c = rep; *c; c++ ) {
364 if ( *c == '\\' && c[1] ) {
369 if ( n == SASLREGEX_REPLACE ) {
371 LDAP_LOG( TRANSPORT, ERR,
372 "slap_sasl_rx_off: \"%s\" has too many $n "
373 "placeholders (max %d)\n", rep, SASLREGEX_REPLACE, 0 );
375 Debug( LDAP_DEBUG_ANY,
376 "SASL replace pattern %s has too many $n "
377 "placeholders (max %d)\n",
378 rep, SASLREGEX_REPLACE, 0 );
381 return( LDAP_OTHER );
388 /* Final placeholder, after the last $n */
392 return( LDAP_SUCCESS );
395 int slap_sasl_regexp_config( const char *match, const char *replace )
400 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
401 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
403 reg = &SaslRegexp[nSaslRegexp];
405 reg->sr_match = ch_strdup( match );
406 reg->sr_replace = ch_strdup( replace );
408 /* Precompile matching pattern */
409 rc = regcomp( ®->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
412 LDAP_LOG( TRANSPORT, ERR,
413 "slap_sasl_regexp_config: \"%s\" could not be compiled.\n",
414 reg->sr_match, 0, 0 );
416 Debug( LDAP_DEBUG_ANY,
417 "SASL match pattern %s could not be compiled by regexp engine\n",
418 reg->sr_match, 0, 0 );
421 return( LDAP_OTHER );
424 rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
425 if ( rc != LDAP_SUCCESS ) return rc;
428 return( LDAP_SUCCESS );
432 /* Perform replacement on regexp matches */
433 static void slap_sasl_rx_exp(
437 const char *saslname,
441 int i, n, len, insert;
443 /* Get the total length of the final URI */
447 while( off[n] >= 0 ) {
448 /* Len of next section from replacement string (x,y,z above) */
449 len += off[n] - off[n-1] - 2;
453 /* Len of string from saslname that matched next $i (b,d above) */
454 i = rep[ off[n] + 1 ] - '0';
455 len += str[i].rm_eo - str[i].rm_so;
458 out->bv_val = sl_malloc( len + 1, ctx );
461 /* Fill in URI with replace string, replacing $i as we go */
464 while( off[n] >= 0) {
465 /* Paste in next section from replacement string (x,y,z above) */
466 len = off[n] - off[n-1] - 2;
467 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
472 /* Paste in string from saslname that matched next $i (b,d above) */
473 i = rep[ off[n] + 1 ] - '0';
474 len = str[i].rm_eo - str[i].rm_so;
475 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
481 out->bv_val[insert] = '\0';
484 /* Take the passed in SASL name and attempt to convert it into an
485 LDAP URI to find the matching LDAP entry, using the pattern matching
486 strings given in the saslregexp config file directive(s) */
488 static int slap_sasl_regexp( struct berval *in, struct berval *out, void *ctx )
490 char *saslname = in->bv_val;
492 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
495 memset( out, 0, sizeof( *out ) );
498 LDAP_LOG( TRANSPORT, ENTRY,
499 "slap_sasl_regexp: converting SASL name %s\n", saslname, 0, 0 );
501 Debug( LDAP_DEBUG_TRACE, "slap_sasl_regexp: converting SASL name %s\n",
505 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
509 /* Match the normalized SASL name to the saslregexp patterns */
510 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
511 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE,
512 sr_strings, 0) == 0 )
516 if( i >= nSaslRegexp ) return( 0 );
519 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
520 * replace pattern of the form "x$1y$2z". The returned string needs
521 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
523 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
524 sr_strings, saslname, out, ctx );
527 LDAP_LOG( TRANSPORT, ENTRY,
528 "slap_sasl_regexp: converted SASL name to %s\n",
529 out->bv_len ? out->bv_val : "", 0, 0 );
531 Debug( LDAP_DEBUG_TRACE,
532 "slap_sasl_regexp: converted SASL name to %s\n",
533 out->bv_len ? out->bv_val : "", 0, 0 );
539 /* This callback actually does some work...*/
540 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
542 struct berval *ndn = o->o_callback->sc_private;
544 if (rs->sr_type != REP_SEARCH) return 0;
546 /* We only want to be called once */
548 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
552 LDAP_LOG( TRANSPORT, DETAIL1,
553 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
555 Debug( LDAP_DEBUG_TRACE,
556 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
561 ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
566 typedef struct smatch_info {
571 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
573 smatch_info *sm = o->o_callback->sc_private;
575 if (rs->sr_type != REP_SEARCH) return 0;
577 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
579 return -1; /* short-circuit the search */
586 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
587 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
588 * the rule must be used as an internal search for entries. If that search
589 * returns the *assertDN entry, the match is successful.
591 * The assertDN should not have the dn: prefix
595 int slap_sasl_match( Operation *opx, struct berval *rule,
596 struct berval *assertDN, struct berval *authc )
601 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
603 SlapReply rs = {REP_RESULT};
606 LDAP_LOG( TRANSPORT, ENTRY,
607 "slap_sasl_match: comparing DN %s to rule %s\n",
608 assertDN->bv_val, rule->bv_val,0 );
610 Debug( LDAP_DEBUG_TRACE,
611 "===>slap_sasl_match: comparing DN %s to rule %s\n",
612 assertDN->bv_val, rule->bv_val, 0 );
615 rc = slap_parseURI( opx, rule, &op.o_req_dn,
616 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
618 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
620 /* Massive shortcut: search scope == base */
621 switch ( op.oq_search.rs_scope ) {
622 case LDAP_SCOPE_BASE:
623 case LDAP_X_SCOPE_EXACT:
625 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
628 rc = LDAP_INAPPROPRIATE_AUTH;
632 case LDAP_X_SCOPE_CHILDREN:
633 case LDAP_X_SCOPE_SUBTREE:
635 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
637 rc = LDAP_INAPPROPRIATE_AUTH;
639 if ( d == 0 && op.oq_search.rs_scope == LDAP_X_SCOPE_SUBTREE ) {
642 } else if ( d > 0 ) {
643 struct berval bv = { op.o_req_ndn.bv_len, assertDN->bv_val + d };
645 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
652 case LDAP_X_SCOPE_REGEX:
653 rc = regcomp(®, op.o_req_ndn.bv_val,
654 REG_EXTENDED|REG_ICASE|REG_NOSUB);
656 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
662 rc = LDAP_INAPPROPRIATE_AUTH;
670 /* Must run an internal search. */
671 if ( op.oq_search.rs_filter == NULL ) {
672 rc = LDAP_FILTER_ERROR;
677 LDAP_LOG( TRANSPORT, DETAIL1,
678 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
679 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
681 Debug( LDAP_DEBUG_TRACE,
682 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
683 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
686 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
687 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
688 rc = LDAP_INAPPROPRIATE_AUTH;
696 op.o_tag = LDAP_REQ_SEARCH;
697 op.o_protocol = LDAP_VERSION3;
700 op.o_time = slap_get_time();
701 op.o_do_not_cache = 1;
702 op.o_is_auth_check = 1;
703 op.o_threadctx = opx->o_threadctx;
704 op.o_tmpmemctx = opx->o_tmpmemctx;
705 op.o_tmpmfuncs = opx->o_tmpmfuncs;
709 op.o_conn = opx->o_conn;
710 op.o_connid = opx->o_connid;
711 op.o_req_dn = op.o_req_ndn;
713 op.o_bd->be_search( &op, &rs );
718 rc = LDAP_INAPPROPRIATE_AUTH;
722 if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
723 if( op.o_req_ndn.bv_len ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
724 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
725 if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
728 LDAP_LOG( TRANSPORT, ENTRY,
729 "slap_sasl_match: comparison returned %d\n", rc, 0, 0 );
731 Debug( LDAP_DEBUG_TRACE,
732 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
740 * This function answers the question, "Can this ID authorize to that ID?",
741 * based on authorization rules. The rules are stored in the *searchDN, in the
742 * attribute named by *attr. If any of those rules map to the *assertDN, the
743 * authorization is approved.
745 * The DNs should not have the dn: prefix
748 slap_sasl_check_authz( Operation *op,
749 struct berval *searchDN,
750 struct berval *assertDN,
751 AttributeDescription *ad,
752 struct berval *authc )
758 LDAP_LOG( TRANSPORT, ENTRY,
759 "slap_sasl_check_authz: does %s match %s rule in %s?\n",
760 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
762 Debug( LDAP_DEBUG_TRACE,
763 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
764 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
767 rc = backend_attribute( op, NULL,
768 searchDN, ad, &vals );
769 if( rc != LDAP_SUCCESS ) goto COMPLETE;
771 /* Check if the *assertDN matches any **vals */
773 for( i=0; vals[i].bv_val != NULL; i++ ) {
774 rc = slap_sasl_match( op, &vals[i], assertDN, authc );
775 if ( rc == LDAP_SUCCESS ) goto COMPLETE;
778 rc = LDAP_INAPPROPRIATE_AUTH;
781 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
784 LDAP_LOG( TRANSPORT, RESULTS,
785 "slap_sasl_check_authz: %s check returning %s\n",
786 ad->ad_cname.bv_val, rc, 0 );
788 Debug( LDAP_DEBUG_TRACE,
789 "<==slap_sasl_check_authz: %s check returning %d\n",
790 ad->ad_cname.bv_val, rc, 0);
797 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
798 * return the LDAP DN to which it matches. The SASL regexp rules in the config
799 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
800 * search with scope=base), just return the URI (or its searchbase). Otherwise
801 * an internal search must be done, and if that search returns exactly one
802 * entry, return the DN of that one entry.
804 void slap_sasl2dn( Operation *opx,
805 struct berval *saslname, struct berval *sasldn )
808 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
810 SlapReply rs = {REP_RESULT};
811 struct berval regout = { 0, NULL };
814 LDAP_LOG( TRANSPORT, ENTRY,
815 "slap_sasl2dn: converting SASL name %s to DN.\n",
816 saslname->bv_val, 0, 0 );
818 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
819 "converting SASL name %s to a DN\n",
820 saslname->bv_val, 0,0 );
823 sasldn->bv_val = NULL;
825 cb.sc_private = sasldn;
827 /* Convert the SASL name into a minimal URI */
828 if( !slap_sasl_regexp( saslname, ®out, opx->o_tmpmemctx ) ) {
832 rc = slap_parseURI( opx, ®out, &op.o_req_dn,
833 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
835 if( regout.bv_val ) sl_free( regout.bv_val, opx->o_tmpmemctx );
836 if( rc != LDAP_SUCCESS ) {
840 /* Must do an internal search */
841 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
843 /* Massive shortcut: search scope == base */
844 switch ( op.oq_search.rs_scope ) {
845 case LDAP_SCOPE_BASE:
846 case LDAP_X_SCOPE_EXACT:
847 *sasldn = op.o_req_ndn;
848 op.o_req_ndn.bv_len = 0;
849 op.o_req_ndn.bv_val = NULL;
850 /* intentionally continue to next case */
852 case LDAP_X_SCOPE_REGEX:
853 case LDAP_X_SCOPE_SUBTREE:
854 case LDAP_X_SCOPE_CHILDREN:
855 /* correctly parsed, but illegal */
858 case LDAP_SCOPE_ONELEVEL:
859 case LDAP_SCOPE_SUBTREE:
864 /* catch unhandled cases (there shouldn't be) */
869 LDAP_LOG( TRANSPORT, DETAIL1,
870 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
871 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
873 Debug( LDAP_DEBUG_TRACE,
874 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
875 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
878 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
882 op.o_conn = opx->o_conn;
883 op.o_connid = opx->o_connid;
884 op.o_tag = LDAP_REQ_SEARCH;
885 op.o_protocol = LDAP_VERSION3;
886 op.o_ndn = opx->o_conn->c_ndn;
888 op.o_time = slap_get_time();
889 op.o_do_not_cache = 1;
890 op.o_is_auth_check = 1;
891 op.o_threadctx = opx->o_threadctx;
892 op.o_tmpmemctx = opx->o_tmpmemctx;
893 op.o_tmpmfuncs = opx->o_tmpmfuncs;
897 op.oq_search.rs_deref = LDAP_DEREF_NEVER;
898 op.oq_search.rs_slimit = 1;
899 op.oq_search.rs_attrsonly = 1;
900 op.o_req_dn = op.o_req_ndn;
902 op.o_bd->be_search( &op, &rs );
905 if( sasldn->bv_len ) {
906 opx->o_conn->c_authz_backend = op.o_bd;
908 if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
909 if( op.o_req_ndn.bv_len ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
910 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
911 if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
914 LDAP_LOG( TRANSPORT, ENTRY,
915 "slap_sasl2dn: Converted SASL name to %s\n",
916 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
918 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
919 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
926 /* Check if a bind can SASL authorize to another identity.
927 * The DNs should not have the dn: prefix
930 int slap_sasl_authorized( Operation *op,
931 struct berval *authcDN, struct berval *authzDN )
933 int rc = LDAP_INAPPROPRIATE_AUTH;
935 /* User binding as anonymous */
936 if ( authzDN == NULL ) {
942 LDAP_LOG( TRANSPORT, ENTRY,
943 "slap_sasl_authorized: can %s become %s?\n",
944 authcDN->bv_val, authzDN->bv_val, 0 );
946 Debug( LDAP_DEBUG_TRACE,
947 "==>slap_sasl_authorized: can %s become %s?\n",
948 authcDN->bv_val, authzDN->bv_val, 0 );
951 /* If person is authorizing to self, succeed */
952 if ( dn_match( authcDN, authzDN ) ) {
957 /* Allow the manager to authorize as any DN. */
958 if( op->o_conn->c_authz_backend && be_isroot( op->o_conn->c_authz_backend, authcDN )) {
963 /* Check source rules */
964 if( authz_policy & SASL_AUTHZ_TO ) {
965 rc = slap_sasl_check_authz( op, authcDN, authzDN,
966 slap_schema.si_ad_saslAuthzTo, authcDN );
967 if( rc == LDAP_SUCCESS ) {
972 /* Check destination rules */
973 if( authz_policy & SASL_AUTHZ_FROM ) {
974 rc = slap_sasl_check_authz( op, authzDN, authcDN,
975 slap_schema.si_ad_saslAuthzFrom, authcDN );
976 if( rc == LDAP_SUCCESS ) {
981 rc = LDAP_INAPPROPRIATE_AUTH;
986 LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );
988 Debug( LDAP_DEBUG_TRACE,
989 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );