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>
31 #define SASLREGEX_REPLACE 10
33 #define LDAP_X_SCOPE_EXACT ((ber_int_t) 0x0010)
34 #define LDAP_X_SCOPE_REGEX ((ber_int_t) 0x0020)
35 #define LDAP_X_SCOPE_CHILDREN ((ber_int_t) 0x0030)
36 #define LDAP_X_SCOPE_SUBTREE ((ber_int_t) 0x0040)
37 #define LDAP_X_SCOPE_ONELEVEL ((ber_int_t) 0x0050)
40 * IDs in DNauthzid form can now have a type specifier, that
41 * influences how they are used in related operations.
43 * syntax: dn[.{exact|regex}]:<val>
45 * dn.exact: the value must pass normalization and is used
47 * dn.regex: the value is treated as a regular expression
48 * in matching DN values in authz{To|From}
50 * dn: for backwards compatibility reasons, the value
51 * is treated as a regular expression, and thus
52 * it is not normalized nor validated; it is used
53 * in exact or regex comparisons based on the
56 * IDs in DNauthzid form can now have a type specifier, that
57 * influences how they are used in related operations.
59 * syntax: u[.mech[/realm]]:<val>
61 * where mech is a SIMPLE, AUTHZ, or a SASL mechanism name
62 * and realm is mechanism specific realm (separate to those
63 * which are representable as part of the principal).
66 typedef struct sasl_regexp {
67 char *sr_match; /* regexp match pattern */
68 char *sr_replace; /* regexp replace pattern */
69 regex_t sr_workspace; /* workspace for regexp engine */
70 int sr_offset[SASLREGEX_REPLACE+2]; /* offsets of $1,$2... in *replace */
73 static int nSaslRegexp = 0;
74 static SaslRegexp_t *SaslRegexp = NULL;
76 #ifdef SLAP_AUTH_REWRITE
78 struct rewrite_info *sasl_rwinfo = NULL;
79 #define AUTHID_CONTEXT "authid"
80 #endif /* SLAP_AUTH_REWRITE */
82 /* What SASL proxy authorization policies are allowed? */
83 #define SASL_AUTHZ_NONE 0x00
84 #define SASL_AUTHZ_FROM 0x01
85 #define SASL_AUTHZ_TO 0x02
86 #define SASL_AUTHZ_AND 0x10
88 static int authz_policy = SASL_AUTHZ_NONE;
90 int slap_sasl_setpolicy( const char *arg )
92 int rc = LDAP_SUCCESS;
94 if ( strcasecmp( arg, "none" ) == 0 ) {
95 authz_policy = SASL_AUTHZ_NONE;
96 } else if ( strcasecmp( arg, "from" ) == 0 ) {
97 authz_policy = SASL_AUTHZ_FROM;
98 } else if ( strcasecmp( arg, "to" ) == 0 ) {
99 authz_policy = SASL_AUTHZ_TO;
100 } else if ( strcasecmp( arg, "both" ) == 0 || strcasecmp( arg, "any" ) == 0 ) {
101 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO;
102 } else if ( strcasecmp( arg, "all" ) == 0 ) {
103 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND;
110 int slap_parse_user( struct berval *id, struct berval *user,
111 struct berval *realm, struct berval *mech )
116 assert( !BER_BVISNULL( id ) );
123 if ( u != 'u' && u != 'U' ) {
124 /* called with something other than u: */
125 return LDAP_PROTOCOL_ERROR;
129 * u[.mech[/realm]]:user
132 user->bv_val = strchr( id->bv_val, ':' );
133 if ( BER_BVISNULL( user ) ) {
134 return LDAP_PROTOCOL_ERROR;
136 user->bv_val[ 0 ] = '\0';
138 user->bv_len = id->bv_len - ( user->bv_val - id->bv_val );
140 mech->bv_val = strchr( id->bv_val, '.' );
141 if ( !BER_BVISNULL( mech ) ) {
142 mech->bv_val[ 0 ] = '\0';
145 realm->bv_val = strchr( mech->bv_val, '/' );
147 if ( !BER_BVISNULL( realm ) ) {
148 realm->bv_val[ 0 ] = '\0';
150 mech->bv_len = realm->bv_val - mech->bv_val - 1;
151 realm->bv_len = user->bv_val - realm->bv_val - 1;
153 mech->bv_len = user->bv_val - mech->bv_val - 1;
160 if ( id->bv_val[ 1 ] != '\0' ) {
161 return LDAP_PROTOCOL_ERROR;
164 if ( !BER_BVISNULL( mech ) ) {
165 assert( mech->bv_val == id->bv_val + 2 );
167 AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
171 if ( !BER_BVISNULL( realm ) ) {
172 assert( realm->bv_val >= id->bv_val + 2 );
174 AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
178 /* leave "u:" before user */
181 user->bv_val[ 0 ] = u;
182 user->bv_val[ 1 ] = ':';
187 static int slap_parseURI( Operation *op, struct berval *uri,
188 struct berval *base, struct berval *nbase,
189 int *scope, Filter **filter, struct berval *fstr )
195 assert( uri != NULL && !BER_BVISNULL( uri ) );
203 LDAP_LOG( TRANSPORT, ENTRY,
204 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
206 Debug( LDAP_DEBUG_TRACE,
207 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
210 rc = LDAP_PROTOCOL_ERROR;
211 if ( !strncasecmp( uri->bv_val, "dn", STRLENOF( "dn" ) ) ) {
212 bv.bv_val = uri->bv_val + STRLENOF( "dn" );
214 if ( bv.bv_val[ 0 ] == '.' ) {
217 if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
218 bv.bv_val += STRLENOF( "exact:" );
219 *scope = LDAP_X_SCOPE_EXACT;
221 } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
222 bv.bv_val += STRLENOF( "regex:" );
223 *scope = LDAP_X_SCOPE_REGEX;
225 } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "chldren:" ) ) ) {
226 bv.bv_val += STRLENOF( "children:" );
227 *scope = LDAP_X_SCOPE_CHILDREN;
229 } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
230 bv.bv_val += STRLENOF( "subtree:" );
231 *scope = LDAP_X_SCOPE_SUBTREE;
233 } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
234 bv.bv_val += STRLENOF( "onelevel:" );
235 *scope = LDAP_X_SCOPE_ONELEVEL;
238 return LDAP_PROTOCOL_ERROR;
242 if ( bv.bv_val[ 0 ] != ':' )
243 return LDAP_PROTOCOL_ERROR;
247 bv.bv_val += strspn( bv.bv_val, " " );
248 /* jump here in case no type specification was present
249 * and uir was not an URI... HEADS-UP: assuming EXACT */
250 is_dn: bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
253 case LDAP_X_SCOPE_EXACT:
254 case LDAP_X_SCOPE_CHILDREN:
255 case LDAP_X_SCOPE_SUBTREE:
256 case LDAP_X_SCOPE_ONELEVEL:
257 rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
258 if( rc != LDAP_SUCCESS ) {
263 case LDAP_X_SCOPE_REGEX:
264 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
275 } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
276 && ( uri->bv_val[ 1 ] == ':'
277 || uri->bv_val[ 1 ] == '/'
278 || uri->bv_val[ 1 ] == '.' ) )
280 Connection c = *op->o_conn;
281 char buf[ SLAP_LDAPDN_MAXLEN ];
287 if ( sizeof( buf ) <= uri->bv_len ) {
288 return LDAP_INVALID_SYNTAX;
291 id.bv_len = uri->bv_len;
293 strncpy( buf, uri->bv_val, sizeof( buf ) );
295 rc = slap_parse_user( &id, &user, &realm, &mech );
296 if ( rc != LDAP_SUCCESS ) {
300 if ( !BER_BVISNULL( &mech ) ) {
301 c.c_sasl_bind_mech = mech;
303 BER_BVSTR( &c.c_sasl_bind_mech, "AUTHZ" );
306 rc = slap_sasl_getdn( &c, op, &user,
307 realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
309 if ( rc == LDAP_SUCCESS ) {
310 *scope = LDAP_X_SCOPE_EXACT;
316 rc = ldap_url_parse( uri->bv_val, &ludp );
317 if ( rc == LDAP_URL_ERR_BADSCHEME ) {
318 /* last chance: assume it's a(n exact) DN ... */
319 bv.bv_val = uri->bv_val;
320 *scope = LDAP_X_SCOPE_EXACT;
324 if ( rc != LDAP_URL_SUCCESS ) {
325 return LDAP_PROTOCOL_ERROR;
328 if (( ludp->lud_host && *ludp->lud_host )
329 || ludp->lud_attrs || ludp->lud_exts )
331 /* host part must be empty */
332 /* attrs and extensions parts must be empty */
333 rc = LDAP_PROTOCOL_ERROR;
338 *scope = ludp->lud_scope;
340 /* Grab the filter */
341 if ( ludp->lud_filter ) {
342 *filter = str2filter_x( op, ludp->lud_filter );
343 if ( *filter == NULL ) {
344 rc = LDAP_PROTOCOL_ERROR;
347 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
350 /* Grab the searchbase */
351 ber_str2bv( ludp->lud_dn, 0, 0, base );
352 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
355 if( rc != LDAP_SUCCESS ) {
356 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 #ifdef SLAP_AUTH_REWRITE
410 int slap_sasl_rewrite_config(
420 /* init at first call */
421 if ( sasl_rwinfo == NULL ) {
422 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
425 /* strip "authid-" prefix for parsing */
427 argv[0] += STRLENOF( "authid-" );
428 rc = rewrite_parse( sasl_rwinfo, fname, lineno, argc, argv );
434 int slap_sasl_rewrite_destroy( void )
437 rewrite_info_delete( sasl_rwinfo );
444 int slap_sasl_regexp_rewrite_config(
449 const char *context )
452 char *newreplace, *p;
453 char *argvRule[] = { "rewriteRule", NULL, NULL, "@", NULL };
455 /* init at first call */
456 if ( sasl_rwinfo == NULL ) {
457 char *argvEngine[] = { "rewriteEngine", "on", NULL };
458 char *argvContext[] = { "rewriteContext", NULL, NULL };
459 char *argvFirstRule[] = { "rewriteRule", ".*",
460 "%{>" AUTHID_CONTEXT "(%0)}", ":", NULL };
462 /* initialize rewrite engine */
463 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
465 /* switch on rewrite engine */
466 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine );
467 if (rc != LDAP_SUCCESS) {
471 /* create generic authid context */
472 argvContext[1] = AUTHID_CONTEXT;
473 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvContext );
474 if (rc != LDAP_SUCCESS) {
479 newreplace = ch_strdup( replace );
481 for (p = strchr( newreplace, '$' ); p; p = strchr( p + 1, '$' ) ) {
482 if ( isdigit( p[1] ) ) {
489 argvRule[1] = (char *)match;
490 argvRule[2] = newreplace;
491 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule );
492 ch_free( newreplace );
496 #endif /* SLAP_AUTH_REWRITE */
498 int slap_sasl_regexp_config( const char *match, const char *replace )
500 #ifdef SLAP_AUTH_REWRITE
501 return slap_sasl_regexp_rewrite_config( "sasl-regexp", 0,
502 match, replace, AUTHID_CONTEXT );
503 #else /* ! SLAP_AUTH_REWRITE */
507 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
508 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
510 reg = &SaslRegexp[nSaslRegexp];
512 reg->sr_match = ch_strdup( match );
513 reg->sr_replace = ch_strdup( replace );
515 /* Precompile matching pattern */
516 rc = regcomp( ®->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
519 LDAP_LOG( TRANSPORT, ERR,
520 "slap_sasl_regexp_config: \"%s\" could not be compiled.\n",
521 reg->sr_match, 0, 0 );
523 Debug( LDAP_DEBUG_ANY,
524 "SASL match pattern %s could not be compiled by regexp engine\n",
525 reg->sr_match, 0, 0 );
528 return( LDAP_OTHER );
531 rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
532 if ( rc != LDAP_SUCCESS ) return rc;
535 return( LDAP_SUCCESS );
536 #endif /* ! SLAP_AUTH_REWRITE */
539 /* Perform replacement on regexp matches */
540 static void slap_sasl_rx_exp(
544 const char *saslname,
548 int i, n, len, insert;
550 /* Get the total length of the final URI */
554 while( off[n] >= 0 ) {
555 /* Len of next section from replacement string (x,y,z above) */
556 len += off[n] - off[n-1] - 2;
560 /* Len of string from saslname that matched next $i (b,d above) */
561 i = rep[ off[n] + 1 ] - '0';
562 len += str[i].rm_eo - str[i].rm_so;
565 out->bv_val = slap_sl_malloc( len + 1, ctx );
568 /* Fill in URI with replace string, replacing $i as we go */
571 while( off[n] >= 0) {
572 /* Paste in next section from replacement string (x,y,z above) */
573 len = off[n] - off[n-1] - 2;
574 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
579 /* Paste in string from saslname that matched next $i (b,d above) */
580 i = rep[ off[n] + 1 ] - '0';
581 len = str[i].rm_eo - str[i].rm_so;
582 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
588 out->bv_val[insert] = '\0';
591 /* Take the passed in SASL name and attempt to convert it into an
592 LDAP URI to find the matching LDAP entry, using the pattern matching
593 strings given in the saslregexp config file directive(s) */
595 static int slap_sasl_regexp( struct berval *in, struct berval *out,
596 int flags, void *ctx )
598 #ifdef SLAP_AUTH_REWRITE
599 const char *context = AUTHID_CONTEXT;
601 if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
605 /* FIXME: if aware of authc/authz mapping,
606 * we could use different contexts ... */
607 switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL,
610 case REWRITE_REGEXEC_OK:
611 if ( !BER_BVISNULL( out ) ) {
612 char *val = out->bv_val;
613 ber_str2bv_x( val, 0, 1, out, ctx );
616 ber_dupbv_x( out, in, ctx );
619 LDAP_LOG( BACK_LDAP, DETAIL1,
620 "[rw] %s: \"%s\" -> \"%s\"\n",
621 context, in->bv_val, out->bv_val );
622 #else /* !NEW_LOGGING */
623 Debug( LDAP_DEBUG_ARGS,
624 "[rw] %s: \"%s\" -> \"%s\"\n",
625 context, in->bv_val, out->bv_val );
626 #endif /* !NEW_LOGGING */
629 case REWRITE_REGEXEC_UNWILLING:
630 case REWRITE_REGEXEC_ERR:
635 #else /* ! SLAP_AUTH_REWRITE */
636 char *saslname = in->bv_val;
638 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
641 memset( out, 0, sizeof( *out ) );
644 LDAP_LOG( TRANSPORT, ENTRY,
645 "slap_sasl_regexp: converting SASL name %s\n", saslname, 0, 0 );
647 Debug( LDAP_DEBUG_TRACE, "slap_sasl_regexp: converting SASL name %s\n",
651 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
655 /* Match the normalized SASL name to the saslregexp patterns */
656 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
657 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE,
658 sr_strings, 0) == 0 )
662 if( i >= nSaslRegexp ) return( 0 );
665 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
666 * replace pattern of the form "x$1y$2z". The returned string needs
667 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
669 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
670 sr_strings, saslname, out, ctx );
673 LDAP_LOG( TRANSPORT, ENTRY,
674 "slap_sasl_regexp: converted SASL name to %s\n",
675 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
677 Debug( LDAP_DEBUG_TRACE,
678 "slap_sasl_regexp: converted SASL name to %s\n",
679 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
683 #endif /* ! SLAP_AUTH_REWRITE */
686 /* This callback actually does some work...*/
687 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
689 struct berval *ndn = o->o_callback->sc_private;
691 if (rs->sr_type != REP_SEARCH) return 0;
693 /* We only want to be called once */
694 if ( !BER_BVISNULL( ndn ) ) {
695 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
699 LDAP_LOG( TRANSPORT, DETAIL1,
700 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
702 Debug( LDAP_DEBUG_TRACE,
703 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
708 ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
713 typedef struct smatch_info {
718 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
720 smatch_info *sm = o->o_callback->sc_private;
722 if (rs->sr_type != REP_SEARCH) return 0;
724 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
726 return -1; /* short-circuit the search */
733 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
734 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
735 * the rule must be used as an internal search for entries. If that search
736 * returns the *assertDN entry, the match is successful.
738 * The assertDN should not have the dn: prefix
742 int slap_sasl_match( Operation *opx, struct berval *rule,
743 struct berval *assertDN, struct berval *authc )
748 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
750 SlapReply rs = {REP_RESULT};
753 LDAP_LOG( TRANSPORT, ENTRY,
754 "slap_sasl_match: comparing DN %s to rule %s\n",
755 assertDN->bv_val, rule->bv_val,0 );
757 Debug( LDAP_DEBUG_TRACE,
758 "===>slap_sasl_match: comparing DN %s to rule %s\n",
759 assertDN->bv_val, rule->bv_val, 0 );
762 rc = slap_parseURI( opx, rule, &op.o_req_dn,
763 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
765 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
767 /* Massive shortcut: search scope == base */
768 switch ( op.oq_search.rs_scope ) {
769 case LDAP_SCOPE_BASE:
770 case LDAP_X_SCOPE_EXACT:
772 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
775 rc = LDAP_INAPPROPRIATE_AUTH;
779 case LDAP_X_SCOPE_CHILDREN:
780 case LDAP_X_SCOPE_SUBTREE:
781 case LDAP_X_SCOPE_ONELEVEL:
783 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
785 rc = LDAP_INAPPROPRIATE_AUTH;
787 if ( d == 0 && op.oq_search.rs_scope == LDAP_X_SCOPE_SUBTREE ) {
790 } else if ( d > 0 ) {
793 /* leave room for at least one char of attributeType,
794 * one for '=' and one for ',' */
795 if ( d < STRLENOF( "x=,") ) {
799 bv.bv_len = op.o_req_ndn.bv_len;
800 bv.bv_val = assertDN->bv_val + d;
802 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
803 switch ( op.oq_search.rs_scope ) {
804 case LDAP_X_SCOPE_SUBTREE:
805 case LDAP_X_SCOPE_CHILDREN:
809 case LDAP_X_SCOPE_ONELEVEL:
813 dnParent( assertDN, &pdn );
814 /* the common portion of the DN
815 * already matches, so only check
816 * if parent DN of assertedDN
817 * is all the pattern */
818 if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
824 /* at present, impossible */
832 case LDAP_X_SCOPE_REGEX:
833 rc = regcomp(®, op.o_req_ndn.bv_val,
834 REG_EXTENDED|REG_ICASE|REG_NOSUB);
836 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
842 rc = LDAP_INAPPROPRIATE_AUTH;
850 /* Must run an internal search. */
851 if ( op.oq_search.rs_filter == NULL ) {
852 rc = LDAP_FILTER_ERROR;
857 LDAP_LOG( TRANSPORT, DETAIL1,
858 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
859 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
861 Debug( LDAP_DEBUG_TRACE,
862 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
863 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
866 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
867 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
868 rc = LDAP_INAPPROPRIATE_AUTH;
876 op.o_tag = LDAP_REQ_SEARCH;
877 op.o_protocol = LDAP_VERSION3;
880 op.o_time = slap_get_time();
881 op.o_do_not_cache = 1;
882 op.o_is_auth_check = 1;
883 op.o_threadctx = opx->o_threadctx;
884 op.o_tmpmemctx = opx->o_tmpmemctx;
885 op.o_tmpmfuncs = opx->o_tmpmfuncs;
889 op.o_conn = opx->o_conn;
890 op.o_connid = opx->o_connid;
891 op.o_req_dn = op.o_req_ndn;
892 op.oq_search.rs_slimit = 1;
893 op.oq_search.rs_tlimit = -1;
895 op.o_bd->be_search( &op, &rs );
900 rc = LDAP_INAPPROPRIATE_AUTH;
904 if( op.o_req_dn.bv_len ) ch_free( op.o_req_dn.bv_val );
905 if( op.o_req_ndn.bv_len ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
906 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
907 if( op.ors_filterstr.bv_len ) ch_free( op.ors_filterstr.bv_val );
910 LDAP_LOG( TRANSPORT, ENTRY,
911 "slap_sasl_match: comparison returned %d\n", rc, 0, 0 );
913 Debug( LDAP_DEBUG_TRACE,
914 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
922 * This function answers the question, "Can this ID authorize to that ID?",
923 * based on authorization rules. The rules are stored in the *searchDN, in the
924 * attribute named by *attr. If any of those rules map to the *assertDN, the
925 * authorization is approved.
927 * The DNs should not have the dn: prefix
930 slap_sasl_check_authz( Operation *op,
931 struct berval *searchDN,
932 struct berval *assertDN,
933 AttributeDescription *ad,
934 struct berval *authc )
940 LDAP_LOG( TRANSPORT, ENTRY,
941 "slap_sasl_check_authz: does %s match %s rule in %s?\n",
942 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
944 Debug( LDAP_DEBUG_TRACE,
945 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
946 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
949 rc = backend_attribute( op, NULL,
950 searchDN, ad, &vals );
951 if( rc != LDAP_SUCCESS ) goto COMPLETE;
953 /* Check if the *assertDN matches any **vals */
955 for( i=0; !BER_BVISNULL( &vals[i] ); i++ ) {
956 rc = slap_sasl_match( op, &vals[i], assertDN, authc );
957 if ( rc == LDAP_SUCCESS ) goto COMPLETE;
960 rc = LDAP_INAPPROPRIATE_AUTH;
963 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
966 LDAP_LOG( TRANSPORT, RESULTS,
967 "slap_sasl_check_authz: %s check returning %s\n",
968 ad->ad_cname.bv_val, rc, 0 );
970 Debug( LDAP_DEBUG_TRACE,
971 "<==slap_sasl_check_authz: %s check returning %d\n",
972 ad->ad_cname.bv_val, rc, 0);
979 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
980 * return the LDAP DN to which it matches. The SASL regexp rules in the config
981 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
982 * search with scope=base), just return the URI (or its searchbase). Otherwise
983 * an internal search must be done, and if that search returns exactly one
984 * entry, return the DN of that one entry.
986 void slap_sasl2dn( Operation *opx,
987 struct berval *saslname, struct berval *sasldn, int flags )
990 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
992 SlapReply rs = {REP_RESULT};
993 struct berval regout = BER_BVNULL;
996 LDAP_LOG( TRANSPORT, ENTRY,
997 "slap_sasl2dn: converting SASL name %s to DN.\n",
998 saslname->bv_val, 0, 0 );
1000 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1001 "converting SASL name %s to a DN\n",
1002 saslname->bv_val, 0,0 );
1005 sasldn->bv_val = NULL;
1007 cb.sc_private = sasldn;
1009 /* Convert the SASL name into a minimal URI */
1010 if( !slap_sasl_regexp( saslname, ®out, flags, opx->o_tmpmemctx ) ) {
1014 rc = slap_parseURI( opx, ®out, &op.o_req_dn,
1015 &op.o_req_ndn, &op.oq_search.rs_scope, &op.oq_search.rs_filter,
1016 &op.ors_filterstr );
1017 if ( !BER_BVISNULL( ®out ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
1018 if ( rc != LDAP_SUCCESS ) {
1022 /* Must do an internal search */
1023 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1025 /* Massive shortcut: search scope == base */
1026 switch ( op.oq_search.rs_scope ) {
1027 case LDAP_SCOPE_BASE:
1028 case LDAP_X_SCOPE_EXACT:
1029 *sasldn = op.o_req_ndn;
1030 BER_BVZERO( &op.o_req_ndn );
1031 /* intentionally continue to next case */
1033 case LDAP_X_SCOPE_REGEX:
1034 case LDAP_X_SCOPE_SUBTREE:
1035 case LDAP_X_SCOPE_CHILDREN:
1036 case LDAP_X_SCOPE_ONELEVEL:
1037 /* correctly parsed, but illegal */
1040 case LDAP_SCOPE_ONELEVEL:
1041 case LDAP_SCOPE_SUBTREE:
1042 #ifdef LDAP_SCOPE_SUBORDINATE
1043 case LDAP_SCOPE_SUBORDINATE:
1049 /* catch unhandled cases (there shouldn't be) */
1054 LDAP_LOG( TRANSPORT, DETAIL1,
1055 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1056 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
1058 Debug( LDAP_DEBUG_TRACE,
1059 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1060 op.o_req_ndn.bv_val, op.oq_search.rs_scope, 0 );
1063 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1067 op.o_conn = opx->o_conn;
1068 op.o_connid = opx->o_connid;
1069 op.o_tag = LDAP_REQ_SEARCH;
1070 op.o_protocol = LDAP_VERSION3;
1071 op.o_ndn = opx->o_conn->c_ndn;
1072 op.o_callback = &cb;
1073 op.o_time = slap_get_time();
1074 op.o_do_not_cache = 1;
1075 op.o_is_auth_check = 1;
1076 op.o_threadctx = opx->o_threadctx;
1077 op.o_tmpmemctx = opx->o_tmpmemctx;
1078 op.o_tmpmfuncs = opx->o_tmpmfuncs;
1080 op.o_pb = opx->o_pb;
1082 op.oq_search.rs_deref = LDAP_DEREF_NEVER;
1083 op.oq_search.rs_slimit = 1;
1084 op.oq_search.rs_tlimit = -1;
1085 op.oq_search.rs_attrsonly = 1;
1086 /* use req_ndn as req_dn instead of non-pretty base of uri */
1087 if( !BER_BVISNULL( &op.o_req_dn ) ) ch_free( op.o_req_dn.bv_val );
1088 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1090 op.o_bd->be_search( &op, &rs );
1093 if( !BER_BVISEMPTY( sasldn ) ) {
1094 opx->o_conn->c_authz_backend = op.o_bd;
1096 if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1097 if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1098 if( op.oq_search.rs_filter ) filter_free_x( opx, op.oq_search.rs_filter );
1099 if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1102 LDAP_LOG( TRANSPORT, ENTRY,
1103 "slap_sasl2dn: Converted SASL name to %s\n",
1104 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
1106 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
1107 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
1114 /* Check if a bind can SASL authorize to another identity.
1115 * The DNs should not have the dn: prefix
1118 int slap_sasl_authorized( Operation *op,
1119 struct berval *authcDN, struct berval *authzDN )
1121 int rc = LDAP_INAPPROPRIATE_AUTH;
1123 /* User binding as anonymous */
1124 if ( authzDN == NULL ) {
1130 LDAP_LOG( TRANSPORT, ENTRY,
1131 "slap_sasl_authorized: can %s become %s?\n",
1132 authcDN->bv_val, authzDN->bv_val, 0 );
1134 Debug( LDAP_DEBUG_TRACE,
1135 "==>slap_sasl_authorized: can %s become %s?\n",
1136 authcDN->bv_val, authzDN->bv_val, 0 );
1139 /* If person is authorizing to self, succeed */
1140 if ( dn_match( authcDN, authzDN ) ) {
1145 /* Allow the manager to authorize as any DN. */
1146 if( op->o_conn->c_authz_backend &&
1147 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
1153 /* Check source rules */
1154 if( authz_policy & SASL_AUTHZ_TO ) {
1155 rc = slap_sasl_check_authz( op, authcDN, authzDN,
1156 slap_schema.si_ad_saslAuthzTo, authcDN );
1157 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
1162 /* Check destination rules */
1163 if( authz_policy & SASL_AUTHZ_FROM ) {
1164 rc = slap_sasl_check_authz( op, authzDN, authcDN,
1165 slap_schema.si_ad_saslAuthzFrom, authcDN );
1166 if( rc == LDAP_SUCCESS ) {
1171 rc = LDAP_INAPPROPRIATE_AUTH;
1176 LDAP_LOG( TRANSPORT, RESULTS, "slap_sasl_authorized: return %d\n", rc,0,0 );
1178 Debug( LDAP_DEBUG_TRACE,
1179 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );