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;
238 if ( bv.bv_val[ 0 ] != ':' ) {
239 return LDAP_PROTOCOL_ERROR;
243 bv.bv_val += strspn( bv.bv_val, " " );
244 /* jump here in case no type specification was present
245 * and uir was not an URI... HEADS-UP: assuming EXACT */
246 is_dn: bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
249 case LDAP_X_SCOPE_EXACT:
250 case LDAP_X_SCOPE_CHILDREN:
251 case LDAP_X_SCOPE_SUBTREE:
252 case LDAP_X_SCOPE_ONELEVEL:
253 rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
254 if( rc != LDAP_SUCCESS ) {
259 case LDAP_X_SCOPE_REGEX:
260 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
271 } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
272 && ( uri->bv_val[ 1 ] == ':'
273 || uri->bv_val[ 1 ] == '/'
274 || uri->bv_val[ 1 ] == '.' ) )
276 Connection c = *op->o_conn;
277 char buf[ SLAP_LDAPDN_MAXLEN ];
283 if ( sizeof( buf ) <= uri->bv_len ) {
284 return LDAP_INVALID_SYNTAX;
287 id.bv_len = uri->bv_len;
289 strncpy( buf, uri->bv_val, sizeof( buf ) );
291 rc = slap_parse_user( &id, &user, &realm, &mech );
292 if ( rc != LDAP_SUCCESS ) {
297 c.c_sasl_bind_mech = mech;
299 c.c_sasl_bind_mech.bv_val = "AUTHZ";
300 c.c_sasl_bind_mech.bv_len = sizeof( "AUTHZ" ) - 1;
303 rc = slap_sasl_getdn( &c, op, user.bv_val, user.bv_len,
304 realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
306 if ( rc == LDAP_SUCCESS ) {
307 *scope = LDAP_X_SCOPE_EXACT;
313 rc = ldap_url_parse( uri->bv_val, &ludp );
314 if ( rc == LDAP_URL_ERR_BADSCHEME ) {
315 /* last chance: assume it's a(n exact) DN ... */
316 bv.bv_val = uri->bv_val;
317 *scope = LDAP_X_SCOPE_EXACT;
321 if ( rc != LDAP_URL_SUCCESS ) {
322 return LDAP_PROTOCOL_ERROR;
325 if (( ludp->lud_host && *ludp->lud_host )
326 || ludp->lud_attrs || ludp->lud_exts )
328 /* host part must be empty */
329 /* attrs and extensions parts must be empty */
330 rc = LDAP_PROTOCOL_ERROR;
335 *scope = ludp->lud_scope;
337 /* Grab the filter */
338 if ( ludp->lud_filter ) {
339 *filter = str2filter_x( op, ludp->lud_filter );
340 if ( *filter == NULL ) {
341 rc = LDAP_PROTOCOL_ERROR;
344 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
347 /* Grab the searchbase */
348 ber_str2bv( ludp->lud_dn, 0, 0, base );
349 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
352 if( rc != LDAP_SUCCESS ) {
353 if( *filter ) filter_free_x( op, *filter );
359 /* Don't free these, return them to caller */
360 ludp->lud_filter = NULL;
364 ldap_free_urldesc( ludp );
368 static int slap_sasl_rx_off(char *rep, int *off)
373 /* Precompile replace pattern. Find the $<n> placeholders */
376 for ( c = rep; *c; c++ ) {
377 if ( *c == '\\' && c[1] ) {
382 if ( n == SASLREGEX_REPLACE ) {
384 LDAP_LOG( TRANSPORT, ERR,
385 "slap_sasl_rx_off: \"%s\" has too many $n "
386 "placeholders (max %d)\n", rep, SASLREGEX_REPLACE, 0 );
388 Debug( LDAP_DEBUG_ANY,
389 "SASL replace pattern %s has too many $n "
390 "placeholders (max %d)\n",
391 rep, SASLREGEX_REPLACE, 0 );
394 return( LDAP_OTHER );
401 /* Final placeholder, after the last $n */
405 return( LDAP_SUCCESS );
408 int slap_sasl_regexp_config( const char *match, const char *replace )
413 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
414 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
416 reg = &SaslRegexp[nSaslRegexp];
418 reg->sr_match = ch_strdup( match );
419 reg->sr_replace = ch_strdup( replace );
421 /* Precompile matching pattern */
422 rc = regcomp( ®->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
425 LDAP_LOG( TRANSPORT, ERR,
426 "slap_sasl_regexp_config: \"%s\" could not be compiled.\n",
427 reg->sr_match, 0, 0 );
429 Debug( LDAP_DEBUG_ANY,
430 "SASL match pattern %s could not be compiled by regexp engine\n",
431 reg->sr_match, 0, 0 );
434 return( LDAP_OTHER );
437 rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
438 if ( rc != LDAP_SUCCESS ) return rc;
441 return( LDAP_SUCCESS );
445 /* Perform replacement on regexp matches */
446 static void slap_sasl_rx_exp(
450 const char *saslname,
454 int i, n, len, insert;
456 /* Get the total length of the final URI */
460 while( off[n] >= 0 ) {
461 /* Len of next section from replacement string (x,y,z above) */
462 len += off[n] - off[n-1] - 2;
466 /* Len of string from saslname that matched next $i (b,d above) */
467 i = rep[ off[n] + 1 ] - '0';
468 len += str[i].rm_eo - str[i].rm_so;
471 out->bv_val = sl_malloc( len + 1, ctx );
474 /* Fill in URI with replace string, replacing $i as we go */
477 while( off[n] >= 0) {
478 /* Paste in next section from replacement string (x,y,z above) */
479 len = off[n] - off[n-1] - 2;
480 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
485 /* Paste in string from saslname that matched next $i (b,d above) */
486 i = rep[ off[n] + 1 ] - '0';
487 len = str[i].rm_eo - str[i].rm_so;
488 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
494 out->bv_val[insert] = '\0';
497 /* Take the passed in SASL name and attempt to convert it into an
498 LDAP URI to find the matching LDAP entry, using the pattern matching
499 strings given in the saslregexp config file directive(s) */
501 static int slap_sasl_regexp( struct berval *in, struct berval *out,
502 int flags, void *ctx )
504 char *saslname = in->bv_val;
506 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
509 memset( out, 0, sizeof( *out ) );
512 LDAP_LOG( TRANSPORT, ENTRY,
513 "slap_sasl_regexp: converting SASL name %s\n", saslname, 0, 0 );
515 Debug( LDAP_DEBUG_TRACE, "slap_sasl_regexp: converting SASL name %s\n",
519 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
523 /* Match the normalized SASL name to the saslregexp patterns */
524 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
525 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE,
526 sr_strings, 0) == 0 )
530 if( i >= nSaslRegexp ) return( 0 );
533 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
534 * replace pattern of the form "x$1y$2z". The returned string needs
535 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
537 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
538 sr_strings, saslname, out, ctx );
541 LDAP_LOG( TRANSPORT, ENTRY,
542 "slap_sasl_regexp: converted SASL name to %s\n",
543 out->bv_len ? out->bv_val : "", 0, 0 );
545 Debug( LDAP_DEBUG_TRACE,
546 "slap_sasl_regexp: converted SASL name to %s\n",
547 out->bv_len ? out->bv_val : "", 0, 0 );
553 /* This callback actually does some work...*/
554 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
556 struct berval *ndn = o->o_callback->sc_private;
558 if (rs->sr_type != REP_SEARCH) return 0;
560 /* We only want to be called once */
562 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
567 LDAP_LOG( TRANSPORT, DETAIL1,
568 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
570 Debug( LDAP_DEBUG_TRACE,
571 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
576 ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
581 typedef struct smatch_info {
586 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
588 smatch_info *sm = o->o_callback->sc_private;
590 if (rs->sr_type != REP_SEARCH) return 0;
592 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
594 return -1; /* short-circuit the search */
601 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
602 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
603 * the rule must be used as an internal search for entries. If that search
604 * returns the *assertDN entry, the match is successful.
606 * The assertDN should not have the dn: prefix
610 int slap_sasl_match( Operation *opx, struct berval *rule,
611 struct berval *assertDN, struct berval *authc )
616 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
618 SlapReply rs = {REP_RESULT};
621 LDAP_LOG( TRANSPORT, ENTRY,
622 "slap_sasl_match: comparing DN %s to rule %s\n",
623 assertDN->bv_val, rule->bv_val,0 );
625 Debug( LDAP_DEBUG_TRACE,
626 "===>slap_sasl_match: comparing DN %s to rule %s\n",
627 assertDN->bv_val, rule->bv_val, 0 );
630 rc = slap_parseURI( opx, rule, &op.o_req_dn,
631 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
633 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
635 /* Massive shortcut: search scope == base */
636 switch ( op.oq_search.rs_scope ) {
637 case LDAP_SCOPE_BASE:
638 case LDAP_X_SCOPE_EXACT:
640 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
643 rc = LDAP_INAPPROPRIATE_AUTH;
647 case LDAP_X_SCOPE_CHILDREN:
648 case LDAP_X_SCOPE_SUBTREE:
649 case LDAP_X_SCOPE_ONELEVEL:
651 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
653 rc = LDAP_INAPPROPRIATE_AUTH;
655 if ( d == 0 && op.oq_search.rs_scope == LDAP_X_SCOPE_SUBTREE ) {
658 } else if ( d > 0 ) {
661 bv.bv_len = op.o_req_ndn.bv_len;
662 bv.bv_val = assertDN->bv_val + d;
664 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
665 switch ( op.oq_search.rs_scope ) {
666 case LDAP_X_SCOPE_SUBTREE:
667 case LDAP_X_SCOPE_CHILDREN:
671 case LDAP_X_SCOPE_ONELEVEL:
675 dnParent( assertDN, &pdn );
676 /* the common portion of the DN
677 * already matches, so only check
678 * if parent DN of assertedDN
679 * is all the pattern */
680 if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
686 /* at present, impossible */
694 case LDAP_X_SCOPE_REGEX:
695 rc = regcomp(®, op.o_req_ndn.bv_val,
696 REG_EXTENDED|REG_ICASE|REG_NOSUB);
698 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
704 rc = LDAP_INAPPROPRIATE_AUTH;
712 /* Must run an internal search. */
713 if ( op.oq_search.rs_filter == NULL ) {
714 rc = LDAP_FILTER_ERROR;
719 LDAP_LOG( TRANSPORT, DETAIL1,
720 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
721 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
723 Debug( LDAP_DEBUG_TRACE,
724 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
725 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
728 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
729 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
730 rc = LDAP_INAPPROPRIATE_AUTH;
738 op.o_tag = LDAP_REQ_SEARCH;
739 op.o_protocol = LDAP_VERSION3;
742 op.o_time = slap_get_time();
743 op.o_do_not_cache = 1;
744 op.o_is_auth_check = 1;
745 op.o_threadctx = opx->o_threadctx;
746 op.o_tmpmemctx = opx->o_tmpmemctx;
747 op.o_tmpmfuncs = opx->o_tmpmfuncs;
751 op.o_conn = opx->o_conn;
752 op.o_connid = opx->o_connid;
753 op.o_req_dn = op.o_req_ndn;
754 op.oq_search.rs_slimit = 1;
755 op.oq_search.rs_tlimit = -1;
756 op.o_sync_slog_size = -1;
758 op.o_bd->be_search( &op, &rs );
763 rc = LDAP_INAPPROPRIATE_AUTH;
767 if( op.o_req_dn.bv_val && op.o_req_dn.bv_val != op.o_req_ndn.bv_val ) ch_free( op.o_req_dn.bv_val );
768 if( op.o_req_ndn.bv_val ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
769 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
770 if( op.ors_filterstr.bv_val ) ch_free( op.ors_filterstr.bv_val );
773 LDAP_LOG( TRANSPORT, ENTRY,
774 "slap_sasl_match: comparison returned %d\n", rc, 0, 0 );
776 Debug( LDAP_DEBUG_TRACE,
777 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
785 * This function answers the question, "Can this ID authorize to that ID?",
786 * based on authorization rules. The rules are stored in the *searchDN, in the
787 * attribute named by *attr. If any of those rules map to the *assertDN, the
788 * authorization is approved.
790 * The DNs should not have the dn: prefix
793 slap_sasl_check_authz( Operation *op,
794 struct berval *searchDN,
795 struct berval *assertDN,
796 AttributeDescription *ad,
797 struct berval *authc )
803 LDAP_LOG( TRANSPORT, ENTRY,
804 "slap_sasl_check_authz: does %s match %s rule in %s?\n",
805 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
807 Debug( LDAP_DEBUG_TRACE,
808 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
809 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
812 rc = backend_attribute( op, NULL,
813 searchDN, ad, &vals );
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 /* Massive shortcut: search scope == base */
889 switch ( op.oq_search.rs_scope ) {
890 case LDAP_SCOPE_BASE:
891 case LDAP_X_SCOPE_EXACT:
892 *sasldn = op.o_req_ndn;
893 op.o_req_ndn.bv_len = 0;
894 op.o_req_ndn.bv_val = NULL;
895 /* intentionally continue to next case */
897 case LDAP_X_SCOPE_REGEX:
898 case LDAP_X_SCOPE_SUBTREE:
899 case LDAP_X_SCOPE_CHILDREN:
900 case LDAP_X_SCOPE_ONELEVEL:
901 /* correctly parsed, but illegal */
904 case LDAP_SCOPE_ONELEVEL:
905 case LDAP_SCOPE_SUBTREE:
906 #ifdef LDAP_SCOPE_SUBORDINATE
907 case LDAP_SCOPE_SUBORDINATE:
913 /* catch unhandled cases (there shouldn't be) */
918 LDAP_LOG( TRANSPORT, DETAIL1,
919 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
920 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
922 Debug( LDAP_DEBUG_TRACE,
923 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
924 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
927 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
931 op.o_conn = opx->o_conn;
932 op.o_connid = opx->o_connid;
933 op.o_tag = LDAP_REQ_SEARCH;
934 op.o_protocol = LDAP_VERSION3;
935 op.o_ndn = opx->o_conn->c_ndn;
937 op.o_time = slap_get_time();
938 op.o_do_not_cache = 1;
939 op.o_is_auth_check = 1;
940 op.o_threadctx = opx->o_threadctx;
941 op.o_tmpmemctx = opx->o_tmpmemctx;
942 op.o_tmpmfuncs = opx->o_tmpmfuncs;
946 op.oq_search.rs_deref = LDAP_DEREF_NEVER;
947 op.oq_search.rs_slimit = 1;
948 op.oq_search.rs_tlimit = -1;
949 op.oq_search.rs_attrsonly = 1;
950 op.o_req_dn = op.o_req_ndn;
952 op.o_bd->be_search( &op, &rs );
955 if( sasldn->bv_len ) {
956 opx->o_conn->c_authz_backend = op.o_bd;
958 if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
959 if( op.o_req_ndn.bv_len ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
960 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
961 if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
964 LDAP_LOG( TRANSPORT, ENTRY,
965 "slap_sasl2dn: Converted SASL name to %s\n",
966 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
968 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
969 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
976 /* Check if a bind can SASL authorize to another identity.
977 * The DNs should not have the dn: prefix
980 int slap_sasl_authorized( Operation *op,
981 struct berval *authcDN, struct berval *authzDN )
983 int rc = LDAP_INAPPROPRIATE_AUTH;
985 /* User binding as anonymous */
986 if ( authzDN == NULL ) {
992 LDAP_LOG( TRANSPORT, ENTRY,
993 "slap_sasl_authorized: can %s become %s?\n",
994 authcDN->bv_val, authzDN->bv_val, 0 );
996 Debug( LDAP_DEBUG_TRACE,
997 "==>slap_sasl_authorized: can %s become %s?\n",
998 authcDN->bv_val, authzDN->bv_val, 0 );
1001 /* If person is authorizing to self, succeed */
1002 if ( dn_match( authcDN, authzDN ) ) {
1007 /* Allow the manager to authorize as any DN. */
1008 if( op->o_conn->c_authz_backend &&
1009 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
1015 /* Check source rules */
1016 if( authz_policy & SASL_AUTHZ_TO ) {
1017 rc = slap_sasl_check_authz( op, authcDN, authzDN,
1018 slap_schema.si_ad_saslAuthzTo, authcDN );
1019 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
1024 /* Check destination rules */
1025 if( authz_policy & SASL_AUTHZ_FROM ) {
1026 rc = slap_sasl_check_authz( op, authzDN, authcDN,
1027 slap_schema.si_ad_saslAuthzFrom, authcDN );
1028 if( rc == LDAP_SUCCESS ) {
1033 rc = LDAP_INAPPROPRIATE_AUTH;
1038 LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );
1040 Debug( LDAP_DEBUG_TRACE,
1041 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );