2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1998-2006 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>.
24 #include <ac/stdlib.h>
25 #include <ac/string.h>
32 #define SASLREGEX_REPLACE 10
34 #define LDAP_X_SCOPE_EXACT ((ber_int_t) 0x0010)
35 #define LDAP_X_SCOPE_REGEX ((ber_int_t) 0x0020)
36 #define LDAP_X_SCOPE_CHILDREN ((ber_int_t) 0x0030)
37 #define LDAP_X_SCOPE_SUBTREE ((ber_int_t) 0x0040)
38 #define LDAP_X_SCOPE_ONELEVEL ((ber_int_t) 0x0050)
39 #define LDAP_X_SCOPE_GROUP ((ber_int_t) 0x0060)
40 #define LDAP_X_SCOPE_USERS ((ber_int_t) 0x0070)
43 * IDs in DNauthzid form can now have a type specifier, that
44 * influences how they are used in related operations.
46 * syntax: dn[.{exact|regex}]:<val>
48 * dn.exact: the value must pass normalization and is used
50 * dn.regex: the value is treated as a regular expression
51 * in matching DN values in authz{To|From}
53 * dn: for backwards compatibility reasons, the value
54 * is treated as a regular expression, and thus
55 * it is not normalized nor validated; it is used
56 * in exact or regex comparisons based on the
59 * IDs in DNauthzid form can now have a type specifier, that
60 * influences how they are used in related operations.
62 * syntax: u[.mech[/realm]]:<val>
64 * where mech is a SIMPLE, AUTHZ, or a SASL mechanism name
65 * and realm is mechanism specific realm (separate to those
66 * which are representable as part of the principal).
69 typedef struct sasl_regexp {
70 char *sr_match; /* regexp match pattern */
71 char *sr_replace; /* regexp replace pattern */
72 regex_t sr_workspace; /* workspace for regexp engine */
73 int sr_offset[SASLREGEX_REPLACE+2]; /* offsets of $1,$2... in *replace */
76 static int nSaslRegexp = 0;
77 static SaslRegexp_t *SaslRegexp = NULL;
79 #ifdef SLAP_AUTH_REWRITE
81 struct rewrite_info *sasl_rwinfo = NULL;
82 #define AUTHID_CONTEXT "authid"
83 #endif /* SLAP_AUTH_REWRITE */
85 /* What SASL proxy authorization policies are allowed? */
86 #define SASL_AUTHZ_NONE 0x00
87 #define SASL_AUTHZ_FROM 0x01
88 #define SASL_AUTHZ_TO 0x02
89 #define SASL_AUTHZ_AND 0x10
91 static const char *policy_txt[] = {
92 "none", "from", "to", "any"
95 static int authz_policy = SASL_AUTHZ_NONE;
98 slap_sasl_match( Operation *opx, struct berval *rule,
99 struct berval *assertDN, struct berval *authc );
101 int slap_sasl_setpolicy( const char *arg )
103 int rc = LDAP_SUCCESS;
105 if ( strcasecmp( arg, "none" ) == 0 ) {
106 authz_policy = SASL_AUTHZ_NONE;
107 } else if ( strcasecmp( arg, "from" ) == 0 ) {
108 authz_policy = SASL_AUTHZ_FROM;
109 } else if ( strcasecmp( arg, "to" ) == 0 ) {
110 authz_policy = SASL_AUTHZ_TO;
111 } else if ( strcasecmp( arg, "both" ) == 0 || strcasecmp( arg, "any" ) == 0 ) {
112 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO;
113 } else if ( strcasecmp( arg, "all" ) == 0 ) {
114 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND;
121 const char * slap_sasl_getpolicy()
123 if ( authz_policy == (SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND) )
126 return policy_txt[authz_policy];
129 int slap_parse_user( struct berval *id, struct berval *user,
130 struct berval *realm, struct berval *mech )
134 assert( id != NULL );
135 assert( !BER_BVISNULL( id ) );
136 assert( user != NULL );
137 assert( realm != NULL );
138 assert( mech != NULL );
142 if ( u != 'u' && u != 'U' ) {
143 /* called with something other than u: */
144 return LDAP_PROTOCOL_ERROR;
148 * u[.mech[/realm]]:user
151 user->bv_val = ber_bvchr( id, ':' );
152 if ( BER_BVISNULL( user ) ) {
153 return LDAP_PROTOCOL_ERROR;
155 user->bv_val[ 0 ] = '\0';
157 user->bv_len = id->bv_len - ( user->bv_val - id->bv_val );
159 mech->bv_val = ber_bvchr( id, '.' );
160 if ( !BER_BVISNULL( mech ) ) {
161 mech->bv_val[ 0 ] = '\0';
163 mech->bv_len = user->bv_val - mech->bv_val - 1;
165 realm->bv_val = ber_bvchr( mech, '/' );
167 if ( !BER_BVISNULL( realm ) ) {
168 realm->bv_val[ 0 ] = '\0';
170 mech->bv_len = realm->bv_val - mech->bv_val - 1;
171 realm->bv_len = user->bv_val - realm->bv_val - 1;
178 if ( id->bv_val[ 1 ] != '\0' ) {
179 return LDAP_PROTOCOL_ERROR;
182 if ( !BER_BVISNULL( mech ) ) {
183 assert( mech->bv_val == id->bv_val + 2 );
185 AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
189 if ( !BER_BVISNULL( realm ) ) {
190 assert( realm->bv_val >= id->bv_val + 2 );
192 AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
196 /* leave "u:" before user */
199 user->bv_val[ 0 ] = u;
200 user->bv_val[ 1 ] = ':';
205 #ifdef SLAP_AUTHZ_SYNTAX
212 int rc = LDAP_INVALID_SYNTAX;
213 LDAPURLDesc *ludp = NULL;
218 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
219 * 3) dn.regex:<pattern>
220 * 4) u[.mech[/realm]]:<ID>
221 * 5) group[/<groupClass>[/<memberAttr>]]:<DN>
225 assert( in != NULL );
226 assert( !BER_BVISNULL( in ) );
228 Debug( LDAP_DEBUG_TRACE,
229 "authzValidate: parsing %s\n", in->bv_val, 0, 0 );
232 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
233 * 3) dn.regex:<pattern>
235 * <DN> must pass DN normalization
237 if ( !strncasecmp( in->bv_val, "dn", STRLENOF( "dn" ) ) ) {
238 bv.bv_val = in->bv_val + STRLENOF( "dn" );
240 if ( bv.bv_val[ 0 ] == '.' ) {
243 if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
244 bv.bv_val += STRLENOF( "exact:" );
245 scope = LDAP_X_SCOPE_EXACT;
247 } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
248 bv.bv_val += STRLENOF( "regex:" );
249 scope = LDAP_X_SCOPE_REGEX;
251 } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
252 bv.bv_val += STRLENOF( "children:" );
253 scope = LDAP_X_SCOPE_CHILDREN;
255 } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
256 bv.bv_val += STRLENOF( "subtree:" );
257 scope = LDAP_X_SCOPE_SUBTREE;
259 } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
260 bv.bv_val += STRLENOF( "onelevel:" );
261 scope = LDAP_X_SCOPE_ONELEVEL;
264 return LDAP_INVALID_SYNTAX;
268 if ( bv.bv_val[ 0 ] != ':' ) {
269 return LDAP_INVALID_SYNTAX;
271 scope = LDAP_X_SCOPE_EXACT;
275 bv.bv_val += strspn( bv.bv_val, " " );
276 /* jump here in case no type specification was present
277 * and uri was not an URI... HEADS-UP: assuming EXACT */
278 is_dn: bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val );
280 /* a single '*' means any DN without using regexes */
281 if ( ber_bvccmp( &bv, '*' ) ) {
282 /* LDAP_X_SCOPE_USERS */
287 case LDAP_X_SCOPE_EXACT:
288 case LDAP_X_SCOPE_CHILDREN:
289 case LDAP_X_SCOPE_SUBTREE:
290 case LDAP_X_SCOPE_ONELEVEL:
291 return dnValidate( NULL, &bv );
293 case LDAP_X_SCOPE_REGEX:
300 * 4) u[.mech[/realm]]:<ID>
302 } else if ( ( in->bv_val[ 0 ] == 'u' || in->bv_val[ 0 ] == 'U' )
303 && ( in->bv_val[ 1 ] == ':'
304 || in->bv_val[ 1 ] == '/'
305 || in->bv_val[ 1 ] == '.' ) )
307 char buf[ SLAP_LDAPDN_MAXLEN ];
313 if ( sizeof( buf ) <= in->bv_len ) {
314 return LDAP_INVALID_SYNTAX;
317 id.bv_len = in->bv_len;
319 strncpy( buf, in->bv_val, sizeof( buf ) );
321 rc = slap_parse_user( &id, &user, &realm, &mech );
322 if ( rc != LDAP_SUCCESS ) {
323 return LDAP_INVALID_SYNTAX;
329 * 5) group[/groupClass[/memberAttr]]:<DN>
331 * <groupClass> defaults to "groupOfNames"
332 * <memberAttr> defaults to "member"
334 * <DN> must pass DN normalization
336 } else if ( strncasecmp( in->bv_val, "group", STRLENOF( "group" ) ) == 0 )
338 struct berval group_dn = BER_BVNULL,
339 group_oc = BER_BVNULL,
340 member_at = BER_BVNULL;
342 bv.bv_val = in->bv_val + STRLENOF( "group" );
343 bv.bv_len = in->bv_len - STRLENOF( "group" );
344 group_dn.bv_val = ber_bvchr( &bv, ':' );
345 if ( group_dn.bv_val == NULL ) {
346 /* last chance: assume it's a(n exact) DN ... */
347 bv.bv_val = in->bv_val;
348 scope = LDAP_X_SCOPE_EXACT;
353 * FIXME: we assume that "member" and "groupOfNames"
354 * are present in schema...
356 if ( bv.bv_val[ 0 ] == '/' ) {
357 group_oc.bv_val = &bv.bv_val[ 1 ];
358 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
360 member_at.bv_val = ber_bvchr( &group_oc, '/' );
361 if ( member_at.bv_val ) {
362 AttributeDescription *ad = NULL;
363 const char *text = NULL;
365 group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
367 member_at.bv_len = group_dn.bv_val - member_at.bv_val;
368 rc = slap_bv2ad( &member_at, &ad, &text );
369 if ( rc != LDAP_SUCCESS ) {
374 if ( oc_bvfind( &group_oc ) == NULL ) {
375 return LDAP_INVALID_SYNTAX;
380 group_dn.bv_len = in->bv_len - ( group_dn.bv_val - in->bv_val );
382 rc = dnValidate( NULL, &group_dn );
383 if ( rc != LDAP_SUCCESS ) {
391 * ldap:///<base>??<scope>?<filter>
392 * <scope> ::= {base|one|subtree}
394 * <scope> defaults to "base"
395 * <base> must pass DN normalization
396 * <filter> must pass str2filter()
398 rc = ldap_url_parse( in->bv_val, &ludp );
400 case LDAP_URL_SUCCESS:
401 /* FIXME: the check is pedantic, but I think it's necessary,
402 * because people tend to use things like ldaps:// which
403 * gives the idea SSL is being used. Maybe we could
404 * accept ldapi:// as well, but the point is that we use
405 * an URL as an easy means to define bits of a search with
408 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
412 rc = LDAP_INVALID_SYNTAX;
417 case LDAP_URL_ERR_BADSCHEME:
419 * last chance: assume it's a(n exact) DN ...
421 * NOTE: must pass DN normalization
423 ldap_free_urldesc( ludp );
424 bv.bv_val = in->bv_val;
425 scope = LDAP_X_SCOPE_EXACT;
429 rc = LDAP_INVALID_SYNTAX;
433 if ( ( ludp->lud_host && *ludp->lud_host )
434 || ludp->lud_attrs || ludp->lud_exts )
436 /* host part must be empty */
437 /* attrs and extensions parts must be empty */
438 rc = LDAP_INVALID_SYNTAX;
442 /* Grab the filter */
443 if ( ludp->lud_filter ) {
444 Filter *f = str2filter( ludp->lud_filter );
446 rc = LDAP_INVALID_SYNTAX;
452 /* Grab the searchbase */
453 assert( ludp->lud_dn != NULL );
454 ber_str2bv( ludp->lud_dn, 0, 0, &bv );
455 rc = dnValidate( NULL, &bv );
458 ldap_free_urldesc( ludp );
469 struct berval *value,
470 void *assertedValue )
472 return octetStringMatch( matchp, flags, syntax, mr, value, assertedValue );
479 struct berval *normalized,
484 int rc = LDAP_INVALID_SYNTAX;
485 LDAPURLDesc *ludp = NULL;
492 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
493 * 3) dn.regex:<pattern>
494 * 4) u[.mech[/realm]]:<ID>
495 * 5) group[/<groupClass>[/<memberAttr>]]:<DN>
499 assert( val != NULL );
500 assert( !BER_BVISNULL( val ) );
503 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
504 * 3) dn.regex:<pattern>
506 * <DN> must pass DN normalization
508 if ( !strncasecmp( val->bv_val, "dn", STRLENOF( "dn" ) ) ) {
509 struct berval out = BER_BVNULL,
513 bv.bv_val = val->bv_val + STRLENOF( "dn" );
515 if ( bv.bv_val[ 0 ] == '.' ) {
518 if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
519 bv.bv_val += STRLENOF( "exact:" );
520 scope = LDAP_X_SCOPE_EXACT;
522 } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
523 bv.bv_val += STRLENOF( "regex:" );
524 scope = LDAP_X_SCOPE_REGEX;
526 } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
527 bv.bv_val += STRLENOF( "children:" );
528 scope = LDAP_X_SCOPE_CHILDREN;
530 } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
531 bv.bv_val += STRLENOF( "subtree:" );
532 scope = LDAP_X_SCOPE_SUBTREE;
534 } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
535 bv.bv_val += STRLENOF( "onelevel:" );
536 scope = LDAP_X_SCOPE_ONELEVEL;
539 return LDAP_INVALID_SYNTAX;
543 if ( bv.bv_val[ 0 ] != ':' ) {
544 return LDAP_INVALID_SYNTAX;
546 scope = LDAP_X_SCOPE_EXACT;
550 bv.bv_val += strspn( bv.bv_val, " " );
551 /* jump here in case no type specification was present
552 * and uri was not an URI... HEADS-UP: assuming EXACT */
553 is_dn: bv.bv_len = val->bv_len - ( bv.bv_val - val->bv_val );
555 /* a single '*' means any DN without using regexes */
556 if ( ber_bvccmp( &bv, '*' ) ) {
557 ber_str2bv_x( "dn:*", STRLENOF( "dn:*" ), 1, normalized, ctx );
562 case LDAP_X_SCOPE_EXACT:
563 case LDAP_X_SCOPE_CHILDREN:
564 case LDAP_X_SCOPE_SUBTREE:
565 case LDAP_X_SCOPE_ONELEVEL:
567 rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
569 rc = dnPretty( NULL, &bv, &out, ctx );
571 if( rc != LDAP_SUCCESS ) {
572 return LDAP_INVALID_SYNTAX;
576 case LDAP_X_SCOPE_REGEX:
577 normalized->bv_len = STRLENOF( "dn.regex:" ) + bv.bv_len;
578 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
579 ptr = lutil_strcopy( normalized->bv_val, "dn.regex:" );
580 ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
585 return LDAP_INVALID_SYNTAX;
590 case LDAP_X_SCOPE_EXACT:
591 BER_BVSTR( &prefix, "dn:" );
594 case LDAP_X_SCOPE_CHILDREN:
595 BER_BVSTR( &prefix, "dn.children:" );
598 case LDAP_X_SCOPE_SUBTREE:
599 BER_BVSTR( &prefix, "dn.subtree:" );
602 case LDAP_X_SCOPE_ONELEVEL:
603 BER_BVSTR( &prefix, "dn.onelevel:" );
611 normalized->bv_len = prefix.bv_len + out.bv_len;
612 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
614 ptr = lutil_strcopy( normalized->bv_val, prefix.bv_val );
615 ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
617 ber_memfree_x( out.bv_val, ctx );
622 * 4) u[.mech[/realm]]:<ID>
624 } else if ( ( val->bv_val[ 0 ] == 'u' || val->bv_val[ 0 ] == 'U' )
625 && ( val->bv_val[ 1 ] == ':'
626 || val->bv_val[ 1 ] == '/'
627 || val->bv_val[ 1 ] == '.' ) )
629 char buf[ SLAP_LDAPDN_MAXLEN ];
635 if ( sizeof( buf ) <= val->bv_len ) {
636 return LDAP_INVALID_SYNTAX;
639 id.bv_len = val->bv_len;
641 strncpy( buf, val->bv_val, sizeof( buf ) );
643 rc = slap_parse_user( &id, &user, &realm, &mech );
644 if ( rc != LDAP_SUCCESS ) {
645 return LDAP_INVALID_SYNTAX;
648 ber_dupbv_x( normalized, val, ctx );
653 * 5) group[/groupClass[/memberAttr]]:<DN>
655 * <groupClass> defaults to "groupOfNames"
656 * <memberAttr> defaults to "member"
658 * <DN> must pass DN normalization
660 } else if ( strncasecmp( val->bv_val, "group", STRLENOF( "group" ) ) == 0 )
662 struct berval group_dn = BER_BVNULL,
663 group_oc = BER_BVNULL,
664 member_at = BER_BVNULL,
668 bv.bv_val = val->bv_val + STRLENOF( "group" );
669 bv.bv_len = val->bv_len - STRLENOF( "group" );
670 group_dn.bv_val = ber_bvchr( &bv, ':' );
671 if ( group_dn.bv_val == NULL ) {
672 /* last chance: assume it's a(n exact) DN ... */
673 bv.bv_val = val->bv_val;
674 scope = LDAP_X_SCOPE_EXACT;
679 * FIXME: we assume that "member" and "groupOfNames"
680 * are present in schema...
682 if ( bv.bv_val[ 0 ] == '/' ) {
683 ObjectClass *oc = NULL;
685 group_oc.bv_val = &bv.bv_val[ 1 ];
686 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
688 member_at.bv_val = ber_bvchr( &group_oc, '/' );
689 if ( member_at.bv_val ) {
690 AttributeDescription *ad = NULL;
691 const char *text = NULL;
693 group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
695 member_at.bv_len = group_dn.bv_val - member_at.bv_val;
696 rc = slap_bv2ad( &member_at, &ad, &text );
697 if ( rc != LDAP_SUCCESS ) {
701 member_at = ad->ad_cname;
705 oc = oc_bvfind( &group_oc );
707 return LDAP_INVALID_SYNTAX;
710 group_oc = oc->soc_cname;
714 group_dn.bv_len = val->bv_len - ( group_dn.bv_val - val->bv_val );
717 rc = dnNormalize( 0, NULL, NULL, &group_dn, &out, ctx );
719 rc = dnPretty( NULL, &group_dn, &out, ctx );
721 if ( rc != LDAP_SUCCESS ) {
725 normalized->bv_len = STRLENOF( "group" ":" ) + out.bv_len;
726 if ( !BER_BVISNULL( &group_oc ) ) {
727 normalized->bv_len += STRLENOF( "/" ) + group_oc.bv_len;
728 if ( !BER_BVISNULL( &member_at ) ) {
729 normalized->bv_len += STRLENOF( "/" ) + member_at.bv_len;
733 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
734 ptr = lutil_strcopy( normalized->bv_val, "group" );
735 if ( !BER_BVISNULL( &group_oc ) ) {
738 ptr = lutil_strncopy( ptr, group_oc.bv_val, group_oc.bv_len );
739 if ( !BER_BVISNULL( &member_at ) ) {
742 ptr = lutil_strncopy( ptr, member_at.bv_val, member_at.bv_len );
747 ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
749 ber_memfree_x( out.bv_val, ctx );
755 * ldap:///<base>??<scope>?<filter>
756 * <scope> ::= {base|one|subtree}
758 * <scope> defaults to "base"
759 * <base> must pass DN normalization
760 * <filter> must pass str2filter()
762 rc = ldap_url_parse( val->bv_val, &ludp );
764 case LDAP_URL_SUCCESS:
765 /* FIXME: the check is pedantic, but I think it's necessary,
766 * because people tend to use things like ldaps:// which
767 * gives the idea SSL is being used. Maybe we could
768 * accept ldapi:// as well, but the point is that we use
769 * an URL as an easy means to define bits of a search with
772 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
776 rc = LDAP_INVALID_SYNTAX;
780 AC_MEMCPY( ludp->lud_scheme, "ldap", STRLENOF( "ldap" ) );
783 case LDAP_URL_ERR_BADSCHEME:
785 * last chance: assume it's a(n exact) DN ...
787 * NOTE: must pass DN normalization
789 ldap_free_urldesc( ludp );
790 bv.bv_val = val->bv_val;
791 scope = LDAP_X_SCOPE_EXACT;
795 rc = LDAP_INVALID_SYNTAX;
799 if ( ( ludp->lud_host && *ludp->lud_host )
800 || ludp->lud_attrs || ludp->lud_exts )
802 /* host part must be empty */
803 /* attrs and extensions parts must be empty */
804 rc = LDAP_INVALID_SYNTAX;
808 /* Grab the filter */
809 if ( ludp->lud_filter ) {
810 struct berval filterstr;
813 lud_filter = ludp->lud_filter;
815 f = str2filter( lud_filter );
817 rc = LDAP_INVALID_SYNTAX;
820 filter2bv( f, &filterstr );
822 if ( BER_BVISNULL( &filterstr ) ) {
823 rc = LDAP_INVALID_SYNTAX;
827 ludp->lud_filter = filterstr.bv_val;
830 /* Grab the searchbase */
831 assert( ludp->lud_dn != NULL );
832 if ( ludp->lud_dn ) {
833 struct berval out = BER_BVNULL;
835 lud_dn = ludp->lud_dn;
837 ber_str2bv( lud_dn, 0, 0, &bv );
839 rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
841 rc = dnPretty( NULL, &bv, &out, ctx );
844 if ( rc != LDAP_SUCCESS ) {
848 ludp->lud_dn = out.bv_val;
852 normalized->bv_val = ldap_url_desc2str( ludp );
853 if ( normalized->bv_val ) {
854 normalized->bv_len = strlen( normalized->bv_val );
857 rc = LDAP_INVALID_SYNTAX;
862 if ( ludp->lud_filter != lud_filter ) {
863 ber_memfree( ludp->lud_filter );
865 ludp->lud_filter = lud_filter;
869 if ( ludp->lud_dn != lud_dn ) {
870 ber_memfree( ludp->lud_dn );
872 ludp->lud_dn = lud_dn;
875 ldap_free_urldesc( ludp );
886 struct berval *normalized,
891 Debug( LDAP_DEBUG_TRACE, ">>> authzNormalize: <%s>\n",
894 rc = authzPrettyNormal( val, normalized, ctx, 1 );
896 Debug( LDAP_DEBUG_TRACE, "<<< authzNormalize: <%s> (%d)\n",
897 normalized->bv_val, rc, 0 );
911 Debug( LDAP_DEBUG_TRACE, ">>> authzPretty: <%s>\n",
914 rc = authzPrettyNormal( val, out, ctx, 0 );
916 Debug( LDAP_DEBUG_TRACE, "<<< authzPretty: <%s> (%d)\n",
917 out->bv_val, rc, 0 );
922 #endif /* SLAP_AUTHZ_SYNTAX */
929 struct berval *nbase,
939 #ifdef SLAP_ORDERED_PRETTYNORM
941 #endif /* SLAP_ORDERED_PRETTYNORM */
943 assert( uri != NULL && !BER_BVISNULL( uri ) );
950 Debug( LDAP_DEBUG_TRACE,
951 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
953 rc = LDAP_PROTOCOL_ERROR;
955 #ifdef SLAP_ORDERED_PRETTYNORM
957 if ( idx.bv_val[ 0 ] == '{' ) {
960 ptr = ber_bvchr( &idx, '}' ) + 1;
962 assert( ptr != (void *)1 );
964 idx.bv_len -= ptr - idx.bv_val;
968 #endif /* SLAP_ORDERED_PRETTYNORM */
971 * dn[.<dnstyle>]:<dnpattern>
972 * <dnstyle> ::= {exact|regex|children|subtree|onelevel}
974 * <dnstyle> defaults to "exact"
975 * if <dnstyle> is not "regex", <dnpattern> must pass DN normalization
977 if ( !strncasecmp( uri->bv_val, "dn", STRLENOF( "dn" ) ) ) {
978 bv.bv_val = uri->bv_val + STRLENOF( "dn" );
980 if ( bv.bv_val[ 0 ] == '.' ) {
983 if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
984 bv.bv_val += STRLENOF( "exact:" );
985 *scope = LDAP_X_SCOPE_EXACT;
987 } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
988 bv.bv_val += STRLENOF( "regex:" );
989 *scope = LDAP_X_SCOPE_REGEX;
991 } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
992 bv.bv_val += STRLENOF( "children:" );
993 *scope = LDAP_X_SCOPE_CHILDREN;
995 } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
996 bv.bv_val += STRLENOF( "subtree:" );
997 *scope = LDAP_X_SCOPE_SUBTREE;
999 } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
1000 bv.bv_val += STRLENOF( "onelevel:" );
1001 *scope = LDAP_X_SCOPE_ONELEVEL;
1004 return LDAP_PROTOCOL_ERROR;
1008 if ( bv.bv_val[ 0 ] != ':' ) {
1009 return LDAP_PROTOCOL_ERROR;
1011 *scope = LDAP_X_SCOPE_EXACT;
1015 bv.bv_val += strspn( bv.bv_val, " " );
1016 /* jump here in case no type specification was present
1017 * and uri was not an URI... HEADS-UP: assuming EXACT */
1018 is_dn: bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
1020 /* a single '*' means any DN without using regexes */
1021 if ( ber_bvccmp( &bv, '*' ) ) {
1022 *scope = LDAP_X_SCOPE_USERS;
1026 case LDAP_X_SCOPE_EXACT:
1027 case LDAP_X_SCOPE_CHILDREN:
1028 case LDAP_X_SCOPE_SUBTREE:
1029 case LDAP_X_SCOPE_ONELEVEL:
1031 rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
1032 if( rc != LDAP_SUCCESS ) {
1036 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1041 case LDAP_X_SCOPE_REGEX:
1042 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1044 case LDAP_X_SCOPE_USERS:
1058 } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
1059 && ( uri->bv_val[ 1 ] == ':'
1060 || uri->bv_val[ 1 ] == '/'
1061 || uri->bv_val[ 1 ] == '.' ) )
1063 Connection c = *op->o_conn;
1064 char buf[ SLAP_LDAPDN_MAXLEN ];
1070 if ( sizeof( buf ) <= uri->bv_len ) {
1071 return LDAP_INVALID_SYNTAX;
1074 id.bv_len = uri->bv_len;
1076 strncpy( buf, uri->bv_val, sizeof( buf ) );
1078 rc = slap_parse_user( &id, &user, &realm, &mech );
1079 if ( rc != LDAP_SUCCESS ) {
1083 if ( !BER_BVISNULL( &mech ) ) {
1084 c.c_sasl_bind_mech = mech;
1086 BER_BVSTR( &c.c_sasl_bind_mech, "AUTHZ" );
1089 rc = slap_sasl_getdn( &c, op, &user,
1090 realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
1092 if ( rc == LDAP_SUCCESS ) {
1093 *scope = LDAP_X_SCOPE_EXACT;
1099 * group[/<groupoc>[/<groupat>]]:<groupdn>
1101 * groupoc defaults to "groupOfNames"
1102 * groupat defaults to "member"
1104 * <groupdn> must pass DN normalization
1106 } else if ( strncasecmp( uri->bv_val, "group", STRLENOF( "group" ) ) == 0 )
1108 struct berval group_dn = BER_BVNULL,
1109 group_oc = BER_BVNULL,
1110 member_at = BER_BVNULL;
1113 bv.bv_val = uri->bv_val + STRLENOF( "group" );
1114 bv.bv_len = uri->bv_len - STRLENOF( "group" );
1115 group_dn.bv_val = ber_bvchr( &bv, ':' );
1116 if ( group_dn.bv_val == NULL ) {
1117 /* last chance: assume it's a(n exact) DN ... */
1118 bv.bv_val = uri->bv_val;
1119 *scope = LDAP_X_SCOPE_EXACT;
1123 if ( bv.bv_val[ 0 ] == '/' ) {
1124 group_oc.bv_val = &bv.bv_val[ 1 ];
1125 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
1127 member_at.bv_val = ber_bvchr( &group_oc, '/' );
1128 if ( member_at.bv_val ) {
1129 group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
1131 member_at.bv_len = group_dn.bv_val - member_at.bv_val;
1134 BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
1138 BER_BVSTR( &group_oc, SLAPD_GROUP_CLASS );
1141 group_dn.bv_len = uri->bv_len - ( group_dn.bv_val - uri->bv_val );
1144 rc = dnNormalize( 0, NULL, NULL, &group_dn, nbase, op->o_tmpmemctx );
1145 if ( rc != LDAP_SUCCESS ) {
1150 ber_dupbv_x( nbase, &group_dn, op->o_tmpmemctx );
1153 *scope = LDAP_X_SCOPE_GROUP;
1155 /* FIXME: caller needs to add value of member attribute
1156 * and close brackets twice */
1157 fstr->bv_len = STRLENOF( "(&(objectClass=)(=" /* )) */ )
1158 + group_oc.bv_len + member_at.bv_len;
1159 fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
1161 tmp = lutil_strncopy( fstr->bv_val, "(&(objectClass=" /* )) */ ,
1162 STRLENOF( "(&(objectClass=" /* )) */ ) );
1163 tmp = lutil_strncopy( tmp, group_oc.bv_val, group_oc.bv_len );
1164 tmp = lutil_strncopy( tmp, /* ( */ ")(" /* ) */ ,
1165 STRLENOF( /* ( */ ")(" /* ) */ ) );
1166 tmp = lutil_strncopy( tmp, member_at.bv_val, member_at.bv_len );
1167 tmp = lutil_strncopy( tmp, "=", STRLENOF( "=" ) );
1173 * ldap:///<base>??<scope>?<filter>
1174 * <scope> ::= {base|one|subtree}
1176 * <scope> defaults to "base"
1177 * <base> must pass DN normalization
1178 * <filter> must pass str2filter()
1180 rc = ldap_url_parse( uri->bv_val, &ludp );
1182 case LDAP_URL_SUCCESS:
1183 /* FIXME: the check is pedantic, but I think it's necessary,
1184 * because people tend to use things like ldaps:// which
1185 * gives the idea SSL is being used. Maybe we could
1186 * accept ldapi:// as well, but the point is that we use
1187 * an URL as an easy means to define bits of a search with
1190 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
1194 rc = LDAP_PROTOCOL_ERROR;
1199 case LDAP_URL_ERR_BADSCHEME:
1201 * last chance: assume it's a(n exact) DN ...
1203 * NOTE: must pass DN normalization
1205 ldap_free_urldesc( ludp );
1206 bv.bv_val = uri->bv_val;
1207 *scope = LDAP_X_SCOPE_EXACT;
1211 rc = LDAP_PROTOCOL_ERROR;
1215 if ( ( ludp->lud_host && *ludp->lud_host )
1216 || ludp->lud_attrs || ludp->lud_exts )
1218 /* host part must be empty */
1219 /* attrs and extensions parts must be empty */
1220 rc = LDAP_PROTOCOL_ERROR;
1224 /* Grab the scope */
1225 *scope = ludp->lud_scope;
1227 /* Grab the filter */
1228 if ( ludp->lud_filter ) {
1229 *filter = str2filter_x( op, ludp->lud_filter );
1230 if ( *filter == NULL ) {
1231 rc = LDAP_PROTOCOL_ERROR;
1234 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
1237 /* Grab the searchbase */
1238 ber_str2bv( ludp->lud_dn, 0, 0, base );
1240 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
1242 ber_dupbv_x( nbase, base, op->o_tmpmemctx );
1247 if( rc != LDAP_SUCCESS ) {
1248 if( *filter ) filter_free_x( op, *filter );
1252 /* Don't free these, return them to caller */
1253 ludp->lud_filter = NULL;
1254 ludp->lud_dn = NULL;
1257 ldap_free_urldesc( ludp );
1261 #ifndef SLAP_AUTH_REWRITE
1262 static int slap_sasl_rx_off(char *rep, int *off)
1267 /* Precompile replace pattern. Find the $<n> placeholders */
1270 for ( c = rep; *c; c++ ) {
1271 if ( *c == '\\' && c[1] ) {
1276 if ( n == SASLREGEX_REPLACE ) {
1277 Debug( LDAP_DEBUG_ANY,
1278 "SASL replace pattern %s has too many $n "
1279 "placeholders (max %d)\n",
1280 rep, SASLREGEX_REPLACE, 0 );
1282 return( LDAP_OTHER );
1289 /* Final placeholder, after the last $n */
1293 return( LDAP_SUCCESS );
1295 #endif /* ! SLAP_AUTH_REWRITE */
1297 #ifdef SLAP_AUTH_REWRITE
1298 int slap_sasl_rewrite_config(
1308 /* init at first call */
1309 if ( sasl_rwinfo == NULL ) {
1310 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1313 /* strip "authid-" prefix for parsing */
1315 argv[0] += STRLENOF( "authid-" );
1316 rc = rewrite_parse( sasl_rwinfo, fname, lineno, argc, argv );
1322 int slap_sasl_rewrite_destroy( void )
1324 if ( sasl_rwinfo ) {
1325 rewrite_info_delete( &sasl_rwinfo );
1332 int slap_sasl_regexp_rewrite_config(
1336 const char *replace,
1337 const char *context )
1340 char *argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL };
1342 /* init at first call */
1343 if ( sasl_rwinfo == NULL ) {
1344 char *argvEngine[] = { "rewriteEngine", "on", NULL };
1345 char *argvContext[] = { "rewriteContext", NULL, NULL };
1347 /* initialize rewrite engine */
1348 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1350 /* switch on rewrite engine */
1351 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine );
1352 if (rc != LDAP_SUCCESS) {
1356 /* create generic authid context */
1357 argvContext[1] = AUTHID_CONTEXT;
1358 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvContext );
1359 if (rc != LDAP_SUCCESS) {
1364 argvRule[1] = (char *)match;
1365 argvRule[2] = (char *)replace;
1366 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule );
1370 #endif /* SLAP_AUTH_REWRITE */
1372 int slap_sasl_regexp_config( const char *match, const char *replace )
1377 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
1378 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
1380 reg = &SaslRegexp[nSaslRegexp];
1382 reg->sr_match = ch_strdup( match );
1383 reg->sr_replace = ch_strdup( replace );
1385 #ifdef SLAP_AUTH_REWRITE
1386 rc = slap_sasl_regexp_rewrite_config( "sasl-regexp", 0,
1387 match, replace, AUTHID_CONTEXT );
1388 if ( rc == LDAP_SUCCESS ) nSaslRegexp++;
1390 #else /* ! SLAP_AUTH_REWRITE */
1392 /* Precompile matching pattern */
1393 rc = regcomp( ®->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
1395 Debug( LDAP_DEBUG_ANY,
1396 "SASL match pattern %s could not be compiled by regexp engine\n",
1397 reg->sr_match, 0, 0 );
1399 #ifdef ENABLE_REWRITE
1400 /* Dummy block to force symbol references in librewrite */
1401 if ( slapMode == ( SLAP_SERVER_MODE|SLAP_TOOL_MODE )) {
1402 rewrite_info_init( 0 );
1405 return( LDAP_OTHER );
1408 rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
1409 if ( rc != LDAP_SUCCESS ) return rc;
1412 return( LDAP_SUCCESS );
1413 #endif /* ! SLAP_AUTH_REWRITE */
1416 void slap_sasl_regexp_unparse( BerVarray *out )
1419 BerVarray bva = NULL;
1420 char ibuf[32], *ptr;
1423 if ( !nSaslRegexp ) return;
1426 bva = ch_malloc( (nSaslRegexp+1) * sizeof(struct berval) );
1427 BER_BVZERO(bva+nSaslRegexp);
1428 for ( i=0; i<nSaslRegexp; i++ ) {
1429 idx.bv_len = sprintf( idx.bv_val, "{%d}", i);
1430 bva[i].bv_len = idx.bv_len + strlen( SaslRegexp[i].sr_match ) +
1431 strlen( SaslRegexp[i].sr_replace ) + 5;
1432 bva[i].bv_val = ch_malloc( bva[i].bv_len+1 );
1433 ptr = lutil_strcopy( bva[i].bv_val, ibuf );
1435 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match );
1436 ptr = lutil_strcopy( ptr, "\" \"" );
1437 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace );
1444 #ifndef SLAP_AUTH_REWRITE
1445 /* Perform replacement on regexp matches */
1446 static void slap_sasl_rx_exp(
1450 const char *saslname,
1454 int i, n, len, insert;
1456 /* Get the total length of the final URI */
1460 while( off[n] >= 0 ) {
1461 /* Len of next section from replacement string (x,y,z above) */
1462 len += off[n] - off[n-1] - 2;
1466 /* Len of string from saslname that matched next $i (b,d above) */
1467 i = rep[ off[n] + 1 ] - '0';
1468 len += str[i].rm_eo - str[i].rm_so;
1471 out->bv_val = slap_sl_malloc( len + 1, ctx );
1474 /* Fill in URI with replace string, replacing $i as we go */
1477 while( off[n] >= 0) {
1478 /* Paste in next section from replacement string (x,y,z above) */
1479 len = off[n] - off[n-1] - 2;
1480 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
1485 /* Paste in string from saslname that matched next $i (b,d above) */
1486 i = rep[ off[n] + 1 ] - '0';
1487 len = str[i].rm_eo - str[i].rm_so;
1488 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
1494 out->bv_val[insert] = '\0';
1496 #endif /* ! SLAP_AUTH_REWRITE */
1498 /* Take the passed in SASL name and attempt to convert it into an
1499 LDAP URI to find the matching LDAP entry, using the pattern matching
1500 strings given in the saslregexp config file directive(s) */
1502 static int slap_authz_regexp( struct berval *in, struct berval *out,
1503 int flags, void *ctx )
1505 #ifdef SLAP_AUTH_REWRITE
1506 const char *context = AUTHID_CONTEXT;
1508 if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
1512 /* FIXME: if aware of authc/authz mapping,
1513 * we could use different contexts ... */
1514 switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL,
1517 case REWRITE_REGEXEC_OK:
1518 if ( !BER_BVISNULL( out ) ) {
1519 char *val = out->bv_val;
1520 ber_str2bv_x( val, 0, 1, out, ctx );
1521 if ( val != in->bv_val ) {
1525 ber_dupbv_x( out, in, ctx );
1527 Debug( LDAP_DEBUG_ARGS,
1528 "[rw] %s: \"%s\" -> \"%s\"\n",
1529 context, in->bv_val, out->bv_val );
1532 case REWRITE_REGEXEC_UNWILLING:
1533 case REWRITE_REGEXEC_ERR:
1538 #else /* ! SLAP_AUTH_REWRITE */
1539 char *saslname = in->bv_val;
1541 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
1544 memset( out, 0, sizeof( *out ) );
1546 Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
1549 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
1553 /* Match the normalized SASL name to the saslregexp patterns */
1554 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
1555 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE,
1556 sr_strings, 0) == 0 )
1560 if( i >= nSaslRegexp ) return( 0 );
1563 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
1564 * replace pattern of the form "x$1y$2z". The returned string needs
1565 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
1567 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
1568 sr_strings, saslname, out, ctx );
1570 Debug( LDAP_DEBUG_TRACE,
1571 "slap_authz_regexp: converted SASL name to %s\n",
1572 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
1575 #endif /* ! SLAP_AUTH_REWRITE */
1578 /* This callback actually does some work...*/
1579 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
1581 struct berval *ndn = o->o_callback->sc_private;
1583 if (rs->sr_type != REP_SEARCH) return 0;
1585 /* We only want to be called once */
1586 if ( !BER_BVISNULL( ndn ) ) {
1587 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
1590 Debug( LDAP_DEBUG_TRACE,
1591 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
1595 ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
1600 typedef struct smatch_info {
1605 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
1607 smatch_info *sm = o->o_callback->sc_private;
1609 if ( rs->sr_type != REP_SEARCH ) {
1610 if ( rs->sr_err != LDAP_SUCCESS ) {
1616 if ( sm->match == 1 ) {
1621 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
1632 slap_sasl_matches( Operation *op, BerVarray rules,
1633 struct berval *assertDN, struct berval *authc )
1635 int rc = LDAP_INAPPROPRIATE_AUTH;
1637 if ( rules != NULL ) {
1640 for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
1641 rc = slap_sasl_match( op, &rules[i], assertDN, authc );
1642 if ( rc == LDAP_SUCCESS ) break;
1650 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
1651 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
1652 * the rule must be used as an internal search for entries. If that search
1653 * returns the *assertDN entry, the match is successful.
1655 * The assertDN should not have the dn: prefix
1659 slap_sasl_match( Operation *opx, struct berval *rule,
1660 struct berval *assertDN, struct berval *authc )
1665 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
1667 SlapReply rs = {REP_RESULT};
1668 struct berval base = BER_BVNULL;
1672 cb.sc_private = &sm;
1674 Debug( LDAP_DEBUG_TRACE,
1675 "===>slap_sasl_match: comparing DN %s to rule %s\n",
1676 assertDN->bv_val, rule->bv_val, 0 );
1678 /* NOTE: don't normalize rule if authz syntax is enabled */
1679 rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn,
1680 &op.ors_scope, &op.ors_filter, &op.ors_filterstr,
1681 #ifdef SLAP_AUTHZ_SYNTAX
1683 #else /* ! SLAP_AUTHZ_SYNTAX */
1685 #endif /* ! SLAP_AUTHZ_SYNTAX */
1688 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
1690 switch ( op.ors_scope ) {
1691 case LDAP_X_SCOPE_EXACT:
1693 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
1696 rc = LDAP_INAPPROPRIATE_AUTH;
1700 case LDAP_X_SCOPE_CHILDREN:
1701 case LDAP_X_SCOPE_SUBTREE:
1702 case LDAP_X_SCOPE_ONELEVEL:
1704 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
1706 rc = LDAP_INAPPROPRIATE_AUTH;
1708 if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
1711 } else if ( d > 0 ) {
1714 /* leave room for at least one char of attributeType,
1715 * one for '=' and one for ',' */
1716 if ( d < STRLENOF( "x=,") ) {
1720 bv.bv_len = op.o_req_ndn.bv_len;
1721 bv.bv_val = assertDN->bv_val + d;
1723 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
1724 switch ( op.ors_scope ) {
1725 case LDAP_X_SCOPE_SUBTREE:
1726 case LDAP_X_SCOPE_CHILDREN:
1730 case LDAP_X_SCOPE_ONELEVEL:
1734 dnParent( assertDN, &pdn );
1735 /* the common portion of the DN
1736 * already matches, so only check
1737 * if parent DN of assertedDN
1738 * is all the pattern */
1739 if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
1745 /* at present, impossible */
1753 case LDAP_X_SCOPE_REGEX:
1754 rc = regcomp(®, op.o_req_ndn.bv_val,
1755 REG_EXTENDED|REG_ICASE|REG_NOSUB);
1757 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
1763 rc = LDAP_INAPPROPRIATE_AUTH;
1767 case LDAP_X_SCOPE_GROUP: {
1770 /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
1771 * we need to append the <assertDN> so that the <group_dn> is searched
1772 * with scope "base", and the filter ensures that <assertDN> is
1773 * member of the group */
1774 tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len +
1775 assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 );
1776 if ( tmp == NULL ) {
1777 rc = LDAP_NO_MEMORY;
1780 op.ors_filterstr.bv_val = tmp;
1782 tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
1783 tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
1785 /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
1786 op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
1787 if ( op.ors_filter == NULL ) {
1788 rc = LDAP_PROTOCOL_ERROR;
1791 op.ors_scope = LDAP_SCOPE_BASE;
1793 /* hijack match DN: use that of the group instead of the assertDN;
1794 * assertDN is now in the filter */
1795 sm.dn = &op.o_req_ndn;
1801 case LDAP_X_SCOPE_USERS:
1802 if ( !BER_BVISEMPTY( assertDN ) ) {
1805 rc = LDAP_INAPPROPRIATE_AUTH;
1813 /* Must run an internal search. */
1814 if ( op.ors_filter == NULL ) {
1815 rc = LDAP_FILTER_ERROR;
1819 Debug( LDAP_DEBUG_TRACE,
1820 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
1821 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1823 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1824 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1825 rc = LDAP_INAPPROPRIATE_AUTH;
1829 op.o_hdr = opx->o_hdr;
1830 op.o_tag = LDAP_REQ_SEARCH;
1832 op.o_callback = &cb;
1833 slap_op_time( &op.o_time, &op.o_tincr );
1834 op.o_do_not_cache = 1;
1835 op.o_is_auth_check = 1;
1836 /* use req_ndn as req_dn instead of non-pretty base of uri */
1837 if( !BER_BVISNULL( &base ) ) {
1838 ch_free( base.bv_val );
1839 /* just in case... */
1840 BER_BVZERO( &base );
1842 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1843 op.ors_deref = LDAP_DEREF_NEVER;
1845 op.ors_tlimit = SLAP_NO_LIMIT;
1846 op.ors_attrs = slap_anlist_no_attrs;
1847 op.ors_attrsonly = 1;
1849 op.o_bd->be_search( &op, &rs );
1851 if (sm.match == 1) {
1854 rc = LDAP_INAPPROPRIATE_AUTH;
1858 if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1859 if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1860 if( op.ors_filter ) filter_free_x( opx, op.ors_filter );
1861 if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1863 Debug( LDAP_DEBUG_TRACE,
1864 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
1871 * This function answers the question, "Can this ID authorize to that ID?",
1872 * based on authorization rules. The rules are stored in the *searchDN, in the
1873 * attribute named by *attr. If any of those rules map to the *assertDN, the
1874 * authorization is approved.
1876 * The DNs should not have the dn: prefix
1879 slap_sasl_check_authz( Operation *op,
1880 struct berval *searchDN,
1881 struct berval *assertDN,
1882 AttributeDescription *ad,
1883 struct berval *authc )
1886 BerVarray vals = NULL;
1888 Debug( LDAP_DEBUG_TRACE,
1889 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
1890 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1892 rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
1893 if( rc != LDAP_SUCCESS ) goto COMPLETE;
1895 /* Check if the *assertDN matches any *vals */
1896 rc = slap_sasl_matches( op, vals, assertDN, authc );
1899 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1901 Debug( LDAP_DEBUG_TRACE,
1902 "<==slap_sasl_check_authz: %s check returning %d\n",
1903 ad->ad_cname.bv_val, rc, 0);
1909 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
1910 * return the LDAP DN to which it matches. The SASL regexp rules in the config
1911 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
1912 * search with scope=base), just return the URI (or its searchbase). Otherwise
1913 * an internal search must be done, and if that search returns exactly one
1914 * entry, return the DN of that one entry.
1919 struct berval *saslname,
1920 struct berval *sasldn,
1924 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
1926 SlapReply rs = {REP_RESULT};
1927 struct berval regout = BER_BVNULL;
1928 struct berval base = BER_BVNULL;
1930 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1931 "converting SASL name %s to a DN\n",
1932 saslname->bv_val, 0,0 );
1934 BER_BVZERO( sasldn );
1935 cb.sc_private = sasldn;
1937 /* Convert the SASL name into a minimal URI */
1938 if( !slap_authz_regexp( saslname, ®out, flags, opx->o_tmpmemctx ) ) {
1942 /* NOTE: always normalize regout because it results
1943 * from string submatch expansion */
1944 rc = slap_parseURI( opx, ®out, &base, &op.o_req_ndn,
1945 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 );
1946 if ( !BER_BVISNULL( ®out ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
1947 if ( rc != LDAP_SUCCESS ) {
1951 /* Must do an internal search */
1952 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1954 switch ( op.ors_scope ) {
1955 case LDAP_X_SCOPE_EXACT:
1956 *sasldn = op.o_req_ndn;
1957 BER_BVZERO( &op.o_req_ndn );
1958 /* intentionally continue to next case */
1960 case LDAP_X_SCOPE_REGEX:
1961 case LDAP_X_SCOPE_SUBTREE:
1962 case LDAP_X_SCOPE_CHILDREN:
1963 case LDAP_X_SCOPE_ONELEVEL:
1964 case LDAP_X_SCOPE_GROUP:
1965 case LDAP_X_SCOPE_USERS:
1966 /* correctly parsed, but illegal */
1969 case LDAP_SCOPE_BASE:
1970 case LDAP_SCOPE_ONELEVEL:
1971 case LDAP_SCOPE_SUBTREE:
1972 case LDAP_SCOPE_SUBORDINATE:
1977 /* catch unhandled cases (there shouldn't be) */
1981 Debug( LDAP_DEBUG_TRACE,
1982 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1983 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1985 if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
1989 /* Must run an internal search. */
1990 if ( op.ors_filter == NULL ) {
1991 rc = LDAP_FILTER_ERROR;
1995 op.o_hdr = opx->o_hdr;
1996 op.o_tag = LDAP_REQ_SEARCH;
1997 op.o_ndn = opx->o_conn->c_ndn;
1998 op.o_callback = &cb;
1999 slap_op_time( &op.o_time, &op.o_tincr );
2000 op.o_do_not_cache = 1;
2001 op.o_is_auth_check = 1;
2002 op.ors_deref = LDAP_DEREF_NEVER;
2004 op.ors_tlimit = SLAP_NO_LIMIT;
2005 op.ors_attrs = slap_anlist_no_attrs;
2006 op.ors_attrsonly = 1;
2007 /* use req_ndn as req_dn instead of non-pretty base of uri */
2008 if( !BER_BVISNULL( &base ) ) {
2009 ch_free( base.bv_val );
2010 /* just in case... */
2011 BER_BVZERO( &base );
2013 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
2015 op.o_bd->be_search( &op, &rs );
2018 if( !BER_BVISEMPTY( sasldn ) ) {
2019 opx->o_conn->c_authz_backend = op.o_bd;
2021 if( !BER_BVISNULL( &op.o_req_dn ) ) {
2022 slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
2024 if( !BER_BVISNULL( &op.o_req_ndn ) ) {
2025 slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
2027 if( op.ors_filter ) {
2028 filter_free_x( opx, op.ors_filter );
2030 if( !BER_BVISNULL( &op.ors_filterstr ) ) {
2031 ch_free( op.ors_filterstr.bv_val );
2034 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
2035 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
2041 /* Check if a bind can SASL authorize to another identity.
2042 * The DNs should not have the dn: prefix
2045 int slap_sasl_authorized( Operation *op,
2046 struct berval *authcDN, struct berval *authzDN )
2048 int rc = LDAP_INAPPROPRIATE_AUTH;
2050 /* User binding as anonymous */
2051 if ( authzDN == NULL ) {
2056 Debug( LDAP_DEBUG_TRACE,
2057 "==>slap_sasl_authorized: can %s become %s?\n",
2058 authcDN->bv_len ? authcDN->bv_val : "(null)",
2059 authzDN->bv_len ? authzDN->bv_val : "(null)", 0 );
2061 /* If person is authorizing to self, succeed */
2062 if ( dn_match( authcDN, authzDN ) ) {
2067 /* Allow the manager to authorize as any DN. */
2068 if( op->o_conn->c_authz_backend &&
2069 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
2075 /* Check source rules */
2076 if( authz_policy & SASL_AUTHZ_TO ) {
2077 rc = slap_sasl_check_authz( op, authcDN, authzDN,
2078 slap_schema.si_ad_saslAuthzTo, authcDN );
2079 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
2084 /* Check destination rules */
2085 if( authz_policy & SASL_AUTHZ_FROM ) {
2086 rc = slap_sasl_check_authz( op, authzDN, authcDN,
2087 slap_schema.si_ad_saslAuthzFrom, authcDN );
2088 if( rc == LDAP_SUCCESS ) {
2093 rc = LDAP_INAPPROPRIATE_AUTH;
2097 Debug( LDAP_DEBUG_TRACE,
2098 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );