2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1998-2005 The OpenLDAP Foundation.
5 * Portions Copyright 2000 Mark Adamson, Carnegie Mellon.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
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 = strchr( id->bv_val, ':' );
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 = strchr( id->bv_val, '.' );
160 if ( !BER_BVISNULL( mech ) ) {
161 mech->bv_val[ 0 ] = '\0';
164 realm->bv_val = strchr( mech->bv_val, '/' );
166 if ( !BER_BVISNULL( realm ) ) {
167 realm->bv_val[ 0 ] = '\0';
169 mech->bv_len = realm->bv_val - mech->bv_val - 1;
170 realm->bv_len = user->bv_val - realm->bv_val - 1;
172 mech->bv_len = user->bv_val - mech->bv_val - 1;
179 if ( id->bv_val[ 1 ] != '\0' ) {
180 return LDAP_PROTOCOL_ERROR;
183 if ( !BER_BVISNULL( mech ) ) {
184 assert( mech->bv_val == id->bv_val + 2 );
186 AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
190 if ( !BER_BVISNULL( realm ) ) {
191 assert( realm->bv_val >= id->bv_val + 2 );
193 AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
197 /* leave "u:" before user */
200 user->bv_val[ 0 ] = u;
201 user->bv_val[ 1 ] = ':';
206 #ifdef SLAP_AUTHZ_SYNTAX
213 int rc = LDAP_INVALID_SYNTAX;
214 LDAPURLDesc *ludp = NULL;
219 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
220 * 3) dn.regex:<pattern>
221 * 4) u[.mech[/realm]]:<ID>
222 * 5) group[/<groupClass>[/<memberAttr>]]:<DN>
226 assert( in != NULL );
227 assert( !BER_BVISNULL( in ) );
229 Debug( LDAP_DEBUG_TRACE,
230 "authzValidate: parsing %s\n", in->bv_val, 0, 0 );
233 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
234 * 3) dn.regex:<pattern>
236 * <DN> must pass DN normalization
238 if ( !strncasecmp( in->bv_val, "dn", STRLENOF( "dn" ) ) ) {
239 bv.bv_val = in->bv_val + STRLENOF( "dn" );
241 if ( bv.bv_val[ 0 ] == '.' ) {
244 if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
245 bv.bv_val += STRLENOF( "exact:" );
246 scope = LDAP_X_SCOPE_EXACT;
248 } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
249 bv.bv_val += STRLENOF( "regex:" );
250 scope = LDAP_X_SCOPE_REGEX;
252 } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
253 bv.bv_val += STRLENOF( "children:" );
254 scope = LDAP_X_SCOPE_CHILDREN;
256 } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
257 bv.bv_val += STRLENOF( "subtree:" );
258 scope = LDAP_X_SCOPE_SUBTREE;
260 } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
261 bv.bv_val += STRLENOF( "onelevel:" );
262 scope = LDAP_X_SCOPE_ONELEVEL;
265 return LDAP_INVALID_SYNTAX;
269 if ( bv.bv_val[ 0 ] != ':' ) {
270 return LDAP_INVALID_SYNTAX;
272 scope = LDAP_X_SCOPE_EXACT;
276 bv.bv_val += strspn( bv.bv_val, " " );
277 /* jump here in case no type specification was present
278 * and uri was not an URI... HEADS-UP: assuming EXACT */
279 is_dn: bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val );
281 /* a single '*' means any DN without using regexes */
282 if ( ber_bvccmp( &bv, '*' ) ) {
283 /* LDAP_X_SCOPE_USERS */
288 case LDAP_X_SCOPE_EXACT:
289 case LDAP_X_SCOPE_CHILDREN:
290 case LDAP_X_SCOPE_SUBTREE:
291 case LDAP_X_SCOPE_ONELEVEL:
292 return dnValidate( NULL, &bv );
294 case LDAP_X_SCOPE_REGEX:
301 * 4) u[.mech[/realm]]:<ID>
303 } else if ( ( in->bv_val[ 0 ] == 'u' || in->bv_val[ 0 ] == 'U' )
304 && ( in->bv_val[ 1 ] == ':'
305 || in->bv_val[ 1 ] == '/'
306 || in->bv_val[ 1 ] == '.' ) )
308 char buf[ SLAP_LDAPDN_MAXLEN ];
314 if ( sizeof( buf ) <= in->bv_len ) {
315 return LDAP_INVALID_SYNTAX;
318 id.bv_len = in->bv_len;
320 strncpy( buf, in->bv_val, sizeof( buf ) );
322 rc = slap_parse_user( &id, &user, &realm, &mech );
323 if ( rc != LDAP_SUCCESS ) {
324 return LDAP_INVALID_SYNTAX;
330 * 5) group[/groupClass[/memberAttr]]:<DN>
332 * <groupClass> defaults to "groupOfNames"
333 * <memberAttr> defaults to "member"
335 * <DN> must pass DN normalization
337 } else if ( strncasecmp( in->bv_val, "group", STRLENOF( "group" ) ) == 0 )
339 struct berval group_dn = BER_BVNULL,
340 group_oc = BER_BVNULL,
341 member_at = BER_BVNULL;
343 bv.bv_val = in->bv_val + STRLENOF( "group" );
344 group_dn.bv_val = strchr( bv.bv_val, ':' );
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 ];
359 member_at.bv_val = strchr( group_oc.bv_val, '/' );
360 if ( member_at.bv_val ) {
361 AttributeDescription *ad = NULL;
362 const char *text = NULL;
364 group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
366 member_at.bv_len = group_dn.bv_val - member_at.bv_val;
367 rc = slap_bv2ad( &member_at, &ad, &text );
368 if ( rc != LDAP_SUCCESS ) {
373 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
375 if ( oc_bvfind( &group_oc ) == NULL ) {
376 return LDAP_INVALID_SYNTAX;
382 group_dn.bv_len = in->bv_len - ( group_dn.bv_val - in->bv_val );
384 rc = dnValidate( NULL, &group_dn );
385 if ( rc != LDAP_SUCCESS ) {
393 * ldap:///<base>??<scope>?<filter>
394 * <scope> ::= {base|one|subtree}
396 * <scope> defaults to "base"
397 * <base> must pass DN normalization
398 * <filter> must pass str2filter()
400 rc = ldap_url_parse( in->bv_val, &ludp );
402 case LDAP_URL_SUCCESS:
403 /* FIXME: the check is pedantic, but I think it's necessary,
404 * because people tend to use things like ldaps:// which
405 * gives the idea SSL is being used. Maybe we could
406 * accept ldapi:// as well, but the point is that we use
407 * an URL as an easy means to define bits of a search with
410 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
414 rc = LDAP_INVALID_SYNTAX;
419 case LDAP_URL_ERR_BADSCHEME:
421 * last chance: assume it's a(n exact) DN ...
423 * NOTE: must pass DN normalization
425 ldap_free_urldesc( ludp );
426 bv.bv_val = in->bv_val;
427 scope = LDAP_X_SCOPE_EXACT;
431 rc = LDAP_INVALID_SYNTAX;
435 if ( ( ludp->lud_host && *ludp->lud_host )
436 || ludp->lud_attrs || ludp->lud_exts )
438 /* host part must be empty */
439 /* attrs and extensions parts must be empty */
440 rc = LDAP_INVALID_SYNTAX;
444 /* Grab the filter */
445 if ( ludp->lud_filter ) {
446 Filter *f = str2filter( ludp->lud_filter );
448 rc = LDAP_INVALID_SYNTAX;
454 /* Grab the searchbase */
455 assert( ludp->lud_dn != NULL );
456 ber_str2bv( ludp->lud_dn, 0, 0, &bv );
457 rc = dnValidate( NULL, &bv );
460 ldap_free_urldesc( ludp );
471 struct berval *value,
472 void *assertedValue )
474 return octetStringMatch( matchp, flags, syntax, mr, value, assertedValue );
481 struct berval *normalized,
486 int rc = LDAP_INVALID_SYNTAX;
487 LDAPURLDesc *ludp = NULL;
494 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
495 * 3) dn.regex:<pattern>
496 * 4) u[.mech[/realm]]:<ID>
497 * 5) group[/<groupClass>[/<memberAttr>]]:<DN>
501 assert( val != NULL );
502 assert( !BER_BVISNULL( val ) );
505 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
506 * 3) dn.regex:<pattern>
508 * <DN> must pass DN normalization
510 if ( !strncasecmp( val->bv_val, "dn", STRLENOF( "dn" ) ) ) {
511 struct berval out = BER_BVNULL,
515 bv.bv_val = val->bv_val + STRLENOF( "dn" );
517 if ( bv.bv_val[ 0 ] == '.' ) {
520 if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
521 bv.bv_val += STRLENOF( "exact:" );
522 scope = LDAP_X_SCOPE_EXACT;
524 } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
525 bv.bv_val += STRLENOF( "regex:" );
526 scope = LDAP_X_SCOPE_REGEX;
528 } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
529 bv.bv_val += STRLENOF( "children:" );
530 scope = LDAP_X_SCOPE_CHILDREN;
532 } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
533 bv.bv_val += STRLENOF( "subtree:" );
534 scope = LDAP_X_SCOPE_SUBTREE;
536 } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
537 bv.bv_val += STRLENOF( "onelevel:" );
538 scope = LDAP_X_SCOPE_ONELEVEL;
541 return LDAP_INVALID_SYNTAX;
545 if ( bv.bv_val[ 0 ] != ':' ) {
546 return LDAP_INVALID_SYNTAX;
548 scope = LDAP_X_SCOPE_EXACT;
552 bv.bv_val += strspn( bv.bv_val, " " );
553 /* jump here in case no type specification was present
554 * and uri was not an URI... HEADS-UP: assuming EXACT */
555 is_dn: bv.bv_len = val->bv_len - ( bv.bv_val - val->bv_val );
557 /* a single '*' means any DN without using regexes */
558 if ( ber_bvccmp( &bv, '*' ) ) {
559 ber_str2bv_x( "dn:*", STRLENOF( "dn:*" ), 1, normalized, ctx );
564 case LDAP_X_SCOPE_EXACT:
565 case LDAP_X_SCOPE_CHILDREN:
566 case LDAP_X_SCOPE_SUBTREE:
567 case LDAP_X_SCOPE_ONELEVEL:
569 rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
571 rc = dnPretty( NULL, &bv, &out, ctx );
573 if( rc != LDAP_SUCCESS ) {
574 return LDAP_INVALID_SYNTAX;
578 case LDAP_X_SCOPE_REGEX:
579 normalized->bv_len = STRLENOF( "dn.regex:" ) + bv.bv_len;
580 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
581 ptr = lutil_strcopy( normalized->bv_val, "dn.regex:" );
582 ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
587 return LDAP_INVALID_SYNTAX;
592 case LDAP_X_SCOPE_EXACT:
593 BER_BVSTR( &prefix, "dn:" );
596 case LDAP_X_SCOPE_CHILDREN:
597 BER_BVSTR( &prefix, "dn.children:" );
600 case LDAP_X_SCOPE_SUBTREE:
601 BER_BVSTR( &prefix, "dn.subtree:" );
604 case LDAP_X_SCOPE_ONELEVEL:
605 BER_BVSTR( &prefix, "dn.onelevel:" );
613 normalized->bv_len = prefix.bv_len + out.bv_len;
614 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
616 ptr = lutil_strcopy( normalized->bv_val, prefix.bv_val );
617 ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
619 ber_memfree_x( out.bv_val, ctx );
624 * 4) u[.mech[/realm]]:<ID>
626 } else if ( ( val->bv_val[ 0 ] == 'u' || val->bv_val[ 0 ] == 'U' )
627 && ( val->bv_val[ 1 ] == ':'
628 || val->bv_val[ 1 ] == '/'
629 || val->bv_val[ 1 ] == '.' ) )
631 char buf[ SLAP_LDAPDN_MAXLEN ];
637 if ( sizeof( buf ) <= val->bv_len ) {
638 return LDAP_INVALID_SYNTAX;
641 id.bv_len = val->bv_len;
643 strncpy( buf, val->bv_val, sizeof( buf ) );
645 rc = slap_parse_user( &id, &user, &realm, &mech );
646 if ( rc != LDAP_SUCCESS ) {
647 return LDAP_INVALID_SYNTAX;
650 ber_dupbv_x( normalized, val, ctx );
655 * 5) group[/groupClass[/memberAttr]]:<DN>
657 * <groupClass> defaults to "groupOfNames"
658 * <memberAttr> defaults to "member"
660 * <DN> must pass DN normalization
662 } else if ( strncasecmp( val->bv_val, "group", STRLENOF( "group" ) ) == 0 )
664 struct berval group_dn = BER_BVNULL,
665 group_oc = BER_BVNULL,
666 member_at = BER_BVNULL,
670 bv.bv_val = val->bv_val + STRLENOF( "group" );
671 group_dn.bv_val = strchr( bv.bv_val, ':' );
672 if ( group_dn.bv_val == NULL ) {
673 /* last chance: assume it's a(n exact) DN ... */
674 bv.bv_val = val->bv_val;
675 scope = LDAP_X_SCOPE_EXACT;
680 * FIXME: we assume that "member" and "groupOfNames"
681 * are present in schema...
683 if ( bv.bv_val[ 0 ] == '/' ) {
684 group_oc.bv_val = &bv.bv_val[ 1 ];
686 member_at.bv_val = strchr( group_oc.bv_val, '/' );
687 if ( member_at.bv_val ) {
688 AttributeDescription *ad = NULL;
689 const char *text = NULL;
691 group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
693 member_at.bv_len = group_dn.bv_val - member_at.bv_val;
694 rc = slap_bv2ad( &member_at, &ad, &text );
695 if ( rc != LDAP_SUCCESS ) {
699 member_at = ad->ad_cname;
702 ObjectClass *oc = NULL;
704 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
706 oc = oc_bvfind( &group_oc );
708 return LDAP_INVALID_SYNTAX;
711 group_oc = oc->soc_cname;
716 group_dn.bv_len = val->bv_len - ( group_dn.bv_val - val->bv_val );
719 rc = dnNormalize( 0, NULL, NULL, &group_dn, &out, ctx );
721 rc = dnPretty( NULL, &group_dn, &out, ctx );
723 if ( rc != LDAP_SUCCESS ) {
727 normalized->bv_len = STRLENOF( "group" ":" ) + out.bv_len;
728 if ( !BER_BVISNULL( &group_oc ) ) {
729 normalized->bv_len += STRLENOF( "/" ) + group_oc.bv_len;
730 if ( !BER_BVISNULL( &member_at ) ) {
731 normalized->bv_len += STRLENOF( "/" ) + member_at.bv_len;
735 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
736 ptr = lutil_strcopy( normalized->bv_val, "group" );
737 if ( !BER_BVISNULL( &group_oc ) ) {
740 ptr = lutil_strncopy( ptr, group_oc.bv_val, group_oc.bv_len );
741 if ( !BER_BVISNULL( &member_at ) ) {
744 ptr = lutil_strncopy( ptr, member_at.bv_val, member_at.bv_len );
749 ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
751 ber_memfree_x( out.bv_val, ctx );
757 * ldap:///<base>??<scope>?<filter>
758 * <scope> ::= {base|one|subtree}
760 * <scope> defaults to "base"
761 * <base> must pass DN normalization
762 * <filter> must pass str2filter()
764 rc = ldap_url_parse( val->bv_val, &ludp );
766 case LDAP_URL_SUCCESS:
767 /* FIXME: the check is pedantic, but I think it's necessary,
768 * because people tend to use things like ldaps:// which
769 * gives the idea SSL is being used. Maybe we could
770 * accept ldapi:// as well, but the point is that we use
771 * an URL as an easy means to define bits of a search with
774 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
778 rc = LDAP_INVALID_SYNTAX;
782 AC_MEMCPY( ludp->lud_scheme, "ldap", STRLENOF( "ldap" ) );
785 case LDAP_URL_ERR_BADSCHEME:
787 * last chance: assume it's a(n exact) DN ...
789 * NOTE: must pass DN normalization
791 ldap_free_urldesc( ludp );
792 bv.bv_val = val->bv_val;
793 scope = LDAP_X_SCOPE_EXACT;
797 rc = LDAP_INVALID_SYNTAX;
801 if ( ( ludp->lud_host && *ludp->lud_host )
802 || ludp->lud_attrs || ludp->lud_exts )
804 /* host part must be empty */
805 /* attrs and extensions parts must be empty */
806 rc = LDAP_INVALID_SYNTAX;
810 /* Grab the filter */
811 if ( ludp->lud_filter ) {
812 struct berval filterstr;
815 lud_filter = ludp->lud_filter;
817 f = str2filter( lud_filter );
819 rc = LDAP_INVALID_SYNTAX;
822 filter2bv( f, &filterstr );
824 if ( BER_BVISNULL( &filterstr ) ) {
825 rc = LDAP_INVALID_SYNTAX;
829 ludp->lud_filter = filterstr.bv_val;
832 /* Grab the searchbase */
833 assert( ludp->lud_dn != NULL );
834 if ( ludp->lud_dn ) {
835 struct berval out = BER_BVNULL;
837 lud_dn = ludp->lud_dn;
839 ber_str2bv( lud_dn, 0, 0, &bv );
841 rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
843 rc = dnPretty( NULL, &bv, &out, ctx );
846 if ( rc != LDAP_SUCCESS ) {
850 ludp->lud_dn = out.bv_val;
854 normalized->bv_val = ldap_url_desc2str( ludp );
855 if ( normalized->bv_val ) {
856 normalized->bv_len = strlen( normalized->bv_val );
859 rc = LDAP_INVALID_SYNTAX;
864 if ( ludp->lud_filter != lud_filter ) {
865 ber_memfree( ludp->lud_filter );
867 ludp->lud_filter = lud_filter;
871 if ( ludp->lud_dn != lud_dn ) {
872 ber_memfree( ludp->lud_dn );
874 ludp->lud_dn = lud_dn;
877 ldap_free_urldesc( ludp );
888 struct berval *normalized,
893 Debug( LDAP_DEBUG_TRACE, ">>> authzNormalize: <%s>\n",
896 rc = authzPrettyNormal( val, normalized, ctx, 1 );
898 Debug( LDAP_DEBUG_TRACE, "<<< authzNormalize: <%s> (%d)\n",
899 normalized->bv_val, rc, 0 );
913 Debug( LDAP_DEBUG_TRACE, ">>> authzPretty: <%s>\n",
916 rc = authzPrettyNormal( val, out, ctx, 0 );
918 Debug( LDAP_DEBUG_TRACE, "<<< authzPretty: <%s> (%d)\n",
919 out->bv_val, rc, 0 );
924 #endif /* SLAP_AUTHZ_SYNTAX */
931 struct berval *nbase,
941 #ifdef SLAP_ORDERED_PRETTYNORM
943 #endif /* SLAP_ORDERED_PRETTYNORM */
945 assert( uri != NULL && !BER_BVISNULL( uri ) );
952 Debug( LDAP_DEBUG_TRACE,
953 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
955 rc = LDAP_PROTOCOL_ERROR;
957 #ifdef SLAP_ORDERED_PRETTYNORM
959 if ( idx.bv_val[ 0 ] == '{' ) {
962 ptr = strchr( idx.bv_val, '}' ) + 1;
964 assert( ptr != (void *)1 );
966 idx.bv_len -= ptr - idx.bv_val;
970 #endif /* SLAP_ORDERED_PRETTYNORM */
973 * dn[.<dnstyle>]:<dnpattern>
974 * <dnstyle> ::= {exact|regex|children|subtree|onelevel}
976 * <dnstyle> defaults to "exact"
977 * if <dnstyle> is not "regex", <dnpattern> must pass DN normalization
979 if ( !strncasecmp( uri->bv_val, "dn", STRLENOF( "dn" ) ) ) {
980 bv.bv_val = uri->bv_val + STRLENOF( "dn" );
982 if ( bv.bv_val[ 0 ] == '.' ) {
985 if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
986 bv.bv_val += STRLENOF( "exact:" );
987 *scope = LDAP_X_SCOPE_EXACT;
989 } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
990 bv.bv_val += STRLENOF( "regex:" );
991 *scope = LDAP_X_SCOPE_REGEX;
993 } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
994 bv.bv_val += STRLENOF( "children:" );
995 *scope = LDAP_X_SCOPE_CHILDREN;
997 } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
998 bv.bv_val += STRLENOF( "subtree:" );
999 *scope = LDAP_X_SCOPE_SUBTREE;
1001 } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
1002 bv.bv_val += STRLENOF( "onelevel:" );
1003 *scope = LDAP_X_SCOPE_ONELEVEL;
1006 return LDAP_PROTOCOL_ERROR;
1010 if ( bv.bv_val[ 0 ] != ':' ) {
1011 return LDAP_PROTOCOL_ERROR;
1013 *scope = LDAP_X_SCOPE_EXACT;
1017 bv.bv_val += strspn( bv.bv_val, " " );
1018 /* jump here in case no type specification was present
1019 * and uri was not an URI... HEADS-UP: assuming EXACT */
1020 is_dn: bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
1022 /* a single '*' means any DN without using regexes */
1023 if ( ber_bvccmp( &bv, '*' ) ) {
1024 *scope = LDAP_X_SCOPE_USERS;
1028 case LDAP_X_SCOPE_EXACT:
1029 case LDAP_X_SCOPE_CHILDREN:
1030 case LDAP_X_SCOPE_SUBTREE:
1031 case LDAP_X_SCOPE_ONELEVEL:
1033 rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
1034 if( rc != LDAP_SUCCESS ) {
1038 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1043 case LDAP_X_SCOPE_REGEX:
1044 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1046 case LDAP_X_SCOPE_USERS:
1060 } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
1061 && ( uri->bv_val[ 1 ] == ':'
1062 || uri->bv_val[ 1 ] == '/'
1063 || uri->bv_val[ 1 ] == '.' ) )
1065 Connection c = *op->o_conn;
1066 char buf[ SLAP_LDAPDN_MAXLEN ];
1072 if ( sizeof( buf ) <= uri->bv_len ) {
1073 return LDAP_INVALID_SYNTAX;
1076 id.bv_len = uri->bv_len;
1078 strncpy( buf, uri->bv_val, sizeof( buf ) );
1080 rc = slap_parse_user( &id, &user, &realm, &mech );
1081 if ( rc != LDAP_SUCCESS ) {
1085 if ( !BER_BVISNULL( &mech ) ) {
1086 c.c_sasl_bind_mech = mech;
1088 BER_BVSTR( &c.c_sasl_bind_mech, "AUTHZ" );
1091 rc = slap_sasl_getdn( &c, op, &user,
1092 realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
1094 if ( rc == LDAP_SUCCESS ) {
1095 *scope = LDAP_X_SCOPE_EXACT;
1101 * group[/<groupoc>[/<groupat>]]:<groupdn>
1103 * groupoc defaults to "groupOfNames"
1104 * groupat defaults to "member"
1106 * <groupdn> must pass DN normalization
1108 } else if ( strncasecmp( uri->bv_val, "group", STRLENOF( "group" ) ) == 0 )
1110 struct berval group_dn = BER_BVNULL,
1111 group_oc = BER_BVNULL,
1112 member_at = BER_BVNULL;
1115 bv.bv_val = uri->bv_val + STRLENOF( "group" );
1116 group_dn.bv_val = strchr( bv.bv_val, ':' );
1117 if ( group_dn.bv_val == NULL ) {
1118 /* last chance: assume it's a(n exact) DN ... */
1119 bv.bv_val = uri->bv_val;
1120 *scope = LDAP_X_SCOPE_EXACT;
1124 if ( bv.bv_val[ 0 ] == '/' ) {
1125 group_oc.bv_val = &bv.bv_val[ 1 ];
1127 member_at.bv_val = strchr( group_oc.bv_val, '/' );
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 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
1135 BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
1139 BER_BVSTR( &group_oc, SLAPD_GROUP_CLASS );
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 *o, SlapReply *rs )
1582 struct berval *ndn = o->o_callback->sc_private;
1584 if (rs->sr_type != REP_SEARCH) return 0;
1586 /* We only want to be called once */
1587 if ( !BER_BVISNULL( ndn ) ) {
1588 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
1591 Debug( LDAP_DEBUG_TRACE,
1592 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
1596 ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
1601 typedef struct smatch_info {
1606 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
1608 smatch_info *sm = o->o_callback->sc_private;
1610 if ( rs->sr_type != REP_SEARCH ) {
1611 if ( rs->sr_err != LDAP_SUCCESS ) {
1617 if ( sm->match == 1 ) {
1622 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
1633 slap_sasl_matches( Operation *op, BerVarray rules,
1634 struct berval *assertDN, struct berval *authc )
1636 int rc = LDAP_INAPPROPRIATE_AUTH;
1638 if ( rules != NULL ) {
1641 for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
1642 rc = slap_sasl_match( op, &rules[i], assertDN, authc );
1643 if ( rc == LDAP_SUCCESS ) break;
1651 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
1652 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
1653 * the rule must be used as an internal search for entries. If that search
1654 * returns the *assertDN entry, the match is successful.
1656 * The assertDN should not have the dn: prefix
1660 slap_sasl_match( Operation *opx, struct berval *rule,
1661 struct berval *assertDN, struct berval *authc )
1666 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
1668 SlapReply rs = {REP_RESULT};
1669 struct berval base = BER_BVNULL;
1673 cb.sc_private = &sm;
1675 Debug( LDAP_DEBUG_TRACE,
1676 "===>slap_sasl_match: comparing DN %s to rule %s\n",
1677 assertDN->bv_val, rule->bv_val, 0 );
1679 /* NOTE: don't normalize rule if authz syntax is enabled */
1680 rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn,
1681 &op.ors_scope, &op.ors_filter, &op.ors_filterstr,
1682 #ifdef SLAP_AUTHZ_SYNTAX
1684 #else /* ! SLAP_AUTHZ_SYNTAX */
1686 #endif /* ! SLAP_AUTHZ_SYNTAX */
1689 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
1691 switch ( op.ors_scope ) {
1692 case LDAP_X_SCOPE_EXACT:
1694 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
1697 rc = LDAP_INAPPROPRIATE_AUTH;
1701 case LDAP_X_SCOPE_CHILDREN:
1702 case LDAP_X_SCOPE_SUBTREE:
1703 case LDAP_X_SCOPE_ONELEVEL:
1705 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
1707 rc = LDAP_INAPPROPRIATE_AUTH;
1709 if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
1712 } else if ( d > 0 ) {
1715 /* leave room for at least one char of attributeType,
1716 * one for '=' and one for ',' */
1717 if ( d < STRLENOF( "x=,") ) {
1721 bv.bv_len = op.o_req_ndn.bv_len;
1722 bv.bv_val = assertDN->bv_val + d;
1724 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
1725 switch ( op.ors_scope ) {
1726 case LDAP_X_SCOPE_SUBTREE:
1727 case LDAP_X_SCOPE_CHILDREN:
1731 case LDAP_X_SCOPE_ONELEVEL:
1735 dnParent( assertDN, &pdn );
1736 /* the common portion of the DN
1737 * already matches, so only check
1738 * if parent DN of assertedDN
1739 * is all the pattern */
1740 if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
1746 /* at present, impossible */
1754 case LDAP_X_SCOPE_REGEX:
1755 rc = regcomp(®, op.o_req_ndn.bv_val,
1756 REG_EXTENDED|REG_ICASE|REG_NOSUB);
1758 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
1764 rc = LDAP_INAPPROPRIATE_AUTH;
1768 case LDAP_X_SCOPE_GROUP: {
1771 /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
1772 * we need to append the <assertDN> so that the <group_dn> is searched
1773 * with scope "base", and the filter ensures that <assertDN> is
1774 * member of the group */
1775 tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len +
1776 assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 );
1777 if ( tmp == NULL ) {
1778 rc = LDAP_NO_MEMORY;
1781 op.ors_filterstr.bv_val = tmp;
1783 tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
1784 tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
1786 /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
1787 op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
1788 if ( op.ors_filter == NULL ) {
1789 rc = LDAP_PROTOCOL_ERROR;
1792 op.ors_scope = LDAP_SCOPE_BASE;
1794 /* hijack match DN: use that of the group instead of the assertDN;
1795 * assertDN is now in the filter */
1796 sm.dn = &op.o_req_ndn;
1802 case LDAP_X_SCOPE_USERS:
1803 if ( !BER_BVISEMPTY( assertDN ) ) {
1806 rc = LDAP_INAPPROPRIATE_AUTH;
1814 /* Must run an internal search. */
1815 if ( op.ors_filter == NULL ) {
1816 rc = LDAP_FILTER_ERROR;
1820 Debug( LDAP_DEBUG_TRACE,
1821 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
1822 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1824 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1825 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1826 rc = LDAP_INAPPROPRIATE_AUTH;
1830 op.o_hdr = opx->o_hdr;
1831 op.o_tag = LDAP_REQ_SEARCH;
1833 op.o_callback = &cb;
1834 slap_op_time( &op.o_time, &op.o_tincr );
1835 op.o_do_not_cache = 1;
1836 op.o_is_auth_check = 1;
1837 /* use req_ndn as req_dn instead of non-pretty base of uri */
1838 if( !BER_BVISNULL( &base ) ) {
1839 ch_free( base.bv_val );
1840 /* just in case... */
1841 BER_BVZERO( &base );
1843 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1844 op.ors_deref = LDAP_DEREF_NEVER;
1846 op.ors_tlimit = SLAP_NO_LIMIT;
1847 op.ors_attrs = slap_anlist_no_attrs;
1848 op.ors_attrsonly = 1;
1850 op.o_bd->be_search( &op, &rs );
1852 if (sm.match == 1) {
1855 rc = LDAP_INAPPROPRIATE_AUTH;
1859 if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1860 if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1861 if( op.ors_filter ) filter_free_x( opx, op.ors_filter );
1862 if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1864 Debug( LDAP_DEBUG_TRACE,
1865 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
1872 * This function answers the question, "Can this ID authorize to that ID?",
1873 * based on authorization rules. The rules are stored in the *searchDN, in the
1874 * attribute named by *attr. If any of those rules map to the *assertDN, the
1875 * authorization is approved.
1877 * The DNs should not have the dn: prefix
1880 slap_sasl_check_authz( Operation *op,
1881 struct berval *searchDN,
1882 struct berval *assertDN,
1883 AttributeDescription *ad,
1884 struct berval *authc )
1887 BerVarray vals = NULL;
1889 Debug( LDAP_DEBUG_TRACE,
1890 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
1891 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1893 rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
1894 if( rc != LDAP_SUCCESS ) goto COMPLETE;
1896 /* Check if the *assertDN matches any *vals */
1897 rc = slap_sasl_matches( op, vals, assertDN, authc );
1900 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1902 Debug( LDAP_DEBUG_TRACE,
1903 "<==slap_sasl_check_authz: %s check returning %d\n",
1904 ad->ad_cname.bv_val, rc, 0);
1910 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
1911 * return the LDAP DN to which it matches. The SASL regexp rules in the config
1912 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
1913 * search with scope=base), just return the URI (or its searchbase). Otherwise
1914 * an internal search must be done, and if that search returns exactly one
1915 * entry, return the DN of that one entry.
1920 struct berval *saslname,
1921 struct berval *sasldn,
1925 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
1927 SlapReply rs = {REP_RESULT};
1928 struct berval regout = BER_BVNULL;
1929 struct berval base = BER_BVNULL;
1931 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1932 "converting SASL name %s to a DN\n",
1933 saslname->bv_val, 0,0 );
1935 BER_BVZERO( sasldn );
1936 cb.sc_private = sasldn;
1938 /* Convert the SASL name into a minimal URI */
1939 if( !slap_authz_regexp( saslname, ®out, flags, opx->o_tmpmemctx ) ) {
1943 /* NOTE: always normalize regout because it results
1944 * from string submatch expansion */
1945 rc = slap_parseURI( opx, ®out, &base, &op.o_req_ndn,
1946 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 );
1947 if ( !BER_BVISNULL( ®out ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
1948 if ( rc != LDAP_SUCCESS ) {
1952 /* Must do an internal search */
1953 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1955 switch ( op.ors_scope ) {
1956 case LDAP_X_SCOPE_EXACT:
1957 *sasldn = op.o_req_ndn;
1958 BER_BVZERO( &op.o_req_ndn );
1959 /* intentionally continue to next case */
1961 case LDAP_X_SCOPE_REGEX:
1962 case LDAP_X_SCOPE_SUBTREE:
1963 case LDAP_X_SCOPE_CHILDREN:
1964 case LDAP_X_SCOPE_ONELEVEL:
1965 case LDAP_X_SCOPE_GROUP:
1966 case LDAP_X_SCOPE_USERS:
1967 /* correctly parsed, but illegal */
1970 case LDAP_SCOPE_BASE:
1971 case LDAP_SCOPE_ONELEVEL:
1972 case LDAP_SCOPE_SUBTREE:
1973 #ifdef LDAP_SCOPE_SUBORDINATE
1974 case LDAP_SCOPE_SUBORDINATE:
1980 /* catch unhandled cases (there shouldn't be) */
1984 Debug( LDAP_DEBUG_TRACE,
1985 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1986 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1988 if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
1992 /* Must run an internal search. */
1993 if ( op.ors_filter == NULL ) {
1994 rc = LDAP_FILTER_ERROR;
1998 op.o_hdr = opx->o_hdr;
1999 op.o_tag = LDAP_REQ_SEARCH;
2000 op.o_ndn = opx->o_conn->c_ndn;
2001 op.o_callback = &cb;
2002 slap_op_time( &op.o_time, &op.o_tincr );
2003 op.o_do_not_cache = 1;
2004 op.o_is_auth_check = 1;
2005 op.ors_deref = LDAP_DEREF_NEVER;
2007 op.ors_tlimit = SLAP_NO_LIMIT;
2008 op.ors_attrs = slap_anlist_no_attrs;
2009 op.ors_attrsonly = 1;
2010 /* use req_ndn as req_dn instead of non-pretty base of uri */
2011 if( !BER_BVISNULL( &base ) ) {
2012 ch_free( base.bv_val );
2013 /* just in case... */
2014 BER_BVZERO( &base );
2016 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
2018 op.o_bd->be_search( &op, &rs );
2021 if( !BER_BVISEMPTY( sasldn ) ) {
2022 opx->o_conn->c_authz_backend = op.o_bd;
2024 if( !BER_BVISNULL( &op.o_req_dn ) ) {
2025 slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
2027 if( !BER_BVISNULL( &op.o_req_ndn ) ) {
2028 slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
2030 if( op.ors_filter ) {
2031 filter_free_x( opx, op.ors_filter );
2033 if( !BER_BVISNULL( &op.ors_filterstr ) ) {
2034 ch_free( op.ors_filterstr.bv_val );
2037 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
2038 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
2044 /* Check if a bind can SASL authorize to another identity.
2045 * The DNs should not have the dn: prefix
2048 int slap_sasl_authorized( Operation *op,
2049 struct berval *authcDN, struct berval *authzDN )
2051 int rc = LDAP_INAPPROPRIATE_AUTH;
2053 /* User binding as anonymous */
2054 if ( authzDN == NULL ) {
2059 Debug( LDAP_DEBUG_TRACE,
2060 "==>slap_sasl_authorized: can %s become %s?\n",
2061 authcDN->bv_val, authzDN->bv_val, 0 );
2063 /* If person is authorizing to self, succeed */
2064 if ( dn_match( authcDN, authzDN ) ) {
2069 /* Allow the manager to authorize as any DN. */
2070 if( op->o_conn->c_authz_backend &&
2071 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
2077 /* Check source rules */
2078 if( authz_policy & SASL_AUTHZ_TO ) {
2079 rc = slap_sasl_check_authz( op, authcDN, authzDN,
2080 slap_schema.si_ad_saslAuthzTo, authcDN );
2081 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
2086 /* Check destination rules */
2087 if( authz_policy & SASL_AUTHZ_FROM ) {
2088 rc = slap_sasl_check_authz( op, authzDN, authcDN,
2089 slap_schema.si_ad_saslAuthzFrom, authcDN );
2090 if( rc == LDAP_SUCCESS ) {
2095 rc = LDAP_INAPPROPRIATE_AUTH;
2099 Debug( LDAP_DEBUG_TRACE,
2100 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );