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 ];
272 struct berval id = { uri->bv_len, (char *)buf },
277 if ( sizeof( buf ) <= uri->bv_len ) {
278 return LDAP_INVALID_SYNTAX;
281 strncpy( buf, uri->bv_val, sizeof( buf ) );
283 rc = slap_parse_user( &id, &user, &realm, &mech );
284 if ( rc != LDAP_SUCCESS ) {
289 c.c_sasl_bind_mech = mech;
291 c.c_sasl_bind_mech.bv_val = "AUTHZ";
292 c.c_sasl_bind_mech.bv_len = sizeof( "AUTHZ" ) - 1;
295 rc = slap_sasl_getdn( &c, op, user.bv_val, user.bv_len,
296 realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
298 if ( rc == LDAP_SUCCESS ) {
299 *scope = LDAP_X_SCOPE_EXACT;
305 rc = ldap_url_parse( uri->bv_val, &ludp );
306 if ( rc == LDAP_URL_ERR_BADSCHEME ) {
307 /* last chance: assume it's a(n exact) DN ... */
308 bv.bv_val = uri->bv_val;
309 *scope = LDAP_X_SCOPE_EXACT;
313 if ( rc != LDAP_URL_SUCCESS ) {
314 return LDAP_PROTOCOL_ERROR;
317 if (( ludp->lud_host && *ludp->lud_host )
318 || ludp->lud_attrs || ludp->lud_exts )
320 /* host part must be empty */
321 /* attrs and extensions parts must be empty */
322 rc = LDAP_PROTOCOL_ERROR;
327 *scope = ludp->lud_scope;
329 /* Grab the filter */
330 if ( ludp->lud_filter ) {
331 *filter = str2filter_x( op, ludp->lud_filter );
332 if ( *filter == NULL ) {
333 rc = LDAP_PROTOCOL_ERROR;
336 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
339 /* Grab the searchbase */
340 ber_str2bv( ludp->lud_dn, 0, 0, base );
341 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
344 if( rc != LDAP_SUCCESS ) {
345 if( *filter ) filter_free_x( op, *filter );
351 /* Don't free these, return them to caller */
352 ludp->lud_filter = NULL;
356 ldap_free_urldesc( ludp );
360 static int slap_sasl_rx_off(char *rep, int *off)
365 /* Precompile replace pattern. Find the $<n> placeholders */
368 for ( c = rep; *c; c++ ) {
369 if ( *c == '\\' && c[1] ) {
374 if ( n == SASLREGEX_REPLACE ) {
376 LDAP_LOG( TRANSPORT, ERR,
377 "slap_sasl_rx_off: \"%s\" has too many $n "
378 "placeholders (max %d)\n", rep, SASLREGEX_REPLACE, 0 );
380 Debug( LDAP_DEBUG_ANY,
381 "SASL replace pattern %s has too many $n "
382 "placeholders (max %d)\n",
383 rep, SASLREGEX_REPLACE, 0 );
386 return( LDAP_OTHER );
393 /* Final placeholder, after the last $n */
397 return( LDAP_SUCCESS );
400 int slap_sasl_regexp_config( const char *match, const char *replace )
405 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
406 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
408 reg = &SaslRegexp[nSaslRegexp];
410 reg->sr_match = ch_strdup( match );
411 reg->sr_replace = ch_strdup( replace );
413 /* Precompile matching pattern */
414 rc = regcomp( ®->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
417 LDAP_LOG( TRANSPORT, ERR,
418 "slap_sasl_regexp_config: \"%s\" could not be compiled.\n",
419 reg->sr_match, 0, 0 );
421 Debug( LDAP_DEBUG_ANY,
422 "SASL match pattern %s could not be compiled by regexp engine\n",
423 reg->sr_match, 0, 0 );
426 return( LDAP_OTHER );
429 rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
430 if ( rc != LDAP_SUCCESS ) return rc;
433 return( LDAP_SUCCESS );
437 /* Perform replacement on regexp matches */
438 static void slap_sasl_rx_exp(
442 const char *saslname,
446 int i, n, len, insert;
448 /* Get the total length of the final URI */
452 while( off[n] >= 0 ) {
453 /* Len of next section from replacement string (x,y,z above) */
454 len += off[n] - off[n-1] - 2;
458 /* Len of string from saslname that matched next $i (b,d above) */
459 i = rep[ off[n] + 1 ] - '0';
460 len += str[i].rm_eo - str[i].rm_so;
463 out->bv_val = sl_malloc( len + 1, ctx );
466 /* Fill in URI with replace string, replacing $i as we go */
469 while( off[n] >= 0) {
470 /* Paste in next section from replacement string (x,y,z above) */
471 len = off[n] - off[n-1] - 2;
472 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
477 /* Paste in string from saslname that matched next $i (b,d above) */
478 i = rep[ off[n] + 1 ] - '0';
479 len = str[i].rm_eo - str[i].rm_so;
480 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
486 out->bv_val[insert] = '\0';
489 /* Take the passed in SASL name and attempt to convert it into an
490 LDAP URI to find the matching LDAP entry, using the pattern matching
491 strings given in the saslregexp config file directive(s) */
493 static int slap_sasl_regexp( struct berval *in, struct berval *out,
494 int flags, void *ctx )
496 char *saslname = in->bv_val;
498 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
501 memset( out, 0, sizeof( *out ) );
504 LDAP_LOG( TRANSPORT, ENTRY,
505 "slap_sasl_regexp: converting SASL name %s\n", saslname, 0, 0 );
507 Debug( LDAP_DEBUG_TRACE, "slap_sasl_regexp: converting SASL name %s\n",
511 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
515 /* Match the normalized SASL name to the saslregexp patterns */
516 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
517 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE,
518 sr_strings, 0) == 0 )
522 if( i >= nSaslRegexp ) return( 0 );
525 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
526 * replace pattern of the form "x$1y$2z". The returned string needs
527 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
529 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
530 sr_strings, saslname, out, ctx );
533 LDAP_LOG( TRANSPORT, ENTRY,
534 "slap_sasl_regexp: converted SASL name to %s\n",
535 out->bv_len ? out->bv_val : "", 0, 0 );
537 Debug( LDAP_DEBUG_TRACE,
538 "slap_sasl_regexp: converted SASL name to %s\n",
539 out->bv_len ? out->bv_val : "", 0, 0 );
545 /* This callback actually does some work...*/
546 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
548 struct berval *ndn = o->o_callback->sc_private;
550 if (rs->sr_type != REP_SEARCH) return 0;
552 /* We only want to be called once */
554 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
558 LDAP_LOG( TRANSPORT, DETAIL1,
559 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
561 Debug( LDAP_DEBUG_TRACE,
562 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
567 ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
572 typedef struct smatch_info {
577 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
579 smatch_info *sm = o->o_callback->sc_private;
581 if (rs->sr_type != REP_SEARCH) return 0;
583 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
585 return -1; /* short-circuit the search */
592 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
593 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
594 * the rule must be used as an internal search for entries. If that search
595 * returns the *assertDN entry, the match is successful.
597 * The assertDN should not have the dn: prefix
601 int slap_sasl_match( Operation *opx, struct berval *rule,
602 struct berval *assertDN, struct berval *authc )
607 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
609 SlapReply rs = {REP_RESULT};
612 LDAP_LOG( TRANSPORT, ENTRY,
613 "slap_sasl_match: comparing DN %s to rule %s\n",
614 assertDN->bv_val, rule->bv_val,0 );
616 Debug( LDAP_DEBUG_TRACE,
617 "===>slap_sasl_match: comparing DN %s to rule %s\n",
618 assertDN->bv_val, rule->bv_val, 0 );
621 rc = slap_parseURI( opx, rule, &op.o_req_dn,
622 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
624 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
626 /* Massive shortcut: search scope == base */
627 switch ( op.oq_search.rs_scope ) {
628 case LDAP_SCOPE_BASE:
629 case LDAP_X_SCOPE_EXACT:
631 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
634 rc = LDAP_INAPPROPRIATE_AUTH;
638 case LDAP_X_SCOPE_CHILDREN:
639 case LDAP_X_SCOPE_SUBTREE:
641 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
643 rc = LDAP_INAPPROPRIATE_AUTH;
645 if ( d == 0 && op.oq_search.rs_scope == LDAP_X_SCOPE_SUBTREE ) {
648 } else if ( d > 0 ) {
649 struct berval bv = { op.o_req_ndn.bv_len, assertDN->bv_val + d };
651 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
658 case LDAP_X_SCOPE_REGEX:
659 rc = regcomp(®, op.o_req_ndn.bv_val,
660 REG_EXTENDED|REG_ICASE|REG_NOSUB);
662 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
668 rc = LDAP_INAPPROPRIATE_AUTH;
676 /* Must run an internal search. */
677 if ( op.oq_search.rs_filter == NULL ) {
678 rc = LDAP_FILTER_ERROR;
683 LDAP_LOG( TRANSPORT, DETAIL1,
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 );
687 Debug( LDAP_DEBUG_TRACE,
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 );
692 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
693 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
694 rc = LDAP_INAPPROPRIATE_AUTH;
702 op.o_tag = LDAP_REQ_SEARCH;
703 op.o_protocol = LDAP_VERSION3;
706 op.o_time = slap_get_time();
707 op.o_do_not_cache = 1;
708 op.o_is_auth_check = 1;
709 op.o_threadctx = opx->o_threadctx;
710 op.o_tmpmemctx = opx->o_tmpmemctx;
711 op.o_tmpmfuncs = opx->o_tmpmfuncs;
715 op.o_conn = opx->o_conn;
716 op.o_connid = opx->o_connid;
717 op.o_req_dn = op.o_req_ndn;
719 op.o_bd->be_search( &op, &rs );
724 rc = LDAP_INAPPROPRIATE_AUTH;
728 if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
729 if( op.o_req_ndn.bv_len ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
730 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
731 if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
734 LDAP_LOG( TRANSPORT, ENTRY,
735 "slap_sasl_match: comparison returned %d\n", rc, 0, 0 );
737 Debug( LDAP_DEBUG_TRACE,
738 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
746 * This function answers the question, "Can this ID authorize to that ID?",
747 * based on authorization rules. The rules are stored in the *searchDN, in the
748 * attribute named by *attr. If any of those rules map to the *assertDN, the
749 * authorization is approved.
751 * The DNs should not have the dn: prefix
754 slap_sasl_check_authz( Operation *op,
755 struct berval *searchDN,
756 struct berval *assertDN,
757 AttributeDescription *ad,
758 struct berval *authc )
764 LDAP_LOG( TRANSPORT, ENTRY,
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);
768 Debug( LDAP_DEBUG_TRACE,
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);
773 rc = backend_attribute( op, NULL,
774 searchDN, ad, &vals );
775 if( rc != LDAP_SUCCESS ) goto COMPLETE;
777 /* Check if the *assertDN matches any **vals */
779 for( i=0; vals[i].bv_val != NULL; i++ ) {
780 rc = slap_sasl_match( op, &vals[i], assertDN, authc );
781 if ( rc == LDAP_SUCCESS ) goto COMPLETE;
784 rc = LDAP_INAPPROPRIATE_AUTH;
787 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
790 LDAP_LOG( TRANSPORT, RESULTS,
791 "slap_sasl_check_authz: %s check returning %s\n",
792 ad->ad_cname.bv_val, rc, 0 );
794 Debug( LDAP_DEBUG_TRACE,
795 "<==slap_sasl_check_authz: %s check returning %d\n",
796 ad->ad_cname.bv_val, rc, 0);
803 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
804 * return the LDAP DN to which it matches. The SASL regexp rules in the config
805 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
806 * search with scope=base), just return the URI (or its searchbase). Otherwise
807 * an internal search must be done, and if that search returns exactly one
808 * entry, return the DN of that one entry.
810 void slap_sasl2dn( Operation *opx,
811 struct berval *saslname, struct berval *sasldn, int flags )
814 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
816 SlapReply rs = {REP_RESULT};
817 struct berval regout = { 0, NULL };
820 LDAP_LOG( TRANSPORT, ENTRY,
821 "slap_sasl2dn: converting SASL name %s to DN.\n",
822 saslname->bv_val, 0, 0 );
824 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
825 "converting SASL name %s to a DN\n",
826 saslname->bv_val, 0,0 );
829 sasldn->bv_val = NULL;
831 cb.sc_private = sasldn;
833 /* Convert the SASL name into a minimal URI */
834 if( !slap_sasl_regexp( saslname, ®out, flags, opx->o_tmpmemctx ) ) {
838 rc = slap_parseURI( opx, ®out, &op.o_req_dn,
839 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
841 if( regout.bv_val ) sl_free( regout.bv_val, opx->o_tmpmemctx );
842 if( rc != LDAP_SUCCESS ) {
846 /* Must do an internal search */
847 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
849 /* Massive shortcut: search scope == base */
850 switch ( op.oq_search.rs_scope ) {
851 case LDAP_SCOPE_BASE:
852 case LDAP_X_SCOPE_EXACT:
853 *sasldn = op.o_req_ndn;
854 op.o_req_ndn.bv_len = 0;
855 op.o_req_ndn.bv_val = NULL;
856 /* intentionally continue to next case */
858 case LDAP_X_SCOPE_REGEX:
859 case LDAP_X_SCOPE_SUBTREE:
860 case LDAP_X_SCOPE_CHILDREN:
861 /* correctly parsed, but illegal */
864 case LDAP_SCOPE_ONELEVEL:
865 case LDAP_SCOPE_SUBTREE:
870 /* catch unhandled cases (there shouldn't be) */
875 LDAP_LOG( TRANSPORT, DETAIL1,
876 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
877 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
879 Debug( LDAP_DEBUG_TRACE,
880 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
881 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
884 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
888 op.o_conn = opx->o_conn;
889 op.o_connid = opx->o_connid;
890 op.o_tag = LDAP_REQ_SEARCH;
891 op.o_protocol = LDAP_VERSION3;
892 op.o_ndn = opx->o_conn->c_ndn;
894 op.o_time = slap_get_time();
895 op.o_do_not_cache = 1;
896 op.o_is_auth_check = 1;
897 op.o_threadctx = opx->o_threadctx;
898 op.o_tmpmemctx = opx->o_tmpmemctx;
899 op.o_tmpmfuncs = opx->o_tmpmfuncs;
903 op.oq_search.rs_deref = LDAP_DEREF_NEVER;
904 op.oq_search.rs_slimit = 1;
905 op.oq_search.rs_attrsonly = 1;
906 op.o_req_dn = op.o_req_ndn;
908 op.o_bd->be_search( &op, &rs );
911 if( sasldn->bv_len ) {
912 opx->o_conn->c_authz_backend = op.o_bd;
914 if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
915 if( op.o_req_ndn.bv_len ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
916 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
917 if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
920 LDAP_LOG( TRANSPORT, ENTRY,
921 "slap_sasl2dn: Converted SASL name to %s\n",
922 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
924 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
925 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
932 /* Check if a bind can SASL authorize to another identity.
933 * The DNs should not have the dn: prefix
936 int slap_sasl_authorized( Operation *op,
937 struct berval *authcDN, struct berval *authzDN )
939 int rc = LDAP_INAPPROPRIATE_AUTH;
941 /* User binding as anonymous */
942 if ( authzDN == NULL ) {
948 LDAP_LOG( TRANSPORT, ENTRY,
949 "slap_sasl_authorized: can %s become %s?\n",
950 authcDN->bv_val, authzDN->bv_val, 0 );
952 Debug( LDAP_DEBUG_TRACE,
953 "==>slap_sasl_authorized: can %s become %s?\n",
954 authcDN->bv_val, authzDN->bv_val, 0 );
957 /* If person is authorizing to self, succeed */
958 if ( dn_match( authcDN, authzDN ) ) {
963 /* Allow the manager to authorize as any DN. */
964 if( op->o_conn->c_authz_backend && be_isroot( op->o_conn->c_authz_backend, authcDN )) {
969 /* Check source rules */
970 if( authz_policy & SASL_AUTHZ_TO ) {
971 rc = slap_sasl_check_authz( op, authcDN, authzDN,
972 slap_schema.si_ad_saslAuthzTo, authcDN );
973 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
978 /* Check destination rules */
979 if( authz_policy & SASL_AUTHZ_FROM ) {
980 rc = slap_sasl_check_authz( op, authzDN, authcDN,
981 slap_schema.si_ad_saslAuthzFrom, authcDN );
982 if( rc == LDAP_SUCCESS ) {
987 rc = LDAP_INAPPROPRIATE_AUTH;
992 LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );
994 Debug( LDAP_DEBUG_TRACE,
995 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );