2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1998-2005 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 0 /* leave this for later releases */
318 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
322 return LDAP_PROTOCOL_ERROR;
327 case LDAP_URL_ERR_BADSCHEME:
328 /* last chance: assume it's a(n exact) DN ... */
329 bv.bv_val = uri->bv_val;
330 *scope = LDAP_X_SCOPE_EXACT;
334 return LDAP_PROTOCOL_ERROR;
337 if ( ( ludp->lud_host && *ludp->lud_host )
338 || ludp->lud_attrs || ludp->lud_exts )
340 /* host part must be empty */
341 /* attrs and extensions parts must be empty */
342 rc = LDAP_PROTOCOL_ERROR;
347 *scope = ludp->lud_scope;
349 /* Grab the filter */
350 if ( ludp->lud_filter ) {
351 *filter = str2filter_x( op, ludp->lud_filter );
352 if ( *filter == NULL ) {
353 rc = LDAP_PROTOCOL_ERROR;
356 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
359 /* Grab the searchbase */
360 ber_str2bv( ludp->lud_dn, 0, 0, base );
361 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
364 if( rc != LDAP_SUCCESS ) {
365 if( *filter ) filter_free_x( op, *filter );
371 /* Don't free these, return them to caller */
372 ludp->lud_filter = NULL;
376 ldap_free_urldesc( ludp );
380 static int slap_sasl_rx_off(char *rep, int *off)
385 /* Precompile replace pattern. Find the $<n> placeholders */
388 for ( c = rep; *c; c++ ) {
389 if ( *c == '\\' && c[1] ) {
394 if ( n == SASLREGEX_REPLACE ) {
396 LDAP_LOG( TRANSPORT, ERR,
397 "slap_sasl_rx_off: \"%s\" has too many $n "
398 "placeholders (max %d)\n", rep, SASLREGEX_REPLACE, 0 );
400 Debug( LDAP_DEBUG_ANY,
401 "SASL replace pattern %s has too many $n "
402 "placeholders (max %d)\n",
403 rep, SASLREGEX_REPLACE, 0 );
406 return( LDAP_OTHER );
413 /* Final placeholder, after the last $n */
417 return( LDAP_SUCCESS );
420 int slap_sasl_regexp_config( const char *match, const char *replace )
425 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
426 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
428 reg = &SaslRegexp[nSaslRegexp];
430 reg->sr_match = ch_strdup( match );
431 reg->sr_replace = ch_strdup( replace );
433 /* Precompile matching pattern */
434 rc = regcomp( ®->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
437 LDAP_LOG( TRANSPORT, ERR,
438 "slap_sasl_regexp_config: \"%s\" could not be compiled.\n",
439 reg->sr_match, 0, 0 );
441 Debug( LDAP_DEBUG_ANY,
442 "SASL match pattern %s could not be compiled by regexp engine\n",
443 reg->sr_match, 0, 0 );
446 return( LDAP_OTHER );
449 rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
450 if ( rc != LDAP_SUCCESS ) return rc;
453 return( LDAP_SUCCESS );
457 /* Perform replacement on regexp matches */
458 static void slap_sasl_rx_exp(
462 const char *saslname,
466 int i, n, len, insert;
468 /* Get the total length of the final URI */
472 while( off[n] >= 0 ) {
473 /* Len of next section from replacement string (x,y,z above) */
474 len += off[n] - off[n-1] - 2;
478 /* Len of string from saslname that matched next $i (b,d above) */
479 i = rep[ off[n] + 1 ] - '0';
480 len += str[i].rm_eo - str[i].rm_so;
483 out->bv_val = sl_malloc( len + 1, ctx );
486 /* Fill in URI with replace string, replacing $i as we go */
489 while( off[n] >= 0) {
490 /* Paste in next section from replacement string (x,y,z above) */
491 len = off[n] - off[n-1] - 2;
492 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
497 /* Paste in string from saslname that matched next $i (b,d above) */
498 i = rep[ off[n] + 1 ] - '0';
499 len = str[i].rm_eo - str[i].rm_so;
500 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
506 out->bv_val[insert] = '\0';
509 /* Take the passed in SASL name and attempt to convert it into an
510 LDAP URI to find the matching LDAP entry, using the pattern matching
511 strings given in the saslregexp config file directive(s) */
513 static int slap_sasl_regexp( struct berval *in, struct berval *out,
514 int flags, void *ctx )
516 char *saslname = in->bv_val;
518 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
521 memset( out, 0, sizeof( *out ) );
524 LDAP_LOG( TRANSPORT, ENTRY,
525 "slap_sasl_regexp: converting SASL name %s\n", saslname, 0, 0 );
527 Debug( LDAP_DEBUG_TRACE, "slap_sasl_regexp: converting SASL name %s\n",
531 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
535 /* Match the normalized SASL name to the saslregexp patterns */
536 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
537 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE,
538 sr_strings, 0) == 0 )
542 if( i >= nSaslRegexp ) return( 0 );
545 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
546 * replace pattern of the form "x$1y$2z". The returned string needs
547 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
549 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
550 sr_strings, saslname, out, ctx );
553 LDAP_LOG( TRANSPORT, ENTRY,
554 "slap_sasl_regexp: converted SASL name to %s\n",
555 out->bv_len ? out->bv_val : "", 0, 0 );
557 Debug( LDAP_DEBUG_TRACE,
558 "slap_sasl_regexp: converted SASL name to %s\n",
559 out->bv_len ? out->bv_val : "", 0, 0 );
565 /* This callback actually does some work...*/
566 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
568 struct berval *ndn = o->o_callback->sc_private;
570 if (rs->sr_type != REP_SEARCH) return 0;
572 /* We only want to be called once */
574 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
579 LDAP_LOG( TRANSPORT, DETAIL1,
580 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
582 Debug( LDAP_DEBUG_TRACE,
583 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
588 ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
593 typedef struct smatch_info {
598 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
600 smatch_info *sm = o->o_callback->sc_private;
602 if (rs->sr_type != REP_SEARCH) return 0;
604 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
606 return -1; /* short-circuit the search */
613 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
614 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
615 * the rule must be used as an internal search for entries. If that search
616 * returns the *assertDN entry, the match is successful.
618 * The assertDN should not have the dn: prefix
622 int slap_sasl_match( Operation *opx, struct berval *rule,
623 struct berval *assertDN, struct berval *authc )
628 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
630 SlapReply rs = {REP_RESULT};
633 LDAP_LOG( TRANSPORT, ENTRY,
634 "slap_sasl_match: comparing DN %s to rule %s\n",
635 assertDN->bv_val, rule->bv_val,0 );
637 Debug( LDAP_DEBUG_TRACE,
638 "===>slap_sasl_match: comparing DN %s to rule %s\n",
639 assertDN->bv_val, rule->bv_val, 0 );
642 rc = slap_parseURI( opx, rule, &op.o_req_dn,
643 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
645 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
647 switch ( op.oq_search.rs_scope ) {
648 case LDAP_X_SCOPE_EXACT:
650 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
653 rc = LDAP_INAPPROPRIATE_AUTH;
657 case LDAP_X_SCOPE_CHILDREN:
658 case LDAP_X_SCOPE_SUBTREE:
659 case LDAP_X_SCOPE_ONELEVEL:
661 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
663 rc = LDAP_INAPPROPRIATE_AUTH;
665 if ( d == 0 && op.oq_search.rs_scope == LDAP_X_SCOPE_SUBTREE ) {
668 } else if ( d > 0 ) {
671 bv.bv_len = op.o_req_ndn.bv_len;
672 bv.bv_val = assertDN->bv_val + d;
674 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
675 switch ( op.oq_search.rs_scope ) {
676 case LDAP_X_SCOPE_SUBTREE:
677 case LDAP_X_SCOPE_CHILDREN:
681 case LDAP_X_SCOPE_ONELEVEL:
685 dnParent( assertDN, &pdn );
686 /* the common portion of the DN
687 * already matches, so only check
688 * if parent DN of assertedDN
689 * is all the pattern */
690 if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
696 /* at present, impossible */
704 case LDAP_X_SCOPE_REGEX:
705 rc = regcomp(®, op.o_req_ndn.bv_val,
706 REG_EXTENDED|REG_ICASE|REG_NOSUB);
708 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
714 rc = LDAP_INAPPROPRIATE_AUTH;
722 /* Must run an internal search. */
723 if ( op.oq_search.rs_filter == NULL ) {
724 rc = LDAP_FILTER_ERROR;
729 LDAP_LOG( TRANSPORT, DETAIL1,
730 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
731 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
733 Debug( LDAP_DEBUG_TRACE,
734 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
735 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
738 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
739 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
740 rc = LDAP_INAPPROPRIATE_AUTH;
748 op.o_tag = LDAP_REQ_SEARCH;
749 op.o_protocol = LDAP_VERSION3;
752 op.o_time = slap_get_time();
753 op.o_do_not_cache = 1;
754 op.o_is_auth_check = 1;
755 op.o_threadctx = opx->o_threadctx;
756 op.o_tmpmemctx = opx->o_tmpmemctx;
757 op.o_tmpmfuncs = opx->o_tmpmfuncs;
761 op.o_conn = opx->o_conn;
762 op.o_connid = opx->o_connid;
763 /* use req_ndn as req_dn instead of non-pretty base of uri */
764 if( !BER_BVISNULL( &op.o_req_dn ) ) ch_free( op.o_req_dn.bv_val );
765 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
766 op.oq_search.rs_slimit = 1;
767 op.oq_search.rs_tlimit = SLAP_NO_LIMIT;
768 op.o_sync_slog_size = -1;
770 op.o_bd->be_search( &op, &rs );
775 rc = LDAP_INAPPROPRIATE_AUTH;
779 if( !BER_BVISNULL( &op.o_req_dn ) ) sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
780 if( !BER_BVISNULL( &op.o_req_ndn ) ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
781 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
782 if( op.ors_filterstr.bv_val ) ch_free( op.ors_filterstr.bv_val );
785 LDAP_LOG( TRANSPORT, ENTRY,
786 "slap_sasl_match: comparison returned %d\n", rc, 0, 0 );
788 Debug( LDAP_DEBUG_TRACE,
789 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
797 * This function answers the question, "Can this ID authorize to that ID?",
798 * based on authorization rules. The rules are stored in the *searchDN, in the
799 * attribute named by *attr. If any of those rules map to the *assertDN, the
800 * authorization is approved.
802 * The DNs should not have the dn: prefix
805 slap_sasl_check_authz( Operation *op,
806 struct berval *searchDN,
807 struct berval *assertDN,
808 AttributeDescription *ad,
809 struct berval *authc )
815 LDAP_LOG( TRANSPORT, ENTRY,
816 "slap_sasl_check_authz: does %s match %s rule in %s?\n",
817 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
819 Debug( LDAP_DEBUG_TRACE,
820 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
821 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
824 rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
825 if( rc != LDAP_SUCCESS ) goto COMPLETE;
827 /* Check if the *assertDN matches any **vals */
829 for( i=0; vals[i].bv_val != NULL; i++ ) {
830 rc = slap_sasl_match( op, &vals[i], assertDN, authc );
831 if ( rc == LDAP_SUCCESS ) goto COMPLETE;
834 rc = LDAP_INAPPROPRIATE_AUTH;
837 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
840 LDAP_LOG( TRANSPORT, RESULTS,
841 "slap_sasl_check_authz: %s check returning %s\n",
842 ad->ad_cname.bv_val, rc, 0 );
844 Debug( LDAP_DEBUG_TRACE,
845 "<==slap_sasl_check_authz: %s check returning %d\n",
846 ad->ad_cname.bv_val, rc, 0);
853 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
854 * return the LDAP DN to which it matches. The SASL regexp rules in the config
855 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
856 * search with scope=base), just return the URI (or its searchbase). Otherwise
857 * an internal search must be done, and if that search returns exactly one
858 * entry, return the DN of that one entry.
860 void slap_sasl2dn( Operation *opx,
861 struct berval *saslname, struct berval *sasldn, int flags )
864 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
866 SlapReply rs = {REP_RESULT};
867 struct berval regout = BER_BVNULL;
870 LDAP_LOG( TRANSPORT, ENTRY,
871 "slap_sasl2dn: converting SASL name %s to DN.\n",
872 saslname->bv_val, 0, 0 );
874 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
875 "converting SASL name %s to a DN\n",
876 saslname->bv_val, 0,0 );
879 sasldn->bv_val = NULL;
881 cb.sc_private = sasldn;
883 /* Convert the SASL name into a minimal URI */
884 if( !slap_sasl_regexp( saslname, ®out, flags, opx->o_tmpmemctx ) ) {
888 rc = slap_parseURI( opx, ®out, &op.o_req_dn,
889 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
891 if( regout.bv_val ) sl_free( regout.bv_val, opx->o_tmpmemctx );
892 if( rc != LDAP_SUCCESS ) {
896 /* Must do an internal search */
897 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
899 switch ( op.oq_search.rs_scope ) {
900 case LDAP_X_SCOPE_EXACT:
901 *sasldn = op.o_req_ndn;
902 op.o_req_ndn.bv_len = 0;
903 op.o_req_ndn.bv_val = NULL;
904 /* intentionally continue to next case */
906 case LDAP_X_SCOPE_REGEX:
907 case LDAP_X_SCOPE_SUBTREE:
908 case LDAP_X_SCOPE_CHILDREN:
909 case LDAP_X_SCOPE_ONELEVEL:
910 /* correctly parsed, but illegal */
913 case LDAP_SCOPE_BASE:
914 case LDAP_SCOPE_ONELEVEL:
915 case LDAP_SCOPE_SUBTREE:
916 #ifdef LDAP_SCOPE_SUBORDINATE
917 case LDAP_SCOPE_SUBORDINATE:
923 /* catch unhandled cases (there shouldn't be) */
928 LDAP_LOG( TRANSPORT, DETAIL1,
929 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
930 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
932 Debug( LDAP_DEBUG_TRACE,
933 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
934 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
937 if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
941 /* Must run an internal search. */
942 if ( op.ors_filter == NULL ) {
943 rc = LDAP_FILTER_ERROR;
947 op.o_conn = opx->o_conn;
948 op.o_connid = opx->o_connid;
949 op.o_tag = LDAP_REQ_SEARCH;
950 op.o_protocol = LDAP_VERSION3;
951 op.o_ndn = opx->o_conn->c_ndn;
953 op.o_time = slap_get_time();
954 op.o_do_not_cache = 1;
955 op.o_is_auth_check = 1;
956 op.o_threadctx = opx->o_threadctx;
957 op.o_tmpmemctx = opx->o_tmpmemctx;
958 op.o_tmpmfuncs = opx->o_tmpmfuncs;
962 op.oq_search.rs_deref = LDAP_DEREF_NEVER;
963 op.oq_search.rs_slimit = 1;
964 op.oq_search.rs_tlimit = SLAP_NO_LIMIT;
965 op.oq_search.rs_attrsonly = 1;
966 /* use req_ndn as req_dn instead of non-pretty base of uri */
967 if( !BER_BVISNULL( &op.o_req_dn ) ) ch_free( op.o_req_dn.bv_val );
968 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
970 op.o_bd->be_search( &op, &rs );
973 if( sasldn->bv_len ) {
974 opx->o_conn->c_authz_backend = op.o_bd;
976 if( !BER_BVISNULL( &op.o_req_dn ) ) sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
977 if( !BER_BVISNULL( &op.o_req_ndn ) ) sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
978 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
979 if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
982 LDAP_LOG( TRANSPORT, ENTRY,
983 "slap_sasl2dn: Converted SASL name to %s\n",
984 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
986 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
987 sasldn->bv_len ? sasldn->bv_val : "<nothing>", 0, 0 );
994 /* Check if a bind can SASL authorize to another identity.
995 * The DNs should not have the dn: prefix
998 int slap_sasl_authorized( Operation *op,
999 struct berval *authcDN, struct berval *authzDN )
1001 int rc = LDAP_INAPPROPRIATE_AUTH;
1003 /* User binding as anonymous */
1004 if ( authzDN == NULL ) {
1010 LDAP_LOG( TRANSPORT, ENTRY,
1011 "slap_sasl_authorized: can %s become %s?\n",
1012 authcDN->bv_val, authzDN->bv_val, 0 );
1014 Debug( LDAP_DEBUG_TRACE,
1015 "==>slap_sasl_authorized: can %s become %s?\n",
1016 authcDN->bv_val, authzDN->bv_val, 0 );
1019 /* If person is authorizing to self, succeed */
1020 if ( dn_match( authcDN, authzDN ) ) {
1025 /* Allow the manager to authorize as any DN. */
1026 if( op->o_conn->c_authz_backend &&
1027 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
1033 /* Check source rules */
1034 if( authz_policy & SASL_AUTHZ_TO ) {
1035 rc = slap_sasl_check_authz( op, authcDN, authzDN,
1036 slap_schema.si_ad_saslAuthzTo, authcDN );
1037 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
1042 /* Check destination rules */
1043 if( authz_policy & SASL_AUTHZ_FROM ) {
1044 rc = slap_sasl_check_authz( op, authzDN, authcDN,
1045 slap_schema.si_ad_saslAuthzFrom, authcDN );
1046 if( rc == LDAP_SUCCESS ) {
1051 rc = LDAP_INAPPROPRIATE_AUTH;
1056 LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );
1058 Debug( LDAP_DEBUG_TRACE,
1059 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );