2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1998-2004 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,
496 int flags, void *ctx )
498 char *saslname = in->bv_val;
500 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
503 memset( out, 0, sizeof( *out ) );
506 LDAP_LOG( TRANSPORT, ENTRY,
507 "slap_sasl_regexp: converting SASL name %s\n", saslname, 0, 0 );
509 Debug( LDAP_DEBUG_TRACE, "slap_sasl_regexp: converting SASL name %s\n",
513 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
517 /* Match the normalized SASL name to the saslregexp patterns */
518 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
519 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE,
520 sr_strings, 0) == 0 )
524 if( i >= nSaslRegexp ) return( 0 );
527 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
528 * replace pattern of the form "x$1y$2z". The returned string needs
529 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
531 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
532 sr_strings, saslname, out, ctx );
535 LDAP_LOG( TRANSPORT, ENTRY,
536 "slap_sasl_regexp: converted SASL name to %s\n",
537 out->bv_len ? out->bv_val : "", 0, 0 );
539 Debug( LDAP_DEBUG_TRACE,
540 "slap_sasl_regexp: converted SASL name to %s\n",
541 out->bv_len ? out->bv_val : "", 0, 0 );
547 /* This callback actually does some work...*/
548 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
550 struct berval *ndn = o->o_callback->sc_private;
552 if (rs->sr_type != REP_SEARCH) return 0;
554 /* We only want to be called once */
556 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
560 LDAP_LOG( TRANSPORT, DETAIL1,
561 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
563 Debug( LDAP_DEBUG_TRACE,
564 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
569 ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
574 typedef struct smatch_info {
579 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
581 smatch_info *sm = o->o_callback->sc_private;
583 if (rs->sr_type != REP_SEARCH) return 0;
585 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
587 return -1; /* short-circuit the search */
594 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
595 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
596 * the rule must be used as an internal search for entries. If that search
597 * returns the *assertDN entry, the match is successful.
599 * The assertDN should not have the dn: prefix
603 int slap_sasl_match( Operation *opx, struct berval *rule,
604 struct berval *assertDN, struct berval *authc )
609 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
611 SlapReply rs = {REP_RESULT};
614 LDAP_LOG( TRANSPORT, ENTRY,
615 "slap_sasl_match: comparing DN %s to rule %s\n",
616 assertDN->bv_val, rule->bv_val,0 );
618 Debug( LDAP_DEBUG_TRACE,
619 "===>slap_sasl_match: comparing DN %s to rule %s\n",
620 assertDN->bv_val, rule->bv_val, 0 );
623 rc = slap_parseURI( opx, rule, &op.o_req_dn,
624 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
626 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
628 /* Massive shortcut: search scope == base */
629 switch ( op.oq_search.rs_scope ) {
630 case LDAP_SCOPE_BASE:
631 case LDAP_X_SCOPE_EXACT:
633 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
636 rc = LDAP_INAPPROPRIATE_AUTH;
640 case LDAP_X_SCOPE_CHILDREN:
641 case LDAP_X_SCOPE_SUBTREE:
643 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
645 rc = LDAP_INAPPROPRIATE_AUTH;
647 if ( d == 0 && op.oq_search.rs_scope == LDAP_X_SCOPE_SUBTREE ) {
650 } else if ( d > 0 ) {
653 bv.bv_len = op.o_req_ndn.bv_len;
654 bv.bv_val = assertDN->bv_val + d;
656 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
663 case LDAP_X_SCOPE_REGEX:
664 rc = regcomp(®, op.o_req_ndn.bv_val,
665 REG_EXTENDED|REG_ICASE|REG_NOSUB);
667 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
673 rc = LDAP_INAPPROPRIATE_AUTH;
681 /* Must run an internal search. */
682 if ( op.oq_search.rs_filter == NULL ) {
683 rc = LDAP_FILTER_ERROR;
688 LDAP_LOG( TRANSPORT, DETAIL1,
689 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
690 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
692 Debug( LDAP_DEBUG_TRACE,
693 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
694 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
697 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
698 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
699 rc = LDAP_INAPPROPRIATE_AUTH;
707 op.o_tag = LDAP_REQ_SEARCH;
708 op.o_protocol = LDAP_VERSION3;
711 op.o_time = slap_get_time();
712 op.o_do_not_cache = 1;
713 op.o_is_auth_check = 1;
714 op.o_threadctx = opx->o_threadctx;
715 op.o_tmpmemctx = opx->o_tmpmemctx;
716 op.o_tmpmfuncs = opx->o_tmpmfuncs;
720 op.o_conn = opx->o_conn;
721 op.o_connid = opx->o_connid;
722 op.o_req_dn = op.o_req_ndn;
724 op.o_bd->be_search( &op, &rs );
729 rc = LDAP_INAPPROPRIATE_AUTH;
733 if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
734 if( op.o_req_ndn.bv_len ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
735 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
736 if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
739 LDAP_LOG( TRANSPORT, ENTRY,
740 "slap_sasl_match: comparison returned %d\n", rc, 0, 0 );
742 Debug( LDAP_DEBUG_TRACE,
743 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
751 * This function answers the question, "Can this ID authorize to that ID?",
752 * based on authorization rules. The rules are stored in the *searchDN, in the
753 * attribute named by *attr. If any of those rules map to the *assertDN, the
754 * authorization is approved.
756 * The DNs should not have the dn: prefix
759 slap_sasl_check_authz( Operation *op,
760 struct berval *searchDN,
761 struct berval *assertDN,
762 AttributeDescription *ad,
763 struct berval *authc )
769 LDAP_LOG( TRANSPORT, ENTRY,
770 "slap_sasl_check_authz: does %s match %s rule in %s?\n",
771 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
773 Debug( LDAP_DEBUG_TRACE,
774 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
775 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
778 rc = backend_attribute( op, NULL,
779 searchDN, ad, &vals );
780 if( rc != LDAP_SUCCESS ) goto COMPLETE;
782 /* Check if the *assertDN matches any **vals */
784 for( i=0; vals[i].bv_val != NULL; i++ ) {
785 rc = slap_sasl_match( op, &vals[i], assertDN, authc );
786 if ( rc == LDAP_SUCCESS ) goto COMPLETE;
789 rc = LDAP_INAPPROPRIATE_AUTH;
792 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
795 LDAP_LOG( TRANSPORT, RESULTS,
796 "slap_sasl_check_authz: %s check returning %s\n",
797 ad->ad_cname.bv_val, rc, 0 );
799 Debug( LDAP_DEBUG_TRACE,
800 "<==slap_sasl_check_authz: %s check returning %d\n",
801 ad->ad_cname.bv_val, rc, 0);
808 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
809 * return the LDAP DN to which it matches. The SASL regexp rules in the config
810 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
811 * search with scope=base), just return the URI (or its searchbase). Otherwise
812 * an internal search must be done, and if that search returns exactly one
813 * entry, return the DN of that one entry.
815 void slap_sasl2dn( Operation *opx,
816 struct berval *saslname, struct berval *sasldn, int flags )
819 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
821 SlapReply rs = {REP_RESULT};
822 struct berval regout = { 0, NULL };
825 LDAP_LOG( TRANSPORT, ENTRY,
826 "slap_sasl2dn: converting SASL name %s to DN.\n",
827 saslname->bv_val, 0, 0 );
829 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
830 "converting SASL name %s to a DN\n",
831 saslname->bv_val, 0,0 );
834 sasldn->bv_val = NULL;
836 cb.sc_private = sasldn;
838 /* Convert the SASL name into a minimal URI */
839 if( !slap_sasl_regexp( saslname, ®out, flags, opx->o_tmpmemctx ) ) {
843 rc = slap_parseURI( opx, ®out, &op.o_req_dn,
844 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
846 if( regout.bv_val ) sl_free( regout.bv_val, opx->o_tmpmemctx );
847 if( rc != LDAP_SUCCESS ) {
851 /* Must do an internal search */
852 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
854 /* Massive shortcut: search scope == base */
855 switch ( op.oq_search.rs_scope ) {
856 case LDAP_SCOPE_BASE:
857 case LDAP_X_SCOPE_EXACT:
858 *sasldn = op.o_req_ndn;
859 op.o_req_ndn.bv_len = 0;
860 op.o_req_ndn.bv_val = NULL;
861 /* intentionally continue to next case */
863 case LDAP_X_SCOPE_REGEX:
864 case LDAP_X_SCOPE_SUBTREE:
865 case LDAP_X_SCOPE_CHILDREN:
866 /* correctly parsed, but illegal */
869 case LDAP_SCOPE_ONELEVEL:
870 case LDAP_SCOPE_SUBTREE:
871 case LDAP_SCOPE_SUBORDINATE:
876 /* catch unhandled cases (there shouldn't be) */
881 LDAP_LOG( TRANSPORT, DETAIL1,
882 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
883 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
885 Debug( LDAP_DEBUG_TRACE,
886 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
887 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
890 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
894 op.o_conn = opx->o_conn;
895 op.o_connid = opx->o_connid;
896 op.o_tag = LDAP_REQ_SEARCH;
897 op.o_protocol = LDAP_VERSION3;
898 op.o_ndn = opx->o_conn->c_ndn;
900 op.o_time = slap_get_time();
901 op.o_do_not_cache = 1;
902 op.o_is_auth_check = 1;
903 op.o_threadctx = opx->o_threadctx;
904 op.o_tmpmemctx = opx->o_tmpmemctx;
905 op.o_tmpmfuncs = opx->o_tmpmfuncs;
909 op.oq_search.rs_deref = LDAP_DEREF_NEVER;
910 op.oq_search.rs_slimit = 1;
911 op.oq_search.rs_attrsonly = 1;
912 op.o_req_dn = op.o_req_ndn;
914 op.o_bd->be_search( &op, &rs );
917 if( sasldn->bv_len ) {
918 opx->o_conn->c_authz_backend = op.o_bd;
920 if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
921 if( op.o_req_ndn.bv_len ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
922 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
923 if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
926 LDAP_LOG( TRANSPORT, ENTRY,
927 "slap_sasl2dn: Converted SASL name to %s\n",
928 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
930 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
931 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
938 /* Check if a bind can SASL authorize to another identity.
939 * The DNs should not have the dn: prefix
942 int slap_sasl_authorized( Operation *op,
943 struct berval *authcDN, struct berval *authzDN )
945 int rc = LDAP_INAPPROPRIATE_AUTH;
947 /* User binding as anonymous */
948 if ( authzDN == NULL ) {
954 LDAP_LOG( TRANSPORT, ENTRY,
955 "slap_sasl_authorized: can %s become %s?\n",
956 authcDN->bv_val, authzDN->bv_val, 0 );
958 Debug( LDAP_DEBUG_TRACE,
959 "==>slap_sasl_authorized: can %s become %s?\n",
960 authcDN->bv_val, authzDN->bv_val, 0 );
963 /* If person is authorizing to self, succeed */
964 if ( dn_match( authcDN, authzDN ) ) {
969 /* Allow the manager to authorize as any DN. */
970 if( op->o_conn->c_authz_backend && be_isroot( op->o_conn->c_authz_backend, authcDN )) {
975 /* Check source rules */
976 if( authz_policy & SASL_AUTHZ_TO ) {
977 rc = slap_sasl_check_authz( op, authcDN, authzDN,
978 slap_schema.si_ad_saslAuthzTo, authcDN );
979 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
984 /* Check destination rules */
985 if( authz_policy & SASL_AUTHZ_FROM ) {
986 rc = slap_sasl_check_authz( op, authzDN, authcDN,
987 slap_schema.si_ad_saslAuthzFrom, authcDN );
988 if( rc == LDAP_SUCCESS ) {
993 rc = LDAP_INAPPROPRIATE_AUTH;
998 LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );
1000 Debug( LDAP_DEBUG_TRACE,
1001 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );