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 0x00
76 #define SASL_AUTHZ_FROM 0x01
77 #define SASL_AUTHZ_TO 0x02
78 #define SASL_AUTHZ_AND 0x10
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 || strcasecmp( arg, "any" ) == 0 ) {
93 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO;
94 } else if ( strcasecmp( arg, "all" ) == 0 ) {
95 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND;
102 int slap_parse_user( struct berval *id, struct berval *user,
103 struct berval *realm, struct berval *mech )
108 assert( id->bv_val );
115 if ( u != 'u' && u != 'U' ) {
116 /* called with something other than u: */
117 return LDAP_PROTOCOL_ERROR;
121 * u[.mech[/realm]]:user
124 user->bv_val = strchr( id->bv_val, ':' );
125 if ( user->bv_val == NULL ) {
126 return LDAP_PROTOCOL_ERROR;
128 user->bv_val[ 0 ] = '\0';
130 user->bv_len = id->bv_len - ( user->bv_val - id->bv_val );
132 mech->bv_val = strchr( id->bv_val, '.' );
133 if ( mech->bv_val != NULL ) {
134 mech->bv_val[ 0 ] = '\0';
137 realm->bv_val = strchr( mech->bv_val, '/' );
139 if ( realm->bv_val ) {
140 realm->bv_val[ 0 ] = '\0';
142 mech->bv_len = realm->bv_val - mech->bv_val - 1;
143 realm->bv_len = user->bv_val - realm->bv_val - 1;
145 mech->bv_len = user->bv_val - mech->bv_val - 1;
149 realm->bv_val = NULL;
152 if ( id->bv_val[ 1 ] != '\0' ) {
153 return LDAP_PROTOCOL_ERROR;
156 if ( mech->bv_val != NULL ) {
157 assert( mech->bv_val == id->bv_val + 2 );
159 AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
163 if ( realm->bv_val ) {
164 assert( realm->bv_val >= id->bv_val + 2 );
166 AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
170 /* leave "u:" before user */
173 user->bv_val[ 0 ] = u;
174 user->bv_val[ 1 ] = ':';
179 static int slap_parseURI( Operation *op, struct berval *uri,
180 struct berval *base, struct berval *nbase,
181 int *scope, Filter **filter, struct berval *fstr )
187 assert( uri != NULL && uri->bv_val != NULL );
190 nbase->bv_val = NULL;
198 LDAP_LOG( TRANSPORT, ENTRY,
199 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
201 Debug( LDAP_DEBUG_TRACE,
202 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
205 rc = LDAP_PROTOCOL_ERROR;
206 if ( !strncasecmp( uri->bv_val, "dn", sizeof( "dn" ) - 1 ) ) {
207 bv.bv_val = uri->bv_val + sizeof( "dn" ) - 1;
209 if ( bv.bv_val[ 0 ] == '.' ) {
212 if ( !strncasecmp( bv.bv_val, "exact:", sizeof( "exact:" ) - 1 ) ) {
213 bv.bv_val += sizeof( "exact" ) - 1;
214 *scope = LDAP_X_SCOPE_EXACT;
216 } else if ( !strncasecmp( bv.bv_val, "regex:", sizeof( "regex:" ) - 1 ) ) {
217 bv.bv_val += sizeof( "regex" ) - 1;
218 *scope = LDAP_X_SCOPE_REGEX;
220 } else if ( !strncasecmp( bv.bv_val, "children:", sizeof( "chldren:" ) - 1 ) ) {
221 bv.bv_val += sizeof( "children" ) - 1;
222 *scope = LDAP_X_SCOPE_CHILDREN;
224 } else if ( !strncasecmp( bv.bv_val, "subtree:", sizeof( "subtree:" ) - 1 ) ) {
225 bv.bv_val += sizeof( "subtree" ) - 1;
226 *scope = LDAP_X_SCOPE_SUBTREE;
229 return LDAP_PROTOCOL_ERROR;
233 if ( bv.bv_val[ 0 ] != ':' ) {
234 return LDAP_PROTOCOL_ERROR;
238 bv.bv_val += strspn( bv.bv_val, " " );
239 /* jump here in case no type specification was present
240 * and uir was not an URI... HEADS-UP: assuming EXACT */
241 is_dn: bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
244 case LDAP_X_SCOPE_EXACT:
245 case LDAP_X_SCOPE_CHILDREN:
246 case LDAP_X_SCOPE_SUBTREE:
247 rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
248 if( rc != LDAP_SUCCESS ) {
253 case LDAP_X_SCOPE_REGEX:
254 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
265 } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
266 && ( uri->bv_val[ 1 ] == ':'
267 || uri->bv_val[ 1 ] == '/'
268 || uri->bv_val[ 1 ] == '.' ) )
270 Connection c = *op->o_conn;
271 char buf[ SLAP_LDAPDN_MAXLEN ];
277 if ( sizeof( buf ) <= uri->bv_len ) {
278 return LDAP_INVALID_SYNTAX;
281 id.bv_len = uri->bv_len;
283 strncpy( buf, uri->bv_val, sizeof( buf ) );
285 rc = slap_parse_user( &id, &user, &realm, &mech );
286 if ( rc != LDAP_SUCCESS ) {
291 c.c_sasl_bind_mech = mech;
293 c.c_sasl_bind_mech.bv_val = "AUTHZ";
294 c.c_sasl_bind_mech.bv_len = sizeof( "AUTHZ" ) - 1;
297 rc = slap_sasl_getdn( &c, op, user.bv_val, user.bv_len,
298 realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
300 if ( rc == LDAP_SUCCESS ) {
301 *scope = LDAP_X_SCOPE_EXACT;
307 rc = ldap_url_parse( uri->bv_val, &ludp );
308 if ( rc == LDAP_URL_ERR_BADSCHEME ) {
309 /* last chance: assume it's a(n exact) DN ... */
310 bv.bv_val = uri->bv_val;
311 *scope = LDAP_X_SCOPE_EXACT;
315 if ( rc != LDAP_URL_SUCCESS ) {
316 return LDAP_PROTOCOL_ERROR;
319 if (( ludp->lud_host && *ludp->lud_host )
320 || ludp->lud_attrs || ludp->lud_exts )
322 /* host part must be empty */
323 /* attrs and extensions parts must be empty */
324 rc = LDAP_PROTOCOL_ERROR;
329 *scope = ludp->lud_scope;
331 /* Grab the filter */
332 if ( ludp->lud_filter ) {
333 *filter = str2filter_x( op, ludp->lud_filter );
334 if ( *filter == NULL ) {
335 rc = LDAP_PROTOCOL_ERROR;
338 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
341 /* Grab the searchbase */
342 ber_str2bv( ludp->lud_dn, 0, 0, base );
343 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
346 if( rc != LDAP_SUCCESS ) {
347 if( *filter ) filter_free_x( op, *filter );
353 /* Don't free these, return them to caller */
354 ludp->lud_filter = NULL;
358 ldap_free_urldesc( ludp );
362 static int slap_sasl_rx_off(char *rep, int *off)
367 /* Precompile replace pattern. Find the $<n> placeholders */
370 for ( c = rep; *c; c++ ) {
371 if ( *c == '\\' && c[1] ) {
376 if ( n == SASLREGEX_REPLACE ) {
378 LDAP_LOG( TRANSPORT, ERR,
379 "slap_sasl_rx_off: \"%s\" has too many $n "
380 "placeholders (max %d)\n", rep, SASLREGEX_REPLACE, 0 );
382 Debug( LDAP_DEBUG_ANY,
383 "SASL replace pattern %s has too many $n "
384 "placeholders (max %d)\n",
385 rep, SASLREGEX_REPLACE, 0 );
388 return( LDAP_OTHER );
395 /* Final placeholder, after the last $n */
399 return( LDAP_SUCCESS );
402 int slap_sasl_regexp_config( const char *match, const char *replace )
407 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
408 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
410 reg = &SaslRegexp[nSaslRegexp];
412 reg->sr_match = ch_strdup( match );
413 reg->sr_replace = ch_strdup( replace );
415 /* Precompile matching pattern */
416 rc = regcomp( ®->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
419 LDAP_LOG( TRANSPORT, ERR,
420 "slap_sasl_regexp_config: \"%s\" could not be compiled.\n",
421 reg->sr_match, 0, 0 );
423 Debug( LDAP_DEBUG_ANY,
424 "SASL match pattern %s could not be compiled by regexp engine\n",
425 reg->sr_match, 0, 0 );
428 return( LDAP_OTHER );
431 rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
432 if ( rc != LDAP_SUCCESS ) return rc;
435 return( LDAP_SUCCESS );
439 /* Perform replacement on regexp matches */
440 static void slap_sasl_rx_exp(
444 const char *saslname,
448 int i, n, len, insert;
450 /* Get the total length of the final URI */
454 while( off[n] >= 0 ) {
455 /* Len of next section from replacement string (x,y,z above) */
456 len += off[n] - off[n-1] - 2;
460 /* Len of string from saslname that matched next $i (b,d above) */
461 i = rep[ off[n] + 1 ] - '0';
462 len += str[i].rm_eo - str[i].rm_so;
465 out->bv_val = sl_malloc( len + 1, ctx );
468 /* Fill in URI with replace string, replacing $i as we go */
471 while( off[n] >= 0) {
472 /* Paste in next section from replacement string (x,y,z above) */
473 len = off[n] - off[n-1] - 2;
474 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
479 /* Paste in string from saslname that matched next $i (b,d above) */
480 i = rep[ off[n] + 1 ] - '0';
481 len = str[i].rm_eo - str[i].rm_so;
482 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
488 out->bv_val[insert] = '\0';
491 /* Take the passed in SASL name and attempt to convert it into an
492 LDAP URI to find the matching LDAP entry, using the pattern matching
493 strings given in the saslregexp config file directive(s) */
495 static int slap_sasl_regexp( struct berval *in, struct berval *out, void *ctx )
497 char *saslname = in->bv_val;
499 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
502 memset( out, 0, sizeof( *out ) );
505 LDAP_LOG( TRANSPORT, ENTRY,
506 "slap_sasl_regexp: converting SASL name %s\n", saslname, 0, 0 );
508 Debug( LDAP_DEBUG_TRACE, "slap_sasl_regexp: converting SASL name %s\n",
512 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
516 /* Match the normalized SASL name to the saslregexp patterns */
517 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
518 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE,
519 sr_strings, 0) == 0 )
523 if( i >= nSaslRegexp ) return( 0 );
526 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
527 * replace pattern of the form "x$1y$2z". The returned string needs
528 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
530 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
531 sr_strings, saslname, out, ctx );
534 LDAP_LOG( TRANSPORT, ENTRY,
535 "slap_sasl_regexp: converted SASL name to %s\n",
536 out->bv_len ? out->bv_val : "", 0, 0 );
538 Debug( LDAP_DEBUG_TRACE,
539 "slap_sasl_regexp: converted SASL name to %s\n",
540 out->bv_len ? out->bv_val : "", 0, 0 );
546 /* This callback actually does some work...*/
547 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
549 struct berval *ndn = o->o_callback->sc_private;
551 if (rs->sr_type != REP_SEARCH) return 0;
553 /* We only want to be called once */
555 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
559 LDAP_LOG( TRANSPORT, DETAIL1,
560 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
562 Debug( LDAP_DEBUG_TRACE,
563 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
568 ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
573 typedef struct smatch_info {
578 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
580 smatch_info *sm = o->o_callback->sc_private;
582 if (rs->sr_type != REP_SEARCH) return 0;
584 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
586 return -1; /* short-circuit the search */
593 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
594 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
595 * the rule must be used as an internal search for entries. If that search
596 * returns the *assertDN entry, the match is successful.
598 * The assertDN should not have the dn: prefix
602 int slap_sasl_match( Operation *opx, struct berval *rule,
603 struct berval *assertDN, struct berval *authc )
608 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
610 SlapReply rs = {REP_RESULT};
613 LDAP_LOG( TRANSPORT, ENTRY,
614 "slap_sasl_match: comparing DN %s to rule %s\n",
615 assertDN->bv_val, rule->bv_val,0 );
617 Debug( LDAP_DEBUG_TRACE,
618 "===>slap_sasl_match: comparing DN %s to rule %s\n",
619 assertDN->bv_val, rule->bv_val, 0 );
622 rc = slap_parseURI( opx, rule, &op.o_req_dn,
623 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
625 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
627 /* Massive shortcut: search scope == base */
628 switch ( op.oq_search.rs_scope ) {
629 case LDAP_SCOPE_BASE:
630 case LDAP_X_SCOPE_EXACT:
632 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
635 rc = LDAP_INAPPROPRIATE_AUTH;
639 case LDAP_X_SCOPE_CHILDREN:
640 case LDAP_X_SCOPE_SUBTREE:
642 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
644 rc = LDAP_INAPPROPRIATE_AUTH;
646 if ( d == 0 && op.oq_search.rs_scope == LDAP_X_SCOPE_SUBTREE ) {
649 } else if ( d > 0 ) {
652 bv.bv_len = op.o_req_ndn.bv_len;
653 bv.bv_val = assertDN->bv_val + d;
655 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
662 case LDAP_X_SCOPE_REGEX:
663 rc = regcomp(®, op.o_req_ndn.bv_val,
664 REG_EXTENDED|REG_ICASE|REG_NOSUB);
666 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
672 rc = LDAP_INAPPROPRIATE_AUTH;
680 /* Must run an internal search. */
681 if ( op.oq_search.rs_filter == NULL ) {
682 rc = LDAP_FILTER_ERROR;
687 LDAP_LOG( TRANSPORT, DETAIL1,
688 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
689 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
691 Debug( LDAP_DEBUG_TRACE,
692 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
693 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
696 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
697 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
698 rc = LDAP_INAPPROPRIATE_AUTH;
706 op.o_tag = LDAP_REQ_SEARCH;
707 op.o_protocol = LDAP_VERSION3;
710 op.o_time = slap_get_time();
711 op.o_do_not_cache = 1;
712 op.o_is_auth_check = 1;
713 op.o_threadctx = opx->o_threadctx;
714 op.o_tmpmemctx = opx->o_tmpmemctx;
715 op.o_tmpmfuncs = opx->o_tmpmfuncs;
719 op.o_conn = opx->o_conn;
720 op.o_connid = opx->o_connid;
721 op.o_req_dn = op.o_req_ndn;
723 op.o_bd->be_search( &op, &rs );
728 rc = LDAP_INAPPROPRIATE_AUTH;
732 if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
733 if( op.o_req_ndn.bv_len ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
734 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
735 if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
738 LDAP_LOG( TRANSPORT, ENTRY,
739 "slap_sasl_match: comparison returned %d\n", rc, 0, 0 );
741 Debug( LDAP_DEBUG_TRACE,
742 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
750 * This function answers the question, "Can this ID authorize to that ID?",
751 * based on authorization rules. The rules are stored in the *searchDN, in the
752 * attribute named by *attr. If any of those rules map to the *assertDN, the
753 * authorization is approved.
755 * The DNs should not have the dn: prefix
758 slap_sasl_check_authz( Operation *op,
759 struct berval *searchDN,
760 struct berval *assertDN,
761 AttributeDescription *ad,
762 struct berval *authc )
768 LDAP_LOG( TRANSPORT, ENTRY,
769 "slap_sasl_check_authz: does %s match %s rule in %s?\n",
770 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
772 Debug( LDAP_DEBUG_TRACE,
773 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
774 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
777 rc = backend_attribute( op, NULL,
778 searchDN, ad, &vals );
779 if( rc != LDAP_SUCCESS ) goto COMPLETE;
781 /* Check if the *assertDN matches any **vals */
783 for( i=0; vals[i].bv_val != NULL; i++ ) {
784 rc = slap_sasl_match( op, &vals[i], assertDN, authc );
785 if ( rc == LDAP_SUCCESS ) goto COMPLETE;
788 rc = LDAP_INAPPROPRIATE_AUTH;
791 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
794 LDAP_LOG( TRANSPORT, RESULTS,
795 "slap_sasl_check_authz: %s check returning %s\n",
796 ad->ad_cname.bv_val, rc, 0 );
798 Debug( LDAP_DEBUG_TRACE,
799 "<==slap_sasl_check_authz: %s check returning %d\n",
800 ad->ad_cname.bv_val, rc, 0);
807 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
808 * return the LDAP DN to which it matches. The SASL regexp rules in the config
809 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
810 * search with scope=base), just return the URI (or its searchbase). Otherwise
811 * an internal search must be done, and if that search returns exactly one
812 * entry, return the DN of that one entry.
814 void slap_sasl2dn( Operation *opx,
815 struct berval *saslname, struct berval *sasldn )
818 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
820 SlapReply rs = {REP_RESULT};
821 struct berval regout = { 0, NULL };
824 LDAP_LOG( TRANSPORT, ENTRY,
825 "slap_sasl2dn: converting SASL name %s to DN.\n",
826 saslname->bv_val, 0, 0 );
828 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
829 "converting SASL name %s to a DN\n",
830 saslname->bv_val, 0,0 );
833 sasldn->bv_val = NULL;
835 cb.sc_private = sasldn;
837 /* Convert the SASL name into a minimal URI */
838 if( !slap_sasl_regexp( saslname, ®out, opx->o_tmpmemctx ) ) {
842 rc = slap_parseURI( opx, ®out, &op.o_req_dn,
843 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
845 if( regout.bv_val ) sl_free( regout.bv_val, opx->o_tmpmemctx );
846 if( rc != LDAP_SUCCESS ) {
850 /* Must do an internal search */
851 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
853 /* Massive shortcut: search scope == base */
854 switch ( op.oq_search.rs_scope ) {
855 case LDAP_SCOPE_BASE:
856 case LDAP_X_SCOPE_EXACT:
857 *sasldn = op.o_req_ndn;
858 op.o_req_ndn.bv_len = 0;
859 op.o_req_ndn.bv_val = NULL;
860 /* intentionally continue to next case */
862 case LDAP_X_SCOPE_REGEX:
863 case LDAP_X_SCOPE_SUBTREE:
864 case LDAP_X_SCOPE_CHILDREN:
865 /* correctly parsed, but illegal */
868 case LDAP_SCOPE_ONELEVEL:
869 case LDAP_SCOPE_SUBTREE:
870 case LDAP_SCOPE_SUBORDINATE:
875 /* catch unhandled cases (there shouldn't be) */
880 LDAP_LOG( TRANSPORT, DETAIL1,
881 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
882 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
884 Debug( LDAP_DEBUG_TRACE,
885 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
886 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
889 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
893 op.o_conn = opx->o_conn;
894 op.o_connid = opx->o_connid;
895 op.o_tag = LDAP_REQ_SEARCH;
896 op.o_protocol = LDAP_VERSION3;
897 op.o_ndn = opx->o_conn->c_ndn;
899 op.o_time = slap_get_time();
900 op.o_do_not_cache = 1;
901 op.o_is_auth_check = 1;
902 op.o_threadctx = opx->o_threadctx;
903 op.o_tmpmemctx = opx->o_tmpmemctx;
904 op.o_tmpmfuncs = opx->o_tmpmfuncs;
908 op.oq_search.rs_deref = LDAP_DEREF_NEVER;
909 op.oq_search.rs_slimit = 1;
910 op.oq_search.rs_attrsonly = 1;
911 op.o_req_dn = op.o_req_ndn;
913 op.o_bd->be_search( &op, &rs );
916 if( sasldn->bv_len ) {
917 opx->o_conn->c_authz_backend = op.o_bd;
919 if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
920 if( op.o_req_ndn.bv_len ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
921 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
922 if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
925 LDAP_LOG( TRANSPORT, ENTRY,
926 "slap_sasl2dn: Converted SASL name to %s\n",
927 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
929 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
930 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
937 /* Check if a bind can SASL authorize to another identity.
938 * The DNs should not have the dn: prefix
941 int slap_sasl_authorized( Operation *op,
942 struct berval *authcDN, struct berval *authzDN )
944 int rc = LDAP_INAPPROPRIATE_AUTH;
946 /* User binding as anonymous */
947 if ( authzDN == NULL ) {
953 LDAP_LOG( TRANSPORT, ENTRY,
954 "slap_sasl_authorized: can %s become %s?\n",
955 authcDN->bv_val, authzDN->bv_val, 0 );
957 Debug( LDAP_DEBUG_TRACE,
958 "==>slap_sasl_authorized: can %s become %s?\n",
959 authcDN->bv_val, authzDN->bv_val, 0 );
962 /* If person is authorizing to self, succeed */
963 if ( dn_match( authcDN, authzDN ) ) {
968 /* Allow the manager to authorize as any DN. */
969 if( op->o_conn->c_authz_backend && be_isroot( op->o_conn->c_authz_backend, authcDN )) {
974 /* Check source rules */
975 if( authz_policy & SASL_AUTHZ_TO ) {
976 rc = slap_sasl_check_authz( op, authcDN, authzDN,
977 slap_schema.si_ad_saslAuthzTo, authcDN );
978 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
983 /* Check destination rules */
984 if( authz_policy & SASL_AUTHZ_FROM ) {
985 rc = slap_sasl_check_authz( op, authzDN, authcDN,
986 slap_schema.si_ad_saslAuthzFrom, authcDN );
987 if( rc == LDAP_SUCCESS ) {
992 rc = LDAP_INAPPROPRIATE_AUTH;
997 LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );
999 Debug( LDAP_DEBUG_TRACE,
1000 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );