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 );
315 if ( rc == LDAP_URL_ERR_BADSCHEME ) {
316 /* last chance: assume it's a(n exact) DN ... */
317 bv.bv_val = uri->bv_val;
318 *scope = LDAP_X_SCOPE_EXACT;
322 if ( rc != LDAP_URL_SUCCESS ) {
323 return LDAP_PROTOCOL_ERROR;
326 if (( ludp->lud_host && *ludp->lud_host )
327 || ludp->lud_attrs || ludp->lud_exts )
329 /* host part must be empty */
330 /* attrs and extensions parts must be empty */
331 rc = LDAP_PROTOCOL_ERROR;
336 *scope = ludp->lud_scope;
338 /* Grab the filter */
339 if ( ludp->lud_filter ) {
340 *filter = str2filter_x( op, ludp->lud_filter );
341 if ( *filter == NULL ) {
342 rc = LDAP_PROTOCOL_ERROR;
345 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
348 /* Grab the searchbase */
349 ber_str2bv( ludp->lud_dn, 0, 0, base );
350 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
353 if( rc != LDAP_SUCCESS ) {
354 if( *filter ) filter_free_x( op, *filter );
360 /* Don't free these, return them to caller */
361 ludp->lud_filter = NULL;
365 ldap_free_urldesc( ludp );
369 static int slap_sasl_rx_off(char *rep, int *off)
374 /* Precompile replace pattern. Find the $<n> placeholders */
377 for ( c = rep; *c; c++ ) {
378 if ( *c == '\\' && c[1] ) {
383 if ( n == SASLREGEX_REPLACE ) {
385 LDAP_LOG( TRANSPORT, ERR,
386 "slap_sasl_rx_off: \"%s\" has too many $n "
387 "placeholders (max %d)\n", rep, SASLREGEX_REPLACE, 0 );
389 Debug( LDAP_DEBUG_ANY,
390 "SASL replace pattern %s has too many $n "
391 "placeholders (max %d)\n",
392 rep, SASLREGEX_REPLACE, 0 );
395 return( LDAP_OTHER );
402 /* Final placeholder, after the last $n */
406 return( LDAP_SUCCESS );
409 int slap_sasl_regexp_config( const char *match, const char *replace )
414 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
415 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
417 reg = &SaslRegexp[nSaslRegexp];
419 reg->sr_match = ch_strdup( match );
420 reg->sr_replace = ch_strdup( replace );
422 /* Precompile matching pattern */
423 rc = regcomp( ®->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
426 LDAP_LOG( TRANSPORT, ERR,
427 "slap_sasl_regexp_config: \"%s\" could not be compiled.\n",
428 reg->sr_match, 0, 0 );
430 Debug( LDAP_DEBUG_ANY,
431 "SASL match pattern %s could not be compiled by regexp engine\n",
432 reg->sr_match, 0, 0 );
435 return( LDAP_OTHER );
438 rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
439 if ( rc != LDAP_SUCCESS ) return rc;
442 return( LDAP_SUCCESS );
446 /* Perform replacement on regexp matches */
447 static void slap_sasl_rx_exp(
451 const char *saslname,
455 int i, n, len, insert;
457 /* Get the total length of the final URI */
461 while( off[n] >= 0 ) {
462 /* Len of next section from replacement string (x,y,z above) */
463 len += off[n] - off[n-1] - 2;
467 /* Len of string from saslname that matched next $i (b,d above) */
468 i = rep[ off[n] + 1 ] - '0';
469 len += str[i].rm_eo - str[i].rm_so;
472 out->bv_val = sl_malloc( len + 1, ctx );
475 /* Fill in URI with replace string, replacing $i as we go */
478 while( off[n] >= 0) {
479 /* Paste in next section from replacement string (x,y,z above) */
480 len = off[n] - off[n-1] - 2;
481 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
486 /* Paste in string from saslname that matched next $i (b,d above) */
487 i = rep[ off[n] + 1 ] - '0';
488 len = str[i].rm_eo - str[i].rm_so;
489 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
495 out->bv_val[insert] = '\0';
498 /* Take the passed in SASL name and attempt to convert it into an
499 LDAP URI to find the matching LDAP entry, using the pattern matching
500 strings given in the saslregexp config file directive(s) */
502 static int slap_sasl_regexp( struct berval *in, struct berval *out,
503 int flags, void *ctx )
505 char *saslname = in->bv_val;
507 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
510 memset( out, 0, sizeof( *out ) );
513 LDAP_LOG( TRANSPORT, ENTRY,
514 "slap_sasl_regexp: converting SASL name %s\n", saslname, 0, 0 );
516 Debug( LDAP_DEBUG_TRACE, "slap_sasl_regexp: converting SASL name %s\n",
520 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
524 /* Match the normalized SASL name to the saslregexp patterns */
525 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
526 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE,
527 sr_strings, 0) == 0 )
531 if( i >= nSaslRegexp ) return( 0 );
534 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
535 * replace pattern of the form "x$1y$2z". The returned string needs
536 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
538 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
539 sr_strings, saslname, out, ctx );
542 LDAP_LOG( TRANSPORT, ENTRY,
543 "slap_sasl_regexp: converted SASL name to %s\n",
544 out->bv_len ? out->bv_val : "", 0, 0 );
546 Debug( LDAP_DEBUG_TRACE,
547 "slap_sasl_regexp: converted SASL name to %s\n",
548 out->bv_len ? out->bv_val : "", 0, 0 );
554 /* This callback actually does some work...*/
555 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
557 struct berval *ndn = o->o_callback->sc_private;
559 if (rs->sr_type != REP_SEARCH) return 0;
561 /* We only want to be called once */
563 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
568 LDAP_LOG( TRANSPORT, DETAIL1,
569 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
571 Debug( LDAP_DEBUG_TRACE,
572 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
577 ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
582 typedef struct smatch_info {
587 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
589 smatch_info *sm = o->o_callback->sc_private;
591 if (rs->sr_type != REP_SEARCH) return 0;
593 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
595 return -1; /* short-circuit the search */
602 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
603 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
604 * the rule must be used as an internal search for entries. If that search
605 * returns the *assertDN entry, the match is successful.
607 * The assertDN should not have the dn: prefix
611 int slap_sasl_match( Operation *opx, struct berval *rule,
612 struct berval *assertDN, struct berval *authc )
617 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
619 SlapReply rs = {REP_RESULT};
622 LDAP_LOG( TRANSPORT, ENTRY,
623 "slap_sasl_match: comparing DN %s to rule %s\n",
624 assertDN->bv_val, rule->bv_val,0 );
626 Debug( LDAP_DEBUG_TRACE,
627 "===>slap_sasl_match: comparing DN %s to rule %s\n",
628 assertDN->bv_val, rule->bv_val, 0 );
631 rc = slap_parseURI( opx, rule, &op.o_req_dn,
632 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
634 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
636 switch ( op.oq_search.rs_scope ) {
637 case LDAP_X_SCOPE_EXACT:
639 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
642 rc = LDAP_INAPPROPRIATE_AUTH;
646 case LDAP_X_SCOPE_CHILDREN:
647 case LDAP_X_SCOPE_SUBTREE:
648 case LDAP_X_SCOPE_ONELEVEL:
650 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
652 rc = LDAP_INAPPROPRIATE_AUTH;
654 if ( d == 0 && op.oq_search.rs_scope == LDAP_X_SCOPE_SUBTREE ) {
657 } else if ( d > 0 ) {
660 bv.bv_len = op.o_req_ndn.bv_len;
661 bv.bv_val = assertDN->bv_val + d;
663 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
664 switch ( op.oq_search.rs_scope ) {
665 case LDAP_X_SCOPE_SUBTREE:
666 case LDAP_X_SCOPE_CHILDREN:
670 case LDAP_X_SCOPE_ONELEVEL:
674 dnParent( assertDN, &pdn );
675 /* the common portion of the DN
676 * already matches, so only check
677 * if parent DN of assertedDN
678 * is all the pattern */
679 if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
685 /* at present, impossible */
693 case LDAP_X_SCOPE_REGEX:
694 rc = regcomp(®, op.o_req_ndn.bv_val,
695 REG_EXTENDED|REG_ICASE|REG_NOSUB);
697 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
703 rc = LDAP_INAPPROPRIATE_AUTH;
711 /* Must run an internal search. */
712 if ( op.oq_search.rs_filter == NULL ) {
713 rc = LDAP_FILTER_ERROR;
718 LDAP_LOG( TRANSPORT, DETAIL1,
719 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
720 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
722 Debug( LDAP_DEBUG_TRACE,
723 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
724 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
727 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
728 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
729 rc = LDAP_INAPPROPRIATE_AUTH;
737 op.o_tag = LDAP_REQ_SEARCH;
738 op.o_protocol = LDAP_VERSION3;
741 op.o_time = slap_get_time();
742 op.o_do_not_cache = 1;
743 op.o_is_auth_check = 1;
744 op.o_threadctx = opx->o_threadctx;
745 op.o_tmpmemctx = opx->o_tmpmemctx;
746 op.o_tmpmfuncs = opx->o_tmpmfuncs;
750 op.o_conn = opx->o_conn;
751 op.o_connid = opx->o_connid;
752 /* use req_ndn as req_dn instead of non-pretty base of uri */
753 if( !BER_BVISNULL( &op.o_req_dn ) ) ch_free( op.o_req_dn.bv_val );
754 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
755 op.oq_search.rs_slimit = 1;
756 op.oq_search.rs_tlimit = SLAP_NO_LIMIT;
757 op.o_sync_slog_size = -1;
759 op.o_bd->be_search( &op, &rs );
764 rc = LDAP_INAPPROPRIATE_AUTH;
768 if( !BER_BVISNULL( &op.o_req_dn ) ) sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
769 if( !BER_BVISNULL( &op.o_req_ndn ) ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
770 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
771 if( op.ors_filterstr.bv_val ) ch_free( op.ors_filterstr.bv_val );
774 LDAP_LOG( TRANSPORT, ENTRY,
775 "slap_sasl_match: comparison returned %d\n", rc, 0, 0 );
777 Debug( LDAP_DEBUG_TRACE,
778 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
786 * This function answers the question, "Can this ID authorize to that ID?",
787 * based on authorization rules. The rules are stored in the *searchDN, in the
788 * attribute named by *attr. If any of those rules map to the *assertDN, the
789 * authorization is approved.
791 * The DNs should not have the dn: prefix
794 slap_sasl_check_authz( Operation *op,
795 struct berval *searchDN,
796 struct berval *assertDN,
797 AttributeDescription *ad,
798 struct berval *authc )
804 LDAP_LOG( TRANSPORT, ENTRY,
805 "slap_sasl_check_authz: does %s match %s rule in %s?\n",
806 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
808 Debug( LDAP_DEBUG_TRACE,
809 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
810 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
813 rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
814 if( rc != LDAP_SUCCESS ) goto COMPLETE;
816 /* Check if the *assertDN matches any **vals */
818 for( i=0; vals[i].bv_val != NULL; i++ ) {
819 rc = slap_sasl_match( op, &vals[i], assertDN, authc );
820 if ( rc == LDAP_SUCCESS ) goto COMPLETE;
823 rc = LDAP_INAPPROPRIATE_AUTH;
826 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
829 LDAP_LOG( TRANSPORT, RESULTS,
830 "slap_sasl_check_authz: %s check returning %s\n",
831 ad->ad_cname.bv_val, rc, 0 );
833 Debug( LDAP_DEBUG_TRACE,
834 "<==slap_sasl_check_authz: %s check returning %d\n",
835 ad->ad_cname.bv_val, rc, 0);
842 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
843 * return the LDAP DN to which it matches. The SASL regexp rules in the config
844 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
845 * search with scope=base), just return the URI (or its searchbase). Otherwise
846 * an internal search must be done, and if that search returns exactly one
847 * entry, return the DN of that one entry.
849 void slap_sasl2dn( Operation *opx,
850 struct berval *saslname, struct berval *sasldn, int flags )
853 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
855 SlapReply rs = {REP_RESULT};
856 struct berval regout = BER_BVNULL;
859 LDAP_LOG( TRANSPORT, ENTRY,
860 "slap_sasl2dn: converting SASL name %s to DN.\n",
861 saslname->bv_val, 0, 0 );
863 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
864 "converting SASL name %s to a DN\n",
865 saslname->bv_val, 0,0 );
868 sasldn->bv_val = NULL;
870 cb.sc_private = sasldn;
872 /* Convert the SASL name into a minimal URI */
873 if( !slap_sasl_regexp( saslname, ®out, flags, opx->o_tmpmemctx ) ) {
877 rc = slap_parseURI( opx, ®out, &op.o_req_dn,
878 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
880 if( regout.bv_val ) sl_free( regout.bv_val, opx->o_tmpmemctx );
881 if( rc != LDAP_SUCCESS ) {
885 /* Must do an internal search */
886 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
888 switch ( op.oq_search.rs_scope ) {
889 case LDAP_X_SCOPE_EXACT:
890 *sasldn = op.o_req_ndn;
891 op.o_req_ndn.bv_len = 0;
892 op.o_req_ndn.bv_val = NULL;
893 /* intentionally continue to next case */
895 case LDAP_X_SCOPE_REGEX:
896 case LDAP_X_SCOPE_SUBTREE:
897 case LDAP_X_SCOPE_CHILDREN:
898 case LDAP_X_SCOPE_ONELEVEL:
899 /* correctly parsed, but illegal */
902 case LDAP_SCOPE_BASE:
903 case LDAP_SCOPE_ONELEVEL:
904 case LDAP_SCOPE_SUBTREE:
905 #ifdef LDAP_SCOPE_SUBORDINATE
906 case LDAP_SCOPE_SUBORDINATE:
912 /* catch unhandled cases (there shouldn't be) */
917 LDAP_LOG( TRANSPORT, DETAIL1,
918 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
919 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
921 Debug( LDAP_DEBUG_TRACE,
922 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
923 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
926 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
930 op.o_conn = opx->o_conn;
931 op.o_connid = opx->o_connid;
932 op.o_tag = LDAP_REQ_SEARCH;
933 op.o_protocol = LDAP_VERSION3;
934 op.o_ndn = opx->o_conn->c_ndn;
936 op.o_time = slap_get_time();
937 op.o_do_not_cache = 1;
938 op.o_is_auth_check = 1;
939 op.o_threadctx = opx->o_threadctx;
940 op.o_tmpmemctx = opx->o_tmpmemctx;
941 op.o_tmpmfuncs = opx->o_tmpmfuncs;
945 op.oq_search.rs_deref = LDAP_DEREF_NEVER;
946 op.oq_search.rs_slimit = 1;
947 op.oq_search.rs_tlimit = SLAP_NO_LIMIT;
948 op.oq_search.rs_attrsonly = 1;
949 /* use req_ndn as req_dn instead of non-pretty base of uri */
950 if( !BER_BVISNULL( &op.o_req_dn ) ) ch_free( op.o_req_dn.bv_val );
951 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
953 op.o_bd->be_search( &op, &rs );
956 if( sasldn->bv_len ) {
957 opx->o_conn->c_authz_backend = op.o_bd;
959 if( !BER_BVISNULL( &op.o_req_dn ) ) sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
960 if( !BER_BVISNULL( &op.o_req_ndn ) ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
961 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
962 if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
965 LDAP_LOG( TRANSPORT, ENTRY,
966 "slap_sasl2dn: Converted SASL name to %s\n",
967 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
969 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
970 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
977 /* Check if a bind can SASL authorize to another identity.
978 * The DNs should not have the dn: prefix
981 int slap_sasl_authorized( Operation *op,
982 struct berval *authcDN, struct berval *authzDN )
984 int rc = LDAP_INAPPROPRIATE_AUTH;
986 /* User binding as anonymous */
987 if ( authzDN == NULL ) {
993 LDAP_LOG( TRANSPORT, ENTRY,
994 "slap_sasl_authorized: can %s become %s?\n",
995 authcDN->bv_val, authzDN->bv_val, 0 );
997 Debug( LDAP_DEBUG_TRACE,
998 "==>slap_sasl_authorized: can %s become %s?\n",
999 authcDN->bv_val, authzDN->bv_val, 0 );
1002 /* If person is authorizing to self, succeed */
1003 if ( dn_match( authcDN, authzDN ) ) {
1008 /* Allow the manager to authorize as any DN. */
1009 if( op->o_conn->c_authz_backend &&
1010 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
1016 /* Check source rules */
1017 if( authz_policy & SASL_AUTHZ_TO ) {
1018 rc = slap_sasl_check_authz( op, authcDN, authzDN,
1019 slap_schema.si_ad_saslAuthzTo, authcDN );
1020 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
1025 /* Check destination rules */
1026 if( authz_policy & SASL_AUTHZ_FROM ) {
1027 rc = slap_sasl_check_authz( op, authzDN, authcDN,
1028 slap_schema.si_ad_saslAuthzFrom, authcDN );
1029 if( rc == LDAP_SUCCESS ) {
1034 rc = LDAP_INAPPROPRIATE_AUTH;
1039 LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );
1041 Debug( LDAP_DEBUG_TRACE,
1042 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );