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)
36 #define LDAP_X_SCOPE_ONELEVEL ((ber_int_t) 0x0050)
39 * IDs in DNauthzid form can now have a type specifier, that
40 * influences how they are used in related operations.
42 * syntax: dn[.{exact|regex}]:<val>
44 * dn.exact: the value must pass normalization and is used
46 * dn.regex: the value is treated as a regular expression
47 * in matching DN values in saslAuthz{To|From}
49 * dn: for backwards compatibility reasons, the value
50 * is treated as a regular expression, and thus
51 * it is not normalized nor validated; it is used
52 * in exact or regex comparisons based on the
55 * IDs in DNauthzid form can now have a type specifier, that
56 * influences how they are used in related operations.
58 * syntax: u[.mech[/realm]]:<val>
60 * where mech is a SIMPLE, AUTHZ, or a SASL mechanism name
61 * and realm is mechanism specific realm (separate to those
62 * which are representable as part of the principal).
65 typedef struct sasl_regexp {
66 char *sr_match; /* regexp match pattern */
67 char *sr_replace; /* regexp replace pattern */
68 regex_t sr_workspace; /* workspace for regexp engine */
69 int sr_offset[SASLREGEX_REPLACE+2]; /* offsets of $1,$2... in *replace */
72 static int nSaslRegexp = 0;
73 static SaslRegexp_t *SaslRegexp = NULL;
75 /* What SASL proxy authorization policies are allowed? */
76 #define SASL_AUTHZ_NONE 0x00
77 #define SASL_AUTHZ_FROM 0x01
78 #define SASL_AUTHZ_TO 0x02
79 #define SASL_AUTHZ_AND 0x10
81 static int authz_policy = SASL_AUTHZ_NONE;
83 int slap_sasl_setpolicy( const char *arg )
85 int rc = LDAP_SUCCESS;
87 if ( strcasecmp( arg, "none" ) == 0 ) {
88 authz_policy = SASL_AUTHZ_NONE;
89 } else if ( strcasecmp( arg, "from" ) == 0 ) {
90 authz_policy = SASL_AUTHZ_FROM;
91 } else if ( strcasecmp( arg, "to" ) == 0 ) {
92 authz_policy = SASL_AUTHZ_TO;
93 } else if ( strcasecmp( arg, "both" ) == 0 || strcasecmp( arg, "any" ) == 0 ) {
94 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO;
95 } else if ( strcasecmp( arg, "all" ) == 0 ) {
96 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND;
103 int slap_parse_user( struct berval *id, struct berval *user,
104 struct berval *realm, struct berval *mech )
109 assert( id->bv_val );
116 if ( u != 'u' && u != 'U' ) {
117 /* called with something other than u: */
118 return LDAP_PROTOCOL_ERROR;
122 * u[.mech[/realm]]:user
125 user->bv_val = strchr( id->bv_val, ':' );
126 if ( user->bv_val == NULL ) {
127 return LDAP_PROTOCOL_ERROR;
129 user->bv_val[ 0 ] = '\0';
131 user->bv_len = id->bv_len - ( user->bv_val - id->bv_val );
133 mech->bv_val = strchr( id->bv_val, '.' );
134 if ( mech->bv_val != NULL ) {
135 mech->bv_val[ 0 ] = '\0';
138 realm->bv_val = strchr( mech->bv_val, '/' );
140 if ( realm->bv_val ) {
141 realm->bv_val[ 0 ] = '\0';
143 mech->bv_len = realm->bv_val - mech->bv_val - 1;
144 realm->bv_len = user->bv_val - realm->bv_val - 1;
146 mech->bv_len = user->bv_val - mech->bv_val - 1;
150 realm->bv_val = NULL;
153 if ( id->bv_val[ 1 ] != '\0' ) {
154 return LDAP_PROTOCOL_ERROR;
157 if ( mech->bv_val != NULL ) {
158 assert( mech->bv_val == id->bv_val + 2 );
160 AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
164 if ( realm->bv_val ) {
165 assert( realm->bv_val >= id->bv_val + 2 );
167 AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
171 /* leave "u:" before user */
174 user->bv_val[ 0 ] = u;
175 user->bv_val[ 1 ] = ':';
180 static int slap_parseURI( Operation *op, struct berval *uri,
181 struct berval *base, struct berval *nbase,
182 int *scope, Filter **filter, struct berval *fstr )
188 assert( uri != NULL && uri->bv_val != NULL );
191 nbase->bv_val = NULL;
199 LDAP_LOG( TRANSPORT, ENTRY,
200 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
202 Debug( LDAP_DEBUG_TRACE,
203 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
206 rc = LDAP_PROTOCOL_ERROR;
207 if ( !strncasecmp( uri->bv_val, "dn", sizeof( "dn" ) - 1 ) ) {
208 bv.bv_val = uri->bv_val + sizeof( "dn" ) - 1;
210 if ( bv.bv_val[ 0 ] == '.' ) {
213 if ( !strncasecmp( bv.bv_val, "exact:", sizeof( "exact:" ) - 1 ) ) {
214 bv.bv_val += sizeof( "exact:" ) - 1;
215 *scope = LDAP_X_SCOPE_EXACT;
217 } else if ( !strncasecmp( bv.bv_val, "regex:", sizeof( "regex:" ) - 1 ) ) {
218 bv.bv_val += sizeof( "regex:" ) - 1;
219 *scope = LDAP_X_SCOPE_REGEX;
221 } else if ( !strncasecmp( bv.bv_val, "children:", sizeof( "chldren:" ) - 1 ) ) {
222 bv.bv_val += sizeof( "children:" ) - 1;
223 *scope = LDAP_X_SCOPE_CHILDREN;
225 } else if ( !strncasecmp( bv.bv_val, "subtree:", sizeof( "subtree:" ) - 1 ) ) {
226 bv.bv_val += sizeof( "subtree:" ) - 1;
227 *scope = LDAP_X_SCOPE_SUBTREE;
229 } else if ( !strncasecmp( bv.bv_val, "onelevel:", sizeof( "onelevel:" ) - 1 ) ) {
230 bv.bv_val += sizeof( "onelevel:" ) - 1;
231 *scope = LDAP_X_SCOPE_ONELEVEL;
234 return LDAP_PROTOCOL_ERROR;
237 if ( bv.bv_val[ 0 ] != ':' ) {
238 return LDAP_PROTOCOL_ERROR;
240 *scope = LDAP_X_SCOPE_EXACT;
244 bv.bv_val += strspn( bv.bv_val, " " );
245 /* jump here in case no type specification was present
246 * and uir was not an URI... HEADS-UP: assuming EXACT */
247 is_dn: bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
250 case LDAP_X_SCOPE_EXACT:
251 case LDAP_X_SCOPE_CHILDREN:
252 case LDAP_X_SCOPE_SUBTREE:
253 case LDAP_X_SCOPE_ONELEVEL:
254 rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
255 if( rc != LDAP_SUCCESS ) {
260 case LDAP_X_SCOPE_REGEX:
261 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
272 } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
273 && ( uri->bv_val[ 1 ] == ':'
274 || uri->bv_val[ 1 ] == '/'
275 || uri->bv_val[ 1 ] == '.' ) )
277 Connection c = *op->o_conn;
278 char buf[ SLAP_LDAPDN_MAXLEN ];
284 if ( sizeof( buf ) <= uri->bv_len ) {
285 return LDAP_INVALID_SYNTAX;
288 id.bv_len = uri->bv_len;
290 strncpy( buf, uri->bv_val, sizeof( buf ) );
292 rc = slap_parse_user( &id, &user, &realm, &mech );
293 if ( rc != LDAP_SUCCESS ) {
298 c.c_sasl_bind_mech = mech;
300 c.c_sasl_bind_mech.bv_val = "AUTHZ";
301 c.c_sasl_bind_mech.bv_len = sizeof( "AUTHZ" ) - 1;
304 rc = slap_sasl_getdn( &c, op, user.bv_val, user.bv_len,
305 realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
307 if ( rc == LDAP_SUCCESS ) {
308 *scope = LDAP_X_SCOPE_EXACT;
314 rc = ldap_url_parse( uri->bv_val, &ludp );
316 case LDAP_URL_SUCCESS:
317 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
321 return LDAP_PROTOCOL_ERROR;
325 case LDAP_URL_ERR_BADSCHEME:
326 /* last chance: assume it's a(n exact) DN ... */
327 bv.bv_val = uri->bv_val;
328 *scope = LDAP_X_SCOPE_EXACT;
332 return LDAP_PROTOCOL_ERROR;
335 if ( ( ludp->lud_host && *ludp->lud_host )
336 || ludp->lud_attrs || ludp->lud_exts )
338 /* host part must be empty */
339 /* attrs and extensions parts must be empty */
340 rc = LDAP_PROTOCOL_ERROR;
345 *scope = ludp->lud_scope;
347 /* Grab the filter */
348 if ( ludp->lud_filter ) {
349 *filter = str2filter_x( op, ludp->lud_filter );
350 if ( *filter == NULL ) {
351 rc = LDAP_PROTOCOL_ERROR;
354 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
357 /* Grab the searchbase */
358 ber_str2bv( ludp->lud_dn, 0, 0, base );
359 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
362 if( rc != LDAP_SUCCESS ) {
363 if( *filter ) filter_free_x( op, *filter );
369 /* Don't free these, return them to caller */
370 ludp->lud_filter = NULL;
374 ldap_free_urldesc( ludp );
378 static int slap_sasl_rx_off(char *rep, int *off)
383 /* Precompile replace pattern. Find the $<n> placeholders */
386 for ( c = rep; *c; c++ ) {
387 if ( *c == '\\' && c[1] ) {
392 if ( n == SASLREGEX_REPLACE ) {
394 LDAP_LOG( TRANSPORT, ERR,
395 "slap_sasl_rx_off: \"%s\" has too many $n "
396 "placeholders (max %d)\n", rep, SASLREGEX_REPLACE, 0 );
398 Debug( LDAP_DEBUG_ANY,
399 "SASL replace pattern %s has too many $n "
400 "placeholders (max %d)\n",
401 rep, SASLREGEX_REPLACE, 0 );
404 return( LDAP_OTHER );
411 /* Final placeholder, after the last $n */
415 return( LDAP_SUCCESS );
418 int slap_sasl_regexp_config( const char *match, const char *replace )
423 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
424 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
426 reg = &SaslRegexp[nSaslRegexp];
428 reg->sr_match = ch_strdup( match );
429 reg->sr_replace = ch_strdup( replace );
431 /* Precompile matching pattern */
432 rc = regcomp( ®->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
435 LDAP_LOG( TRANSPORT, ERR,
436 "slap_sasl_regexp_config: \"%s\" could not be compiled.\n",
437 reg->sr_match, 0, 0 );
439 Debug( LDAP_DEBUG_ANY,
440 "SASL match pattern %s could not be compiled by regexp engine\n",
441 reg->sr_match, 0, 0 );
444 return( LDAP_OTHER );
447 rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
448 if ( rc != LDAP_SUCCESS ) return rc;
451 return( LDAP_SUCCESS );
455 /* Perform replacement on regexp matches */
456 static void slap_sasl_rx_exp(
460 const char *saslname,
464 int i, n, len, insert;
466 /* Get the total length of the final URI */
470 while( off[n] >= 0 ) {
471 /* Len of next section from replacement string (x,y,z above) */
472 len += off[n] - off[n-1] - 2;
476 /* Len of string from saslname that matched next $i (b,d above) */
477 i = rep[ off[n] + 1 ] - '0';
478 len += str[i].rm_eo - str[i].rm_so;
481 out->bv_val = sl_malloc( len + 1, ctx );
484 /* Fill in URI with replace string, replacing $i as we go */
487 while( off[n] >= 0) {
488 /* Paste in next section from replacement string (x,y,z above) */
489 len = off[n] - off[n-1] - 2;
490 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
495 /* Paste in string from saslname that matched next $i (b,d above) */
496 i = rep[ off[n] + 1 ] - '0';
497 len = str[i].rm_eo - str[i].rm_so;
498 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
504 out->bv_val[insert] = '\0';
507 /* Take the passed in SASL name and attempt to convert it into an
508 LDAP URI to find the matching LDAP entry, using the pattern matching
509 strings given in the saslregexp config file directive(s) */
511 static int slap_sasl_regexp( struct berval *in, struct berval *out,
512 int flags, void *ctx )
514 char *saslname = in->bv_val;
516 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
519 memset( out, 0, sizeof( *out ) );
522 LDAP_LOG( TRANSPORT, ENTRY,
523 "slap_sasl_regexp: converting SASL name %s\n", saslname, 0, 0 );
525 Debug( LDAP_DEBUG_TRACE, "slap_sasl_regexp: converting SASL name %s\n",
529 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
533 /* Match the normalized SASL name to the saslregexp patterns */
534 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
535 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE,
536 sr_strings, 0) == 0 )
540 if( i >= nSaslRegexp ) return( 0 );
543 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
544 * replace pattern of the form "x$1y$2z". The returned string needs
545 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
547 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
548 sr_strings, saslname, out, ctx );
551 LDAP_LOG( TRANSPORT, ENTRY,
552 "slap_sasl_regexp: converted SASL name to %s\n",
553 out->bv_len ? out->bv_val : "", 0, 0 );
555 Debug( LDAP_DEBUG_TRACE,
556 "slap_sasl_regexp: converted SASL name to %s\n",
557 out->bv_len ? out->bv_val : "", 0, 0 );
563 /* This callback actually does some work...*/
564 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
566 struct berval *ndn = o->o_callback->sc_private;
568 if (rs->sr_type != REP_SEARCH) return 0;
570 /* We only want to be called once */
572 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
577 LDAP_LOG( TRANSPORT, DETAIL1,
578 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
580 Debug( LDAP_DEBUG_TRACE,
581 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
586 ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
591 typedef struct smatch_info {
596 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
598 smatch_info *sm = o->o_callback->sc_private;
600 if (rs->sr_type != REP_SEARCH) return 0;
602 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
604 return -1; /* short-circuit the search */
611 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
612 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
613 * the rule must be used as an internal search for entries. If that search
614 * returns the *assertDN entry, the match is successful.
616 * The assertDN should not have the dn: prefix
620 int slap_sasl_match( Operation *opx, struct berval *rule,
621 struct berval *assertDN, struct berval *authc )
626 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
628 SlapReply rs = {REP_RESULT};
631 LDAP_LOG( TRANSPORT, ENTRY,
632 "slap_sasl_match: comparing DN %s to rule %s\n",
633 assertDN->bv_val, rule->bv_val,0 );
635 Debug( LDAP_DEBUG_TRACE,
636 "===>slap_sasl_match: comparing DN %s to rule %s\n",
637 assertDN->bv_val, rule->bv_val, 0 );
640 rc = slap_parseURI( opx, rule, &op.o_req_dn,
641 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
643 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
645 switch ( op.oq_search.rs_scope ) {
646 case LDAP_X_SCOPE_EXACT:
648 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
651 rc = LDAP_INAPPROPRIATE_AUTH;
655 case LDAP_X_SCOPE_CHILDREN:
656 case LDAP_X_SCOPE_SUBTREE:
657 case LDAP_X_SCOPE_ONELEVEL:
659 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
661 rc = LDAP_INAPPROPRIATE_AUTH;
663 if ( d == 0 && op.oq_search.rs_scope == LDAP_X_SCOPE_SUBTREE ) {
666 } else if ( d > 0 ) {
669 bv.bv_len = op.o_req_ndn.bv_len;
670 bv.bv_val = assertDN->bv_val + d;
672 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
673 switch ( op.oq_search.rs_scope ) {
674 case LDAP_X_SCOPE_SUBTREE:
675 case LDAP_X_SCOPE_CHILDREN:
679 case LDAP_X_SCOPE_ONELEVEL:
683 dnParent( assertDN, &pdn );
684 /* the common portion of the DN
685 * already matches, so only check
686 * if parent DN of assertedDN
687 * is all the pattern */
688 if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
694 /* at present, impossible */
702 case LDAP_X_SCOPE_REGEX:
703 rc = regcomp(®, op.o_req_ndn.bv_val,
704 REG_EXTENDED|REG_ICASE|REG_NOSUB);
706 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
712 rc = LDAP_INAPPROPRIATE_AUTH;
720 /* Must run an internal search. */
721 if ( op.oq_search.rs_filter == NULL ) {
722 rc = LDAP_FILTER_ERROR;
727 LDAP_LOG( TRANSPORT, DETAIL1,
728 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
729 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
731 Debug( LDAP_DEBUG_TRACE,
732 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
733 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
736 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
737 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
738 rc = LDAP_INAPPROPRIATE_AUTH;
746 op.o_tag = LDAP_REQ_SEARCH;
747 op.o_protocol = LDAP_VERSION3;
750 op.o_time = slap_get_time();
751 op.o_do_not_cache = 1;
752 op.o_is_auth_check = 1;
753 op.o_threadctx = opx->o_threadctx;
754 op.o_tmpmemctx = opx->o_tmpmemctx;
755 op.o_tmpmfuncs = opx->o_tmpmfuncs;
759 op.o_conn = opx->o_conn;
760 op.o_connid = opx->o_connid;
761 /* use req_ndn as req_dn instead of non-pretty base of uri */
762 if( !BER_BVISNULL( &op.o_req_dn ) ) ch_free( op.o_req_dn.bv_val );
763 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
764 op.oq_search.rs_slimit = 1;
765 op.oq_search.rs_tlimit = SLAP_NO_LIMIT;
766 op.o_sync_slog_size = -1;
768 op.o_bd->be_search( &op, &rs );
773 rc = LDAP_INAPPROPRIATE_AUTH;
777 if( !BER_BVISNULL( &op.o_req_dn ) ) sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
778 if( !BER_BVISNULL( &op.o_req_ndn ) ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
779 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
780 if( op.ors_filterstr.bv_val ) ch_free( op.ors_filterstr.bv_val );
783 LDAP_LOG( TRANSPORT, ENTRY,
784 "slap_sasl_match: comparison returned %d\n", rc, 0, 0 );
786 Debug( LDAP_DEBUG_TRACE,
787 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
795 * This function answers the question, "Can this ID authorize to that ID?",
796 * based on authorization rules. The rules are stored in the *searchDN, in the
797 * attribute named by *attr. If any of those rules map to the *assertDN, the
798 * authorization is approved.
800 * The DNs should not have the dn: prefix
803 slap_sasl_check_authz( Operation *op,
804 struct berval *searchDN,
805 struct berval *assertDN,
806 AttributeDescription *ad,
807 struct berval *authc )
813 LDAP_LOG( TRANSPORT, ENTRY,
814 "slap_sasl_check_authz: does %s match %s rule in %s?\n",
815 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
817 Debug( LDAP_DEBUG_TRACE,
818 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
819 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
822 rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
823 if( rc != LDAP_SUCCESS ) goto COMPLETE;
825 /* Check if the *assertDN matches any **vals */
827 for( i=0; vals[i].bv_val != NULL; i++ ) {
828 rc = slap_sasl_match( op, &vals[i], assertDN, authc );
829 if ( rc == LDAP_SUCCESS ) goto COMPLETE;
832 rc = LDAP_INAPPROPRIATE_AUTH;
835 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
838 LDAP_LOG( TRANSPORT, RESULTS,
839 "slap_sasl_check_authz: %s check returning %s\n",
840 ad->ad_cname.bv_val, rc, 0 );
842 Debug( LDAP_DEBUG_TRACE,
843 "<==slap_sasl_check_authz: %s check returning %d\n",
844 ad->ad_cname.bv_val, rc, 0);
851 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
852 * return the LDAP DN to which it matches. The SASL regexp rules in the config
853 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
854 * search with scope=base), just return the URI (or its searchbase). Otherwise
855 * an internal search must be done, and if that search returns exactly one
856 * entry, return the DN of that one entry.
858 void slap_sasl2dn( Operation *opx,
859 struct berval *saslname, struct berval *sasldn, int flags )
862 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
864 SlapReply rs = {REP_RESULT};
865 struct berval regout = BER_BVNULL;
868 LDAP_LOG( TRANSPORT, ENTRY,
869 "slap_sasl2dn: converting SASL name %s to DN.\n",
870 saslname->bv_val, 0, 0 );
872 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
873 "converting SASL name %s to a DN\n",
874 saslname->bv_val, 0,0 );
877 sasldn->bv_val = NULL;
879 cb.sc_private = sasldn;
881 /* Convert the SASL name into a minimal URI */
882 if( !slap_sasl_regexp( saslname, ®out, flags, opx->o_tmpmemctx ) ) {
886 rc = slap_parseURI( opx, ®out, &op.o_req_dn,
887 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
889 if( regout.bv_val ) sl_free( regout.bv_val, opx->o_tmpmemctx );
890 if( rc != LDAP_SUCCESS ) {
894 /* Must do an internal search */
895 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
897 switch ( op.oq_search.rs_scope ) {
898 case LDAP_X_SCOPE_EXACT:
899 *sasldn = op.o_req_ndn;
900 op.o_req_ndn.bv_len = 0;
901 op.o_req_ndn.bv_val = NULL;
902 /* intentionally continue to next case */
904 case LDAP_X_SCOPE_REGEX:
905 case LDAP_X_SCOPE_SUBTREE:
906 case LDAP_X_SCOPE_CHILDREN:
907 case LDAP_X_SCOPE_ONELEVEL:
908 /* correctly parsed, but illegal */
911 case LDAP_SCOPE_BASE:
912 case LDAP_SCOPE_ONELEVEL:
913 case LDAP_SCOPE_SUBTREE:
914 #ifdef LDAP_SCOPE_SUBORDINATE
915 case LDAP_SCOPE_SUBORDINATE:
921 /* catch unhandled cases (there shouldn't be) */
926 LDAP_LOG( TRANSPORT, DETAIL1,
927 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
928 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
930 Debug( LDAP_DEBUG_TRACE,
931 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
932 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
935 if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
939 /* Must run an internal search. */
940 if ( op.ors_filter == NULL ) {
941 rc = LDAP_FILTER_ERROR;
945 op.o_conn = opx->o_conn;
946 op.o_connid = opx->o_connid;
947 op.o_tag = LDAP_REQ_SEARCH;
948 op.o_protocol = LDAP_VERSION3;
949 op.o_ndn = opx->o_conn->c_ndn;
951 op.o_time = slap_get_time();
952 op.o_do_not_cache = 1;
953 op.o_is_auth_check = 1;
954 op.o_threadctx = opx->o_threadctx;
955 op.o_tmpmemctx = opx->o_tmpmemctx;
956 op.o_tmpmfuncs = opx->o_tmpmfuncs;
960 op.oq_search.rs_deref = LDAP_DEREF_NEVER;
961 op.oq_search.rs_slimit = 1;
962 op.oq_search.rs_tlimit = SLAP_NO_LIMIT;
963 op.oq_search.rs_attrsonly = 1;
964 /* use req_ndn as req_dn instead of non-pretty base of uri */
965 if( !BER_BVISNULL( &op.o_req_dn ) ) ch_free( op.o_req_dn.bv_val );
966 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
968 op.o_bd->be_search( &op, &rs );
971 if( sasldn->bv_len ) {
972 opx->o_conn->c_authz_backend = op.o_bd;
974 if( !BER_BVISNULL( &op.o_req_dn ) ) sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
975 if( !BER_BVISNULL( &op.o_req_ndn ) ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
976 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
977 if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
980 LDAP_LOG( TRANSPORT, ENTRY,
981 "slap_sasl2dn: Converted SASL name to %s\n",
982 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
984 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
985 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
992 /* Check if a bind can SASL authorize to another identity.
993 * The DNs should not have the dn: prefix
996 int slap_sasl_authorized( Operation *op,
997 struct berval *authcDN, struct berval *authzDN )
999 int rc = LDAP_INAPPROPRIATE_AUTH;
1001 /* User binding as anonymous */
1002 if ( authzDN == NULL ) {
1008 LDAP_LOG( TRANSPORT, ENTRY,
1009 "slap_sasl_authorized: can %s become %s?\n",
1010 authcDN->bv_val, authzDN->bv_val, 0 );
1012 Debug( LDAP_DEBUG_TRACE,
1013 "==>slap_sasl_authorized: can %s become %s?\n",
1014 authcDN->bv_val, authzDN->bv_val, 0 );
1017 /* If person is authorizing to self, succeed */
1018 if ( dn_match( authcDN, authzDN ) ) {
1023 /* Allow the manager to authorize as any DN. */
1024 if( op->o_conn->c_authz_backend &&
1025 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
1031 /* Check source rules */
1032 if( authz_policy & SASL_AUTHZ_TO ) {
1033 rc = slap_sasl_check_authz( op, authcDN, authzDN,
1034 slap_schema.si_ad_saslAuthzTo, authcDN );
1035 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
1040 /* Check destination rules */
1041 if( authz_policy & SASL_AUTHZ_FROM ) {
1042 rc = slap_sasl_check_authz( op, authzDN, authcDN,
1043 slap_schema.si_ad_saslAuthzFrom, authcDN );
1044 if( rc == LDAP_SUCCESS ) {
1049 rc = LDAP_INAPPROPRIATE_AUTH;
1054 LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );
1056 Debug( LDAP_DEBUG_TRACE,
1057 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );