2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1998-2007 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 );
1139 BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
1142 group_dn.bv_len = uri->bv_len - ( group_dn.bv_val - uri->bv_val );
1145 rc = dnNormalize( 0, NULL, NULL, &group_dn, nbase, op->o_tmpmemctx );
1146 if ( rc != LDAP_SUCCESS ) {
1151 ber_dupbv_x( nbase, &group_dn, op->o_tmpmemctx );
1154 *scope = LDAP_X_SCOPE_GROUP;
1156 /* FIXME: caller needs to add value of member attribute
1157 * and close brackets twice */
1158 fstr->bv_len = STRLENOF( "(&(objectClass=)(=" /* )) */ )
1159 + group_oc.bv_len + member_at.bv_len;
1160 fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
1162 tmp = lutil_strncopy( fstr->bv_val, "(&(objectClass=" /* )) */ ,
1163 STRLENOF( "(&(objectClass=" /* )) */ ) );
1164 tmp = lutil_strncopy( tmp, group_oc.bv_val, group_oc.bv_len );
1165 tmp = lutil_strncopy( tmp, /* ( */ ")(" /* ) */ ,
1166 STRLENOF( /* ( */ ")(" /* ) */ ) );
1167 tmp = lutil_strncopy( tmp, member_at.bv_val, member_at.bv_len );
1168 tmp = lutil_strncopy( tmp, "=", STRLENOF( "=" ) );
1174 * ldap:///<base>??<scope>?<filter>
1175 * <scope> ::= {base|one|subtree}
1177 * <scope> defaults to "base"
1178 * <base> must pass DN normalization
1179 * <filter> must pass str2filter()
1181 rc = ldap_url_parse( uri->bv_val, &ludp );
1183 case LDAP_URL_SUCCESS:
1184 /* FIXME: the check is pedantic, but I think it's necessary,
1185 * because people tend to use things like ldaps:// which
1186 * gives the idea SSL is being used. Maybe we could
1187 * accept ldapi:// as well, but the point is that we use
1188 * an URL as an easy means to define bits of a search with
1191 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
1195 rc = LDAP_PROTOCOL_ERROR;
1200 case LDAP_URL_ERR_BADSCHEME:
1202 * last chance: assume it's a(n exact) DN ...
1204 * NOTE: must pass DN normalization
1206 ldap_free_urldesc( ludp );
1207 bv.bv_val = uri->bv_val;
1208 *scope = LDAP_X_SCOPE_EXACT;
1212 rc = LDAP_PROTOCOL_ERROR;
1216 if ( ( ludp->lud_host && *ludp->lud_host )
1217 || ludp->lud_attrs || ludp->lud_exts )
1219 /* host part must be empty */
1220 /* attrs and extensions parts must be empty */
1221 rc = LDAP_PROTOCOL_ERROR;
1225 /* Grab the scope */
1226 *scope = ludp->lud_scope;
1228 /* Grab the filter */
1229 if ( ludp->lud_filter ) {
1230 *filter = str2filter_x( op, ludp->lud_filter );
1231 if ( *filter == NULL ) {
1232 rc = LDAP_PROTOCOL_ERROR;
1235 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
1238 /* Grab the searchbase */
1239 ber_str2bv( ludp->lud_dn, 0, 0, base );
1241 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
1243 ber_dupbv_x( nbase, base, op->o_tmpmemctx );
1248 if( rc != LDAP_SUCCESS ) {
1249 if( *filter ) filter_free_x( op, *filter );
1253 /* Don't free these, return them to caller */
1254 ludp->lud_filter = NULL;
1255 ludp->lud_dn = NULL;
1258 ldap_free_urldesc( ludp );
1262 #ifndef SLAP_AUTH_REWRITE
1263 static int slap_sasl_rx_off(char *rep, int *off)
1268 /* Precompile replace pattern. Find the $<n> placeholders */
1271 for ( c = rep; *c; c++ ) {
1272 if ( *c == '\\' && c[1] ) {
1277 if ( n == SASLREGEX_REPLACE ) {
1278 Debug( LDAP_DEBUG_ANY,
1279 "SASL replace pattern %s has too many $n "
1280 "placeholders (max %d)\n",
1281 rep, SASLREGEX_REPLACE, 0 );
1283 return( LDAP_OTHER );
1290 /* Final placeholder, after the last $n */
1294 return( LDAP_SUCCESS );
1296 #endif /* ! SLAP_AUTH_REWRITE */
1298 #ifdef SLAP_AUTH_REWRITE
1299 int slap_sasl_rewrite_config(
1309 /* init at first call */
1310 if ( sasl_rwinfo == NULL ) {
1311 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1314 /* strip "authid-" prefix for parsing */
1316 argv[0] += STRLENOF( "authid-" );
1317 rc = rewrite_parse( sasl_rwinfo, fname, lineno, argc, argv );
1323 int slap_sasl_rewrite_destroy( void )
1325 if ( sasl_rwinfo ) {
1326 rewrite_info_delete( &sasl_rwinfo );
1333 int slap_sasl_regexp_rewrite_config(
1337 const char *replace,
1338 const char *context )
1341 char *argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL };
1343 /* init at first call */
1344 if ( sasl_rwinfo == NULL ) {
1345 char *argvEngine[] = { "rewriteEngine", "on", NULL };
1346 char *argvContext[] = { "rewriteContext", NULL, NULL };
1348 /* initialize rewrite engine */
1349 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1351 /* switch on rewrite engine */
1352 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine );
1353 if (rc != LDAP_SUCCESS) {
1357 /* create generic authid context */
1358 argvContext[1] = AUTHID_CONTEXT;
1359 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvContext );
1360 if (rc != LDAP_SUCCESS) {
1365 argvRule[1] = (char *)match;
1366 argvRule[2] = (char *)replace;
1367 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule );
1371 #endif /* SLAP_AUTH_REWRITE */
1373 int slap_sasl_regexp_config( const char *match, const char *replace )
1378 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
1379 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
1381 reg = &SaslRegexp[nSaslRegexp];
1383 reg->sr_match = ch_strdup( match );
1384 reg->sr_replace = ch_strdup( replace );
1386 #ifdef SLAP_AUTH_REWRITE
1387 rc = slap_sasl_regexp_rewrite_config( "sasl-regexp", 0,
1388 match, replace, AUTHID_CONTEXT );
1389 if ( rc == LDAP_SUCCESS ) nSaslRegexp++;
1391 #else /* ! SLAP_AUTH_REWRITE */
1393 /* Precompile matching pattern */
1394 rc = regcomp( ®->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
1396 Debug( LDAP_DEBUG_ANY,
1397 "SASL match pattern %s could not be compiled by regexp engine\n",
1398 reg->sr_match, 0, 0 );
1400 #ifdef ENABLE_REWRITE
1401 /* Dummy block to force symbol references in librewrite */
1402 if ( slapMode == ( SLAP_SERVER_MODE|SLAP_TOOL_MODE )) {
1403 rewrite_info_init( 0 );
1406 return( LDAP_OTHER );
1409 rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
1410 if ( rc != LDAP_SUCCESS ) return rc;
1413 return( LDAP_SUCCESS );
1414 #endif /* ! SLAP_AUTH_REWRITE */
1417 void slap_sasl_regexp_unparse( BerVarray *out )
1420 BerVarray bva = NULL;
1421 char ibuf[32], *ptr;
1424 if ( !nSaslRegexp ) return;
1427 bva = ch_malloc( (nSaslRegexp+1) * sizeof(struct berval) );
1428 BER_BVZERO(bva+nSaslRegexp);
1429 for ( i=0; i<nSaslRegexp; i++ ) {
1430 idx.bv_len = sprintf( idx.bv_val, "{%d}", i);
1431 bva[i].bv_len = idx.bv_len + strlen( SaslRegexp[i].sr_match ) +
1432 strlen( SaslRegexp[i].sr_replace ) + 5;
1433 bva[i].bv_val = ch_malloc( bva[i].bv_len+1 );
1434 ptr = lutil_strcopy( bva[i].bv_val, ibuf );
1436 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match );
1437 ptr = lutil_strcopy( ptr, "\" \"" );
1438 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace );
1445 #ifndef SLAP_AUTH_REWRITE
1446 /* Perform replacement on regexp matches */
1447 static void slap_sasl_rx_exp(
1451 const char *saslname,
1455 int i, n, len, insert;
1457 /* Get the total length of the final URI */
1461 while( off[n] >= 0 ) {
1462 /* Len of next section from replacement string (x,y,z above) */
1463 len += off[n] - off[n-1] - 2;
1467 /* Len of string from saslname that matched next $i (b,d above) */
1468 i = rep[ off[n] + 1 ] - '0';
1469 len += str[i].rm_eo - str[i].rm_so;
1472 out->bv_val = slap_sl_malloc( len + 1, ctx );
1475 /* Fill in URI with replace string, replacing $i as we go */
1478 while( off[n] >= 0) {
1479 /* Paste in next section from replacement string (x,y,z above) */
1480 len = off[n] - off[n-1] - 2;
1481 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
1486 /* Paste in string from saslname that matched next $i (b,d above) */
1487 i = rep[ off[n] + 1 ] - '0';
1488 len = str[i].rm_eo - str[i].rm_so;
1489 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
1495 out->bv_val[insert] = '\0';
1497 #endif /* ! SLAP_AUTH_REWRITE */
1499 /* Take the passed in SASL name and attempt to convert it into an
1500 LDAP URI to find the matching LDAP entry, using the pattern matching
1501 strings given in the saslregexp config file directive(s) */
1503 static int slap_authz_regexp( struct berval *in, struct berval *out,
1504 int flags, void *ctx )
1506 #ifdef SLAP_AUTH_REWRITE
1507 const char *context = AUTHID_CONTEXT;
1509 if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
1513 /* FIXME: if aware of authc/authz mapping,
1514 * we could use different contexts ... */
1515 switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL,
1518 case REWRITE_REGEXEC_OK:
1519 if ( !BER_BVISNULL( out ) ) {
1520 char *val = out->bv_val;
1521 ber_str2bv_x( val, 0, 1, out, ctx );
1522 if ( val != in->bv_val ) {
1526 ber_dupbv_x( out, in, ctx );
1528 Debug( LDAP_DEBUG_ARGS,
1529 "[rw] %s: \"%s\" -> \"%s\"\n",
1530 context, in->bv_val, out->bv_val );
1533 case REWRITE_REGEXEC_UNWILLING:
1534 case REWRITE_REGEXEC_ERR:
1539 #else /* ! SLAP_AUTH_REWRITE */
1540 char *saslname = in->bv_val;
1542 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
1545 memset( out, 0, sizeof( *out ) );
1547 Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
1550 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
1554 /* Match the normalized SASL name to the saslregexp patterns */
1555 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
1556 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE,
1557 sr_strings, 0) == 0 )
1561 if( i >= nSaslRegexp ) return( 0 );
1564 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
1565 * replace pattern of the form "x$1y$2z". The returned string needs
1566 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
1568 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
1569 sr_strings, saslname, out, ctx );
1571 Debug( LDAP_DEBUG_TRACE,
1572 "slap_authz_regexp: converted SASL name to %s\n",
1573 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
1576 #endif /* ! SLAP_AUTH_REWRITE */
1579 /* This callback actually does some work...*/
1580 static int sasl_sc_sasl2dn( Operation *op, SlapReply *rs )
1582 struct berval *ndn = op->o_callback->sc_private;
1584 if ( rs->sr_type != REP_SEARCH ) return LDAP_SUCCESS;
1586 /* We only want to be called once */
1587 if ( !BER_BVISNULL( ndn ) ) {
1588 op->o_tmpfree( ndn->bv_val, op->o_tmpmemctx );
1591 Debug( LDAP_DEBUG_TRACE,
1592 "%s: slap_sc_sasl2dn: search DN returned more than 1 entry\n",
1593 op->o_log_prefix, 0, 0 );
1594 return LDAP_UNAVAILABLE; /* short-circuit the search */
1597 ber_dupbv_x( ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
1598 return LDAP_SUCCESS;
1602 typedef struct smatch_info {
1607 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
1609 smatch_info *sm = o->o_callback->sc_private;
1611 if (rs->sr_type != REP_SEARCH) return 0;
1613 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
1615 return LDAP_UNAVAILABLE; /* short-circuit the search */
1622 slap_sasl_matches( Operation *op, BerVarray rules,
1623 struct berval *assertDN, struct berval *authc )
1625 int rc = LDAP_INAPPROPRIATE_AUTH;
1627 if ( rules != NULL ) {
1630 for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
1631 rc = slap_sasl_match( op, &rules[i], assertDN, authc );
1632 if ( rc == LDAP_SUCCESS ) break;
1640 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
1641 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
1642 * the rule must be used as an internal search for entries. If that search
1643 * returns the *assertDN entry, the match is successful.
1645 * The assertDN should not have the dn: prefix
1649 slap_sasl_match( Operation *opx, struct berval *rule,
1650 struct berval *assertDN, struct berval *authc )
1655 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
1657 SlapReply rs = {REP_RESULT};
1658 struct berval base = BER_BVNULL;
1662 cb.sc_private = &sm;
1664 Debug( LDAP_DEBUG_TRACE,
1665 "===>slap_sasl_match: comparing DN %s to rule %s\n",
1666 assertDN->bv_val, rule->bv_val, 0 );
1668 /* NOTE: don't normalize rule if authz syntax is enabled */
1669 rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn,
1670 &op.ors_scope, &op.ors_filter, &op.ors_filterstr,
1671 #ifdef SLAP_AUTHZ_SYNTAX
1673 #else /* ! SLAP_AUTHZ_SYNTAX */
1675 #endif /* ! SLAP_AUTHZ_SYNTAX */
1678 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
1680 switch ( op.ors_scope ) {
1681 case LDAP_X_SCOPE_EXACT:
1683 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
1686 rc = LDAP_INAPPROPRIATE_AUTH;
1690 case LDAP_X_SCOPE_CHILDREN:
1691 case LDAP_X_SCOPE_SUBTREE:
1692 case LDAP_X_SCOPE_ONELEVEL:
1694 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
1696 rc = LDAP_INAPPROPRIATE_AUTH;
1698 if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
1701 } else if ( d > 0 ) {
1704 /* leave room for at least one char of attributeType,
1705 * one for '=' and one for ',' */
1706 if ( d < STRLENOF( "x=,") ) {
1710 bv.bv_len = op.o_req_ndn.bv_len;
1711 bv.bv_val = assertDN->bv_val + d;
1713 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
1714 switch ( op.ors_scope ) {
1715 case LDAP_X_SCOPE_SUBTREE:
1716 case LDAP_X_SCOPE_CHILDREN:
1720 case LDAP_X_SCOPE_ONELEVEL:
1724 dnParent( assertDN, &pdn );
1725 /* the common portion of the DN
1726 * already matches, so only check
1727 * if parent DN of assertedDN
1728 * is all the pattern */
1729 if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
1735 /* at present, impossible */
1743 case LDAP_X_SCOPE_REGEX:
1744 rc = regcomp(®, op.o_req_ndn.bv_val,
1745 REG_EXTENDED|REG_ICASE|REG_NOSUB);
1747 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
1753 rc = LDAP_INAPPROPRIATE_AUTH;
1757 case LDAP_X_SCOPE_GROUP: {
1760 /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
1761 * we need to append the <assertDN> so that the <group_dn> is searched
1762 * with scope "base", and the filter ensures that <assertDN> is
1763 * member of the group */
1764 tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len +
1765 assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 );
1766 if ( tmp == NULL ) {
1767 rc = LDAP_NO_MEMORY;
1770 op.ors_filterstr.bv_val = tmp;
1772 tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
1773 tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
1775 /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
1776 op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
1777 if ( op.ors_filter == NULL ) {
1778 rc = LDAP_PROTOCOL_ERROR;
1781 op.ors_scope = LDAP_SCOPE_BASE;
1783 /* hijack match DN: use that of the group instead of the assertDN;
1784 * assertDN is now in the filter */
1785 sm.dn = &op.o_req_ndn;
1791 case LDAP_X_SCOPE_USERS:
1792 if ( !BER_BVISEMPTY( assertDN ) ) {
1795 rc = LDAP_INAPPROPRIATE_AUTH;
1803 /* Must run an internal search. */
1804 if ( op.ors_filter == NULL ) {
1805 rc = LDAP_FILTER_ERROR;
1809 Debug( LDAP_DEBUG_TRACE,
1810 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
1811 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1813 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1814 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1815 rc = LDAP_INAPPROPRIATE_AUTH;
1819 op.o_hdr = opx->o_hdr;
1820 op.o_tag = LDAP_REQ_SEARCH;
1822 op.o_callback = &cb;
1823 slap_op_time( &op.o_time, &op.o_tincr );
1824 op.o_do_not_cache = 1;
1825 op.o_is_auth_check = 1;
1826 /* use req_ndn as req_dn instead of non-pretty base of uri */
1827 if( !BER_BVISNULL( &base ) ) {
1828 ch_free( base.bv_val );
1829 /* just in case... */
1830 BER_BVZERO( &base );
1832 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1833 op.ors_deref = LDAP_DEREF_NEVER;
1835 op.ors_tlimit = SLAP_NO_LIMIT;
1836 op.ors_attrs = slap_anlist_no_attrs;
1837 op.ors_attrsonly = 1;
1839 op.o_bd->be_search( &op, &rs );
1844 rc = LDAP_INAPPROPRIATE_AUTH;
1848 if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1849 if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1850 if( op.ors_filter ) filter_free_x( opx, op.ors_filter );
1851 if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1853 Debug( LDAP_DEBUG_TRACE,
1854 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
1861 * This function answers the question, "Can this ID authorize to that ID?",
1862 * based on authorization rules. The rules are stored in the *searchDN, in the
1863 * attribute named by *attr. If any of those rules map to the *assertDN, the
1864 * authorization is approved.
1866 * The DNs should not have the dn: prefix
1869 slap_sasl_check_authz( Operation *op,
1870 struct berval *searchDN,
1871 struct berval *assertDN,
1872 AttributeDescription *ad,
1873 struct berval *authc )
1876 do_not_cache = op->o_do_not_cache;
1877 BerVarray vals = NULL;
1879 Debug( LDAP_DEBUG_TRACE,
1880 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
1881 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1883 /* ITS#4760: don't cache group access */
1884 op->o_do_not_cache = 1;
1885 rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
1886 op->o_do_not_cache = do_not_cache;
1887 if( rc != LDAP_SUCCESS ) goto COMPLETE;
1889 /* Check if the *assertDN matches any *vals */
1890 rc = slap_sasl_matches( op, vals, assertDN, authc );
1893 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1895 Debug( LDAP_DEBUG_TRACE,
1896 "<==slap_sasl_check_authz: %s check returning %d\n",
1897 ad->ad_cname.bv_val, rc, 0);
1903 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
1904 * return the LDAP DN to which it matches. The SASL regexp rules in the config
1905 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
1906 * search with scope=base), just return the URI (or its searchbase). Otherwise
1907 * an internal search must be done, and if that search returns exactly one
1908 * entry, return the DN of that one entry.
1913 struct berval *saslname,
1914 struct berval *sasldn,
1918 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
1920 SlapReply rs = {REP_RESULT};
1921 struct berval regout = BER_BVNULL;
1922 struct berval base = BER_BVNULL;
1924 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1925 "converting SASL name %s to a DN\n",
1926 saslname->bv_val, 0,0 );
1928 BER_BVZERO( sasldn );
1929 cb.sc_private = sasldn;
1931 /* Convert the SASL name into a minimal URI */
1932 if( !slap_authz_regexp( saslname, ®out, flags, opx->o_tmpmemctx ) ) {
1936 /* NOTE: always normalize regout because it results
1937 * from string submatch expansion */
1938 rc = slap_parseURI( opx, ®out, &base, &op.o_req_ndn,
1939 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 );
1940 if ( !BER_BVISNULL( ®out ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
1941 if ( rc != LDAP_SUCCESS ) {
1945 /* Must do an internal search */
1946 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1948 switch ( op.ors_scope ) {
1949 case LDAP_X_SCOPE_EXACT:
1950 *sasldn = op.o_req_ndn;
1951 BER_BVZERO( &op.o_req_ndn );
1952 /* intentionally continue to next case */
1954 case LDAP_X_SCOPE_REGEX:
1955 case LDAP_X_SCOPE_SUBTREE:
1956 case LDAP_X_SCOPE_CHILDREN:
1957 case LDAP_X_SCOPE_ONELEVEL:
1958 case LDAP_X_SCOPE_GROUP:
1959 case LDAP_X_SCOPE_USERS:
1960 /* correctly parsed, but illegal */
1963 case LDAP_SCOPE_BASE:
1964 case LDAP_SCOPE_ONELEVEL:
1965 case LDAP_SCOPE_SUBTREE:
1966 case LDAP_SCOPE_SUBORDINATE:
1971 /* catch unhandled cases (there shouldn't be) */
1975 Debug( LDAP_DEBUG_TRACE,
1976 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1977 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1979 if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
1983 /* Must run an internal search. */
1984 if ( op.ors_filter == NULL ) {
1985 rc = LDAP_FILTER_ERROR;
1989 op.o_hdr = opx->o_hdr;
1990 op.o_tag = LDAP_REQ_SEARCH;
1991 op.o_ndn = opx->o_conn->c_ndn;
1992 op.o_callback = &cb;
1993 slap_op_time( &op.o_time, &op.o_tincr );
1994 op.o_do_not_cache = 1;
1995 op.o_is_auth_check = 1;
1996 op.ors_deref = LDAP_DEREF_NEVER;
1998 op.ors_tlimit = SLAP_NO_LIMIT;
1999 op.ors_attrs = slap_anlist_no_attrs;
2000 op.ors_attrsonly = 1;
2001 /* use req_ndn as req_dn instead of non-pretty base of uri */
2002 if( !BER_BVISNULL( &base ) ) {
2003 ch_free( base.bv_val );
2004 /* just in case... */
2005 BER_BVZERO( &base );
2007 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
2009 op.o_bd->be_search( &op, &rs );
2012 if( !BER_BVISEMPTY( sasldn ) ) {
2013 opx->o_conn->c_authz_backend = op.o_bd;
2015 if( !BER_BVISNULL( &op.o_req_dn ) ) {
2016 slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
2018 if( !BER_BVISNULL( &op.o_req_ndn ) ) {
2019 slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
2021 if( op.ors_filter ) {
2022 filter_free_x( opx, op.ors_filter );
2024 if( !BER_BVISNULL( &op.ors_filterstr ) ) {
2025 ch_free( op.ors_filterstr.bv_val );
2028 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
2029 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
2035 /* Check if a bind can SASL authorize to another identity.
2036 * The DNs should not have the dn: prefix
2039 int slap_sasl_authorized( Operation *op,
2040 struct berval *authcDN, struct berval *authzDN )
2042 int rc = LDAP_INAPPROPRIATE_AUTH;
2044 /* User binding as anonymous */
2045 if ( authzDN == NULL ) {
2050 Debug( LDAP_DEBUG_TRACE,
2051 "==>slap_sasl_authorized: can %s become %s?\n",
2052 authcDN->bv_len ? authcDN->bv_val : "(null)",
2053 authzDN->bv_len ? authzDN->bv_val : "(null)", 0 );
2055 /* If person is authorizing to self, succeed */
2056 if ( dn_match( authcDN, authzDN ) ) {
2061 /* Allow the manager to authorize as any DN. */
2062 if( op->o_conn->c_authz_backend &&
2063 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
2069 /* Check source rules */
2070 if( authz_policy & SASL_AUTHZ_TO ) {
2071 rc = slap_sasl_check_authz( op, authcDN, authzDN,
2072 slap_schema.si_ad_saslAuthzTo, authcDN );
2073 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
2078 /* Check destination rules */
2079 if( authz_policy & SASL_AUTHZ_FROM ) {
2080 rc = slap_sasl_check_authz( op, authzDN, authcDN,
2081 slap_schema.si_ad_saslAuthzFrom, authcDN );
2082 if( rc == LDAP_SUCCESS ) {
2087 rc = LDAP_INAPPROPRIATE_AUTH;
2091 Debug( LDAP_DEBUG_TRACE,
2092 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );