2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1998-2009 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 ] = ':';
211 int rc = LDAP_INVALID_SYNTAX;
212 LDAPURLDesc *ludp = NULL;
217 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
218 * 3) dn.regex:<pattern>
219 * 4) u[.mech[/realm]]:<ID>
220 * 5) group[/<groupClass>[/<memberAttr>]]:<DN>
224 assert( in != NULL );
225 assert( !BER_BVISNULL( in ) );
227 Debug( LDAP_DEBUG_TRACE,
228 "authzValidate: parsing %s\n", in->bv_val, 0, 0 );
231 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
232 * 3) dn.regex:<pattern>
234 * <DN> must pass DN normalization
236 if ( !strncasecmp( in->bv_val, "dn", STRLENOF( "dn" ) ) ) {
237 bv.bv_val = in->bv_val + STRLENOF( "dn" );
239 if ( bv.bv_val[ 0 ] == '.' ) {
242 if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
243 bv.bv_val += STRLENOF( "exact:" );
244 scope = LDAP_X_SCOPE_EXACT;
246 } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
247 bv.bv_val += STRLENOF( "regex:" );
248 scope = LDAP_X_SCOPE_REGEX;
250 } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
251 bv.bv_val += STRLENOF( "children:" );
252 scope = LDAP_X_SCOPE_CHILDREN;
254 } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
255 bv.bv_val += STRLENOF( "subtree:" );
256 scope = LDAP_X_SCOPE_SUBTREE;
258 } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
259 bv.bv_val += STRLENOF( "onelevel:" );
260 scope = LDAP_X_SCOPE_ONELEVEL;
263 return LDAP_INVALID_SYNTAX;
267 if ( bv.bv_val[ 0 ] != ':' ) {
268 return LDAP_INVALID_SYNTAX;
270 scope = LDAP_X_SCOPE_EXACT;
274 bv.bv_val += strspn( bv.bv_val, " " );
275 /* jump here in case no type specification was present
276 * and uri was not an URI... HEADS-UP: assuming EXACT */
277 is_dn: bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val );
279 /* a single '*' means any DN without using regexes */
280 if ( ber_bvccmp( &bv, '*' ) ) {
281 /* LDAP_X_SCOPE_USERS */
286 case LDAP_X_SCOPE_EXACT:
287 case LDAP_X_SCOPE_CHILDREN:
288 case LDAP_X_SCOPE_SUBTREE:
289 case LDAP_X_SCOPE_ONELEVEL:
290 return dnValidate( NULL, &bv );
292 case LDAP_X_SCOPE_REGEX:
299 * 4) u[.mech[/realm]]:<ID>
301 } else if ( ( in->bv_val[ 0 ] == 'u' || in->bv_val[ 0 ] == 'U' )
302 && ( in->bv_val[ 1 ] == ':'
303 || in->bv_val[ 1 ] == '/'
304 || in->bv_val[ 1 ] == '.' ) )
306 char buf[ SLAP_LDAPDN_MAXLEN ];
312 if ( sizeof( buf ) <= in->bv_len ) {
313 return LDAP_INVALID_SYNTAX;
316 id.bv_len = in->bv_len;
318 strncpy( buf, in->bv_val, sizeof( buf ) );
320 rc = slap_parse_user( &id, &user, &realm, &mech );
321 if ( rc != LDAP_SUCCESS ) {
322 return LDAP_INVALID_SYNTAX;
328 * 5) group[/groupClass[/memberAttr]]:<DN>
330 * <groupClass> defaults to "groupOfNames"
331 * <memberAttr> defaults to "member"
333 * <DN> must pass DN normalization
335 } else if ( strncasecmp( in->bv_val, "group", STRLENOF( "group" ) ) == 0 )
337 struct berval group_dn = BER_BVNULL,
338 group_oc = BER_BVNULL,
339 member_at = BER_BVNULL;
341 bv.bv_val = in->bv_val + STRLENOF( "group" );
342 bv.bv_len = in->bv_len - STRLENOF( "group" );
343 group_dn.bv_val = ber_bvchr( &bv, ':' );
344 if ( group_dn.bv_val == NULL ) {
345 /* last chance: assume it's a(n exact) DN ... */
346 bv.bv_val = in->bv_val;
347 scope = LDAP_X_SCOPE_EXACT;
352 * FIXME: we assume that "member" and "groupOfNames"
353 * are present in schema...
355 if ( bv.bv_val[ 0 ] == '/' ) {
356 group_oc.bv_val = &bv.bv_val[ 1 ];
357 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
359 member_at.bv_val = ber_bvchr( &group_oc, '/' );
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 if ( oc_bvfind( &group_oc ) == NULL ) {
374 return LDAP_INVALID_SYNTAX;
379 group_dn.bv_len = in->bv_len - ( group_dn.bv_val - in->bv_val );
381 rc = dnValidate( NULL, &group_dn );
382 if ( rc != LDAP_SUCCESS ) {
390 * ldap:///<base>??<scope>?<filter>
391 * <scope> ::= {base|one|subtree}
393 * <scope> defaults to "base"
394 * <base> must pass DN normalization
395 * <filter> must pass str2filter()
397 rc = ldap_url_parse( in->bv_val, &ludp );
399 case LDAP_URL_SUCCESS:
400 /* FIXME: the check is pedantic, but I think it's necessary,
401 * because people tend to use things like ldaps:// which
402 * gives the idea SSL is being used. Maybe we could
403 * accept ldapi:// as well, but the point is that we use
404 * an URL as an easy means to define bits of a search with
407 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
411 rc = LDAP_INVALID_SYNTAX;
416 case LDAP_URL_ERR_BADSCHEME:
418 * last chance: assume it's a(n exact) DN ...
420 * NOTE: must pass DN normalization
422 ldap_free_urldesc( ludp );
423 bv.bv_val = in->bv_val;
424 scope = LDAP_X_SCOPE_EXACT;
428 rc = LDAP_INVALID_SYNTAX;
432 if ( ( ludp->lud_host && *ludp->lud_host )
433 || ludp->lud_attrs || ludp->lud_exts )
435 /* host part must be empty */
436 /* attrs and extensions parts must be empty */
437 rc = LDAP_INVALID_SYNTAX;
441 /* Grab the filter */
442 if ( ludp->lud_filter ) {
443 Filter *f = str2filter( ludp->lud_filter );
445 rc = LDAP_INVALID_SYNTAX;
451 /* Grab the searchbase */
452 assert( ludp->lud_dn != NULL );
453 ber_str2bv( ludp->lud_dn, 0, 0, &bv );
454 rc = dnValidate( NULL, &bv );
457 ldap_free_urldesc( ludp );
464 struct berval *normalized,
469 int rc = LDAP_INVALID_SYNTAX;
470 LDAPURLDesc *ludp = NULL;
477 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
478 * 3) dn.regex:<pattern>
479 * 4) u[.mech[/realm]]:<ID>
480 * 5) group[/<groupClass>[/<memberAttr>]]:<DN>
484 assert( val != NULL );
485 assert( !BER_BVISNULL( val ) );
488 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
489 * 3) dn.regex:<pattern>
491 * <DN> must pass DN normalization
493 if ( !strncasecmp( val->bv_val, "dn", STRLENOF( "dn" ) ) ) {
494 struct berval out = BER_BVNULL,
498 bv.bv_val = val->bv_val + STRLENOF( "dn" );
500 if ( bv.bv_val[ 0 ] == '.' ) {
503 if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
504 bv.bv_val += STRLENOF( "exact:" );
505 scope = LDAP_X_SCOPE_EXACT;
507 } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
508 bv.bv_val += STRLENOF( "regex:" );
509 scope = LDAP_X_SCOPE_REGEX;
511 } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
512 bv.bv_val += STRLENOF( "children:" );
513 scope = LDAP_X_SCOPE_CHILDREN;
515 } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
516 bv.bv_val += STRLENOF( "subtree:" );
517 scope = LDAP_X_SCOPE_SUBTREE;
519 } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
520 bv.bv_val += STRLENOF( "onelevel:" );
521 scope = LDAP_X_SCOPE_ONELEVEL;
524 return LDAP_INVALID_SYNTAX;
528 if ( bv.bv_val[ 0 ] != ':' ) {
529 return LDAP_INVALID_SYNTAX;
531 scope = LDAP_X_SCOPE_EXACT;
535 bv.bv_val += strspn( bv.bv_val, " " );
536 /* jump here in case no type specification was present
537 * and uri was not an URI... HEADS-UP: assuming EXACT */
538 is_dn: bv.bv_len = val->bv_len - ( bv.bv_val - val->bv_val );
540 /* a single '*' means any DN without using regexes */
541 if ( ber_bvccmp( &bv, '*' ) ) {
542 ber_str2bv_x( "dn:*", STRLENOF( "dn:*" ), 1, normalized, ctx );
547 case LDAP_X_SCOPE_EXACT:
548 case LDAP_X_SCOPE_CHILDREN:
549 case LDAP_X_SCOPE_SUBTREE:
550 case LDAP_X_SCOPE_ONELEVEL:
552 rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
554 rc = dnPretty( NULL, &bv, &out, ctx );
556 if( rc != LDAP_SUCCESS ) {
557 return LDAP_INVALID_SYNTAX;
561 case LDAP_X_SCOPE_REGEX:
562 normalized->bv_len = STRLENOF( "dn.regex:" ) + bv.bv_len;
563 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
564 ptr = lutil_strcopy( normalized->bv_val, "dn.regex:" );
565 ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
570 return LDAP_INVALID_SYNTAX;
575 case LDAP_X_SCOPE_EXACT:
576 BER_BVSTR( &prefix, "dn:" );
579 case LDAP_X_SCOPE_CHILDREN:
580 BER_BVSTR( &prefix, "dn.children:" );
583 case LDAP_X_SCOPE_SUBTREE:
584 BER_BVSTR( &prefix, "dn.subtree:" );
587 case LDAP_X_SCOPE_ONELEVEL:
588 BER_BVSTR( &prefix, "dn.onelevel:" );
596 normalized->bv_len = prefix.bv_len + out.bv_len;
597 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
599 ptr = lutil_strcopy( normalized->bv_val, prefix.bv_val );
600 ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
602 ber_memfree_x( out.bv_val, ctx );
607 * 4) u[.mech[/realm]]:<ID>
609 } else if ( ( val->bv_val[ 0 ] == 'u' || val->bv_val[ 0 ] == 'U' )
610 && ( val->bv_val[ 1 ] == ':'
611 || val->bv_val[ 1 ] == '/'
612 || val->bv_val[ 1 ] == '.' ) )
614 char buf[ SLAP_LDAPDN_MAXLEN ];
620 if ( sizeof( buf ) <= val->bv_len ) {
621 return LDAP_INVALID_SYNTAX;
624 id.bv_len = val->bv_len;
626 strncpy( buf, val->bv_val, sizeof( buf ) );
628 rc = slap_parse_user( &id, &user, &realm, &mech );
629 if ( rc != LDAP_SUCCESS ) {
630 return LDAP_INVALID_SYNTAX;
633 ber_dupbv_x( normalized, val, ctx );
638 * 5) group[/groupClass[/memberAttr]]:<DN>
640 * <groupClass> defaults to "groupOfNames"
641 * <memberAttr> defaults to "member"
643 * <DN> must pass DN normalization
645 } else if ( strncasecmp( val->bv_val, "group", STRLENOF( "group" ) ) == 0 )
647 struct berval group_dn = BER_BVNULL,
648 group_oc = BER_BVNULL,
649 member_at = BER_BVNULL,
653 bv.bv_val = val->bv_val + STRLENOF( "group" );
654 bv.bv_len = val->bv_len - STRLENOF( "group" );
655 group_dn.bv_val = ber_bvchr( &bv, ':' );
656 if ( group_dn.bv_val == NULL ) {
657 /* last chance: assume it's a(n exact) DN ... */
658 bv.bv_val = val->bv_val;
659 scope = LDAP_X_SCOPE_EXACT;
664 * FIXME: we assume that "member" and "groupOfNames"
665 * are present in schema...
667 if ( bv.bv_val[ 0 ] == '/' ) {
668 ObjectClass *oc = NULL;
670 group_oc.bv_val = &bv.bv_val[ 1 ];
671 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
673 member_at.bv_val = ber_bvchr( &group_oc, '/' );
674 if ( member_at.bv_val ) {
675 AttributeDescription *ad = NULL;
676 const char *text = NULL;
678 group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
680 member_at.bv_len = group_dn.bv_val - member_at.bv_val;
681 rc = slap_bv2ad( &member_at, &ad, &text );
682 if ( rc != LDAP_SUCCESS ) {
686 member_at = ad->ad_cname;
690 oc = oc_bvfind( &group_oc );
692 return LDAP_INVALID_SYNTAX;
695 group_oc = oc->soc_cname;
699 group_dn.bv_len = val->bv_len - ( group_dn.bv_val - val->bv_val );
702 rc = dnNormalize( 0, NULL, NULL, &group_dn, &out, ctx );
704 rc = dnPretty( NULL, &group_dn, &out, ctx );
706 if ( rc != LDAP_SUCCESS ) {
710 normalized->bv_len = STRLENOF( "group" ":" ) + out.bv_len;
711 if ( !BER_BVISNULL( &group_oc ) ) {
712 normalized->bv_len += STRLENOF( "/" ) + group_oc.bv_len;
713 if ( !BER_BVISNULL( &member_at ) ) {
714 normalized->bv_len += STRLENOF( "/" ) + member_at.bv_len;
718 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
719 ptr = lutil_strcopy( normalized->bv_val, "group" );
720 if ( !BER_BVISNULL( &group_oc ) ) {
723 ptr = lutil_strncopy( ptr, group_oc.bv_val, group_oc.bv_len );
724 if ( !BER_BVISNULL( &member_at ) ) {
727 ptr = lutil_strncopy( ptr, member_at.bv_val, member_at.bv_len );
732 ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
734 ber_memfree_x( out.bv_val, ctx );
740 * ldap:///<base>??<scope>?<filter>
741 * <scope> ::= {base|one|subtree}
743 * <scope> defaults to "base"
744 * <base> must pass DN normalization
745 * <filter> must pass str2filter()
747 rc = ldap_url_parse( val->bv_val, &ludp );
749 case LDAP_URL_SUCCESS:
750 /* FIXME: the check is pedantic, but I think it's necessary,
751 * because people tend to use things like ldaps:// which
752 * gives the idea SSL is being used. Maybe we could
753 * accept ldapi:// as well, but the point is that we use
754 * an URL as an easy means to define bits of a search with
757 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
761 rc = LDAP_INVALID_SYNTAX;
765 AC_MEMCPY( ludp->lud_scheme, "ldap", STRLENOF( "ldap" ) );
768 case LDAP_URL_ERR_BADSCHEME:
770 * last chance: assume it's a(n exact) DN ...
772 * NOTE: must pass DN normalization
774 ldap_free_urldesc( ludp );
775 bv.bv_val = val->bv_val;
776 scope = LDAP_X_SCOPE_EXACT;
780 rc = LDAP_INVALID_SYNTAX;
784 if ( ( ludp->lud_host && *ludp->lud_host )
785 || ludp->lud_attrs || ludp->lud_exts )
787 /* host part must be empty */
788 /* attrs and extensions parts must be empty */
789 rc = LDAP_INVALID_SYNTAX;
793 /* Grab the filter */
794 if ( ludp->lud_filter ) {
795 struct berval filterstr;
798 lud_filter = ludp->lud_filter;
800 f = str2filter( lud_filter );
802 rc = LDAP_INVALID_SYNTAX;
805 filter2bv( f, &filterstr );
807 if ( BER_BVISNULL( &filterstr ) ) {
808 rc = LDAP_INVALID_SYNTAX;
812 ludp->lud_filter = filterstr.bv_val;
815 /* Grab the searchbase */
816 assert( ludp->lud_dn != NULL );
817 if ( ludp->lud_dn ) {
818 struct berval out = BER_BVNULL;
820 lud_dn = ludp->lud_dn;
822 ber_str2bv( lud_dn, 0, 0, &bv );
824 rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
826 rc = dnPretty( NULL, &bv, &out, ctx );
829 if ( rc != LDAP_SUCCESS ) {
833 ludp->lud_dn = out.bv_val;
837 normalized->bv_val = ldap_url_desc2str( ludp );
838 if ( normalized->bv_val ) {
839 normalized->bv_len = strlen( normalized->bv_val );
842 rc = LDAP_INVALID_SYNTAX;
847 if ( ludp->lud_filter != lud_filter ) {
848 ber_memfree( ludp->lud_filter );
850 ludp->lud_filter = lud_filter;
854 if ( ludp->lud_dn != lud_dn ) {
855 ber_memfree( ludp->lud_dn );
857 ludp->lud_dn = lud_dn;
860 ldap_free_urldesc( ludp );
871 struct berval *normalized,
876 Debug( LDAP_DEBUG_TRACE, ">>> authzNormalize: <%s>\n",
879 rc = authzPrettyNormal( val, normalized, ctx, 1 );
881 Debug( LDAP_DEBUG_TRACE, "<<< authzNormalize: <%s> (%d)\n",
882 normalized->bv_val, rc, 0 );
896 Debug( LDAP_DEBUG_TRACE, ">>> authzPretty: <%s>\n",
899 rc = authzPrettyNormal( val, out, ctx, 0 );
901 Debug( LDAP_DEBUG_TRACE, "<<< authzPretty: <%s> (%d)\n",
902 out->bv_val, rc, 0 );
913 struct berval *nbase,
925 assert( uri != NULL && !BER_BVISNULL( uri ) );
932 Debug( LDAP_DEBUG_TRACE,
933 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
935 rc = LDAP_PROTOCOL_ERROR;
938 if ( idx.bv_val[ 0 ] == '{' ) {
941 ptr = ber_bvchr( &idx, '}' ) + 1;
943 assert( ptr != (void *)1 );
945 idx.bv_len -= ptr - idx.bv_val;
951 * dn[.<dnstyle>]:<dnpattern>
952 * <dnstyle> ::= {exact|regex|children|subtree|onelevel}
954 * <dnstyle> defaults to "exact"
955 * if <dnstyle> is not "regex", <dnpattern> must pass DN normalization
957 if ( !strncasecmp( uri->bv_val, "dn", STRLENOF( "dn" ) ) ) {
958 bv.bv_val = uri->bv_val + STRLENOF( "dn" );
960 if ( bv.bv_val[ 0 ] == '.' ) {
963 if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
964 bv.bv_val += STRLENOF( "exact:" );
965 *scope = LDAP_X_SCOPE_EXACT;
967 } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
968 bv.bv_val += STRLENOF( "regex:" );
969 *scope = LDAP_X_SCOPE_REGEX;
971 } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
972 bv.bv_val += STRLENOF( "children:" );
973 *scope = LDAP_X_SCOPE_CHILDREN;
975 } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
976 bv.bv_val += STRLENOF( "subtree:" );
977 *scope = LDAP_X_SCOPE_SUBTREE;
979 } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
980 bv.bv_val += STRLENOF( "onelevel:" );
981 *scope = LDAP_X_SCOPE_ONELEVEL;
984 return LDAP_PROTOCOL_ERROR;
988 if ( bv.bv_val[ 0 ] != ':' ) {
989 return LDAP_PROTOCOL_ERROR;
991 *scope = LDAP_X_SCOPE_EXACT;
995 bv.bv_val += strspn( bv.bv_val, " " );
996 /* jump here in case no type specification was present
997 * and uri was not an URI... HEADS-UP: assuming EXACT */
998 is_dn: bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
1000 /* a single '*' means any DN without using regexes */
1001 if ( ber_bvccmp( &bv, '*' ) ) {
1002 *scope = LDAP_X_SCOPE_USERS;
1006 case LDAP_X_SCOPE_EXACT:
1007 case LDAP_X_SCOPE_CHILDREN:
1008 case LDAP_X_SCOPE_SUBTREE:
1009 case LDAP_X_SCOPE_ONELEVEL:
1011 rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
1012 if( rc != LDAP_SUCCESS ) {
1016 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1021 case LDAP_X_SCOPE_REGEX:
1022 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1024 case LDAP_X_SCOPE_USERS:
1038 } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
1039 && ( uri->bv_val[ 1 ] == ':'
1040 || uri->bv_val[ 1 ] == '/'
1041 || uri->bv_val[ 1 ] == '.' ) )
1043 Connection c = *op->o_conn;
1044 char buf[ SLAP_LDAPDN_MAXLEN ];
1050 if ( sizeof( buf ) <= uri->bv_len ) {
1051 return LDAP_INVALID_SYNTAX;
1054 id.bv_len = uri->bv_len;
1056 strncpy( buf, uri->bv_val, sizeof( buf ) );
1058 rc = slap_parse_user( &id, &user, &realm, &mech );
1059 if ( rc != LDAP_SUCCESS ) {
1063 if ( !BER_BVISNULL( &mech ) ) {
1064 c.c_sasl_bind_mech = mech;
1066 BER_BVSTR( &c.c_sasl_bind_mech, "AUTHZ" );
1069 rc = slap_sasl_getdn( &c, op, &user,
1070 realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
1072 if ( rc == LDAP_SUCCESS ) {
1073 *scope = LDAP_X_SCOPE_EXACT;
1079 * group[/<groupoc>[/<groupat>]]:<groupdn>
1081 * groupoc defaults to "groupOfNames"
1082 * groupat defaults to "member"
1084 * <groupdn> must pass DN normalization
1086 } else if ( strncasecmp( uri->bv_val, "group", STRLENOF( "group" ) ) == 0 )
1088 struct berval group_dn = BER_BVNULL,
1089 group_oc = BER_BVNULL,
1090 member_at = BER_BVNULL;
1093 bv.bv_val = uri->bv_val + STRLENOF( "group" );
1094 bv.bv_len = uri->bv_len - STRLENOF( "group" );
1095 group_dn.bv_val = ber_bvchr( &bv, ':' );
1096 if ( group_dn.bv_val == NULL ) {
1097 /* last chance: assume it's a(n exact) DN ... */
1098 bv.bv_val = uri->bv_val;
1099 *scope = LDAP_X_SCOPE_EXACT;
1103 if ( bv.bv_val[ 0 ] == '/' ) {
1104 group_oc.bv_val = &bv.bv_val[ 1 ];
1105 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
1107 member_at.bv_val = ber_bvchr( &group_oc, '/' );
1108 if ( member_at.bv_val ) {
1109 group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
1111 member_at.bv_len = group_dn.bv_val - member_at.bv_val;
1114 BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
1118 BER_BVSTR( &group_oc, SLAPD_GROUP_CLASS );
1119 BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
1122 group_dn.bv_len = uri->bv_len - ( group_dn.bv_val - uri->bv_val );
1125 rc = dnNormalize( 0, NULL, NULL, &group_dn, nbase, op->o_tmpmemctx );
1126 if ( rc != LDAP_SUCCESS ) {
1131 ber_dupbv_x( nbase, &group_dn, op->o_tmpmemctx );
1134 *scope = LDAP_X_SCOPE_GROUP;
1136 /* FIXME: caller needs to add value of member attribute
1137 * and close brackets twice */
1138 fstr->bv_len = STRLENOF( "(&(objectClass=)(=" /* )) */ )
1139 + group_oc.bv_len + member_at.bv_len;
1140 fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
1142 tmp = lutil_strncopy( fstr->bv_val, "(&(objectClass=" /* )) */ ,
1143 STRLENOF( "(&(objectClass=" /* )) */ ) );
1144 tmp = lutil_strncopy( tmp, group_oc.bv_val, group_oc.bv_len );
1145 tmp = lutil_strncopy( tmp, /* ( */ ")(" /* ) */ ,
1146 STRLENOF( /* ( */ ")(" /* ) */ ) );
1147 tmp = lutil_strncopy( tmp, member_at.bv_val, member_at.bv_len );
1148 tmp = lutil_strncopy( tmp, "=", STRLENOF( "=" ) );
1154 * ldap:///<base>??<scope>?<filter>
1155 * <scope> ::= {base|one|subtree}
1157 * <scope> defaults to "base"
1158 * <base> must pass DN normalization
1159 * <filter> must pass str2filter()
1161 rc = ldap_url_parse( uri->bv_val, &ludp );
1163 case LDAP_URL_SUCCESS:
1164 /* FIXME: the check is pedantic, but I think it's necessary,
1165 * because people tend to use things like ldaps:// which
1166 * gives the idea SSL is being used. Maybe we could
1167 * accept ldapi:// as well, but the point is that we use
1168 * an URL as an easy means to define bits of a search with
1171 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
1175 rc = LDAP_PROTOCOL_ERROR;
1180 case LDAP_URL_ERR_BADSCHEME:
1182 * last chance: assume it's a(n exact) DN ...
1184 * NOTE: must pass DN normalization
1186 ldap_free_urldesc( ludp );
1187 bv.bv_val = uri->bv_val;
1188 *scope = LDAP_X_SCOPE_EXACT;
1192 rc = LDAP_PROTOCOL_ERROR;
1196 if ( ( ludp->lud_host && *ludp->lud_host )
1197 || ludp->lud_attrs || ludp->lud_exts )
1199 /* host part must be empty */
1200 /* attrs and extensions parts must be empty */
1201 rc = LDAP_PROTOCOL_ERROR;
1205 /* Grab the scope */
1206 *scope = ludp->lud_scope;
1208 /* Grab the filter */
1209 if ( ludp->lud_filter ) {
1210 *filter = str2filter_x( op, ludp->lud_filter );
1211 if ( *filter == NULL ) {
1212 rc = LDAP_PROTOCOL_ERROR;
1215 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
1218 /* Grab the searchbase */
1219 ber_str2bv( ludp->lud_dn, 0, 0, base );
1221 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
1223 ber_dupbv_x( nbase, base, op->o_tmpmemctx );
1228 if( rc != LDAP_SUCCESS ) {
1229 if( *filter ) filter_free_x( op, *filter, 1 );
1233 /* Don't free these, return them to caller */
1234 ludp->lud_filter = NULL;
1235 ludp->lud_dn = NULL;
1238 ldap_free_urldesc( ludp );
1242 #ifndef SLAP_AUTH_REWRITE
1243 static int slap_sasl_rx_off(char *rep, int *off)
1248 /* Precompile replace pattern. Find the $<n> placeholders */
1251 for ( c = rep; *c; c++ ) {
1252 if ( *c == '\\' && c[1] ) {
1257 if ( n == SASLREGEX_REPLACE ) {
1258 Debug( LDAP_DEBUG_ANY,
1259 "SASL replace pattern %s has too many $n "
1260 "placeholders (max %d)\n",
1261 rep, SASLREGEX_REPLACE, 0 );
1263 return( LDAP_OTHER );
1270 /* Final placeholder, after the last $n */
1274 return( LDAP_SUCCESS );
1276 #endif /* ! SLAP_AUTH_REWRITE */
1278 #ifdef SLAP_AUTH_REWRITE
1279 int slap_sasl_rewrite_config(
1289 /* init at first call */
1290 if ( sasl_rwinfo == NULL ) {
1291 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1294 /* strip "authid-" prefix for parsing */
1296 argv[0] += STRLENOF( "authid-" );
1297 rc = rewrite_parse( sasl_rwinfo, fname, lineno, argc, argv );
1304 slap_sasl_rewrite_destroy( void )
1306 if ( sasl_rwinfo ) {
1307 rewrite_info_delete( &sasl_rwinfo );
1314 int slap_sasl_regexp_rewrite_config(
1318 const char *replace,
1319 const char *context )
1322 char *argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL };
1324 /* init at first call */
1325 if ( sasl_rwinfo == NULL ) {
1326 char *argvEngine[] = { "rewriteEngine", "on", NULL };
1327 char *argvContext[] = { "rewriteContext", NULL, NULL };
1329 /* initialize rewrite engine */
1330 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1332 /* switch on rewrite engine */
1333 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine );
1334 if (rc != LDAP_SUCCESS) {
1338 /* create generic authid context */
1339 argvContext[1] = AUTHID_CONTEXT;
1340 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvContext );
1341 if (rc != LDAP_SUCCESS) {
1346 argvRule[1] = (char *)match;
1347 argvRule[2] = (char *)replace;
1348 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule );
1352 #endif /* SLAP_AUTH_REWRITE */
1354 int slap_sasl_regexp_config( const char *match, const char *replace )
1359 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
1360 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
1362 reg = &SaslRegexp[nSaslRegexp];
1364 #ifdef SLAP_AUTH_REWRITE
1365 rc = slap_sasl_regexp_rewrite_config( "sasl-regexp", 0,
1366 match, replace, AUTHID_CONTEXT );
1367 #else /* ! SLAP_AUTH_REWRITE */
1369 /* Precompile matching pattern */
1370 rc = regcomp( ®->sr_workspace, match, REG_EXTENDED|REG_ICASE );
1372 Debug( LDAP_DEBUG_ANY,
1373 "SASL match pattern %s could not be compiled by regexp engine\n",
1376 #ifdef ENABLE_REWRITE
1377 /* Dummy block to force symbol references in librewrite */
1378 if ( slapMode == ( SLAP_SERVER_MODE|SLAP_TOOL_MODE )) {
1379 rewrite_info_init( 0 );
1382 return( LDAP_OTHER );
1385 rc = slap_sasl_rx_off( replace, reg->sr_offset );
1386 #endif /* ! SLAP_AUTH_REWRITE */
1387 if ( rc == LDAP_SUCCESS ) {
1388 reg->sr_match = ch_strdup( match );
1389 reg->sr_replace = ch_strdup( replace );
1398 slap_sasl_regexp_destroy( void )
1403 for ( n = 0; n < nSaslRegexp; n++ ) {
1404 ch_free( SaslRegexp[ n ].sr_match );
1405 ch_free( SaslRegexp[ n ].sr_replace );
1406 #ifndef SLAP_AUTH_REWRITE
1407 regfree( &SaslRegexp[ n ].sr_workspace );
1408 #endif /* SLAP_AUTH_REWRITE */
1411 ch_free( SaslRegexp );
1414 #ifdef SLAP_AUTH_REWRITE
1415 slap_sasl_rewrite_destroy();
1416 #endif /* SLAP_AUTH_REWRITE */
1419 void slap_sasl_regexp_unparse( BerVarray *out )
1422 BerVarray bva = NULL;
1423 char ibuf[32], *ptr;
1426 if ( !nSaslRegexp ) return;
1429 bva = ch_malloc( (nSaslRegexp+1) * sizeof(struct berval) );
1430 BER_BVZERO(bva+nSaslRegexp);
1431 for ( i=0; i<nSaslRegexp; i++ ) {
1432 idx.bv_len = sprintf( idx.bv_val, "{%d}", i);
1433 bva[i].bv_len = idx.bv_len + strlen( SaslRegexp[i].sr_match ) +
1434 strlen( SaslRegexp[i].sr_replace ) + 5;
1435 bva[i].bv_val = ch_malloc( bva[i].bv_len+1 );
1436 ptr = lutil_strcopy( bva[i].bv_val, ibuf );
1438 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match );
1439 ptr = lutil_strcopy( ptr, "\" \"" );
1440 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace );
1447 #ifndef SLAP_AUTH_REWRITE
1448 /* Perform replacement on regexp matches */
1449 static void slap_sasl_rx_exp(
1453 const char *saslname,
1457 int i, n, len, insert;
1459 /* Get the total length of the final URI */
1463 while( off[n] >= 0 ) {
1464 /* Len of next section from replacement string (x,y,z above) */
1465 len += off[n] - off[n-1] - 2;
1469 /* Len of string from saslname that matched next $i (b,d above) */
1470 i = rep[ off[n] + 1 ] - '0';
1471 len += str[i].rm_eo - str[i].rm_so;
1474 out->bv_val = slap_sl_malloc( len + 1, ctx );
1477 /* Fill in URI with replace string, replacing $i as we go */
1480 while( off[n] >= 0) {
1481 /* Paste in next section from replacement string (x,y,z above) */
1482 len = off[n] - off[n-1] - 2;
1483 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
1488 /* Paste in string from saslname that matched next $i (b,d above) */
1489 i = rep[ off[n] + 1 ] - '0';
1490 len = str[i].rm_eo - str[i].rm_so;
1491 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
1497 out->bv_val[insert] = '\0';
1499 #endif /* ! SLAP_AUTH_REWRITE */
1501 /* Take the passed in SASL name and attempt to convert it into an
1502 LDAP URI to find the matching LDAP entry, using the pattern matching
1503 strings given in the saslregexp config file directive(s) */
1505 static int slap_authz_regexp( struct berval *in, struct berval *out,
1506 int flags, void *ctx )
1508 #ifdef SLAP_AUTH_REWRITE
1509 const char *context = AUTHID_CONTEXT;
1511 if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
1515 /* FIXME: if aware of authc/authz mapping,
1516 * we could use different contexts ... */
1517 switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL,
1520 case REWRITE_REGEXEC_OK:
1521 if ( !BER_BVISNULL( out ) ) {
1522 char *val = out->bv_val;
1523 ber_str2bv_x( val, 0, 1, out, ctx );
1524 if ( val != in->bv_val ) {
1528 ber_dupbv_x( out, in, ctx );
1530 Debug( LDAP_DEBUG_ARGS,
1531 "[rw] %s: \"%s\" -> \"%s\"\n",
1532 context, in->bv_val, out->bv_val );
1535 case REWRITE_REGEXEC_UNWILLING:
1536 case REWRITE_REGEXEC_ERR:
1541 #else /* ! SLAP_AUTH_REWRITE */
1542 char *saslname = in->bv_val;
1544 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
1547 memset( out, 0, sizeof( *out ) );
1549 Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
1552 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
1556 /* Match the normalized SASL name to the saslregexp patterns */
1557 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
1558 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE,
1559 sr_strings, 0) == 0 )
1563 if( i >= nSaslRegexp ) return( 0 );
1566 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
1567 * replace pattern of the form "x$1y$2z". The returned string needs
1568 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
1570 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
1571 sr_strings, saslname, out, ctx );
1573 Debug( LDAP_DEBUG_TRACE,
1574 "slap_authz_regexp: converted SASL name to %s\n",
1575 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
1578 #endif /* ! SLAP_AUTH_REWRITE */
1581 /* This callback actually does some work...*/
1582 static int sasl_sc_sasl2dn( Operation *op, SlapReply *rs )
1584 struct berval *ndn = op->o_callback->sc_private;
1586 if ( rs->sr_type != REP_SEARCH ) return LDAP_SUCCESS;
1588 /* We only want to be called once */
1589 if ( !BER_BVISNULL( ndn ) ) {
1590 op->o_tmpfree( ndn->bv_val, op->o_tmpmemctx );
1593 Debug( LDAP_DEBUG_TRACE,
1594 "%s: slap_sc_sasl2dn: search DN returned more than 1 entry\n",
1595 op->o_log_prefix, 0, 0 );
1596 return LDAP_UNAVAILABLE; /* short-circuit the search */
1599 ber_dupbv_x( ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
1600 return LDAP_SUCCESS;
1604 typedef struct smatch_info {
1609 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
1611 smatch_info *sm = o->o_callback->sc_private;
1613 if (rs->sr_type != REP_SEARCH) return 0;
1615 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
1617 return LDAP_UNAVAILABLE; /* short-circuit the search */
1624 slap_sasl_matches( Operation *op, BerVarray rules,
1625 struct berval *assertDN, struct berval *authc )
1627 int rc = LDAP_INAPPROPRIATE_AUTH;
1629 if ( rules != NULL ) {
1632 for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
1633 rc = slap_sasl_match( op, &rules[i], assertDN, authc );
1634 if ( rc == LDAP_SUCCESS ) break;
1642 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
1643 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
1644 * the rule must be used as an internal search for entries. If that search
1645 * returns the *assertDN entry, the match is successful.
1647 * The assertDN should not have the dn: prefix
1651 slap_sasl_match( Operation *opx, struct berval *rule,
1652 struct berval *assertDN, struct berval *authc )
1657 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
1659 SlapReply rs = {REP_RESULT};
1660 struct berval base = BER_BVNULL;
1664 cb.sc_private = &sm;
1666 Debug( LDAP_DEBUG_TRACE,
1667 "===>slap_sasl_match: comparing DN %s to rule %s\n",
1668 assertDN->bv_len ? assertDN->bv_val : "(null)", rule->bv_val, 0 );
1670 /* NOTE: don't normalize rule if authz syntax is enabled */
1671 rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn,
1672 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 0 );
1674 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
1676 switch ( op.ors_scope ) {
1677 case LDAP_X_SCOPE_EXACT:
1679 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
1682 rc = LDAP_INAPPROPRIATE_AUTH;
1686 case LDAP_X_SCOPE_CHILDREN:
1687 case LDAP_X_SCOPE_SUBTREE:
1688 case LDAP_X_SCOPE_ONELEVEL:
1690 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
1692 rc = LDAP_INAPPROPRIATE_AUTH;
1694 if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
1697 } else if ( d > 0 ) {
1700 /* leave room for at least one char of attributeType,
1701 * one for '=' and one for ',' */
1702 if ( d < (int) STRLENOF( "x=,") ) {
1706 bv.bv_len = op.o_req_ndn.bv_len;
1707 bv.bv_val = assertDN->bv_val + d;
1709 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
1710 switch ( op.ors_scope ) {
1711 case LDAP_X_SCOPE_SUBTREE:
1712 case LDAP_X_SCOPE_CHILDREN:
1716 case LDAP_X_SCOPE_ONELEVEL:
1720 dnParent( assertDN, &pdn );
1721 /* the common portion of the DN
1722 * already matches, so only check
1723 * if parent DN of assertedDN
1724 * is all the pattern */
1725 if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
1731 /* at present, impossible */
1739 case LDAP_X_SCOPE_REGEX:
1740 rc = regcomp(®, op.o_req_ndn.bv_val,
1741 REG_EXTENDED|REG_ICASE|REG_NOSUB);
1743 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
1749 rc = LDAP_INAPPROPRIATE_AUTH;
1753 case LDAP_X_SCOPE_GROUP: {
1756 /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
1757 * we need to append the <assertDN> so that the <group_dn> is searched
1758 * with scope "base", and the filter ensures that <assertDN> is
1759 * member of the group */
1760 tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len +
1761 assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 );
1762 if ( tmp == NULL ) {
1763 rc = LDAP_NO_MEMORY;
1766 op.ors_filterstr.bv_val = tmp;
1768 tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
1769 tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
1771 /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
1772 op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
1773 if ( op.ors_filter == NULL ) {
1774 rc = LDAP_PROTOCOL_ERROR;
1777 op.ors_scope = LDAP_SCOPE_BASE;
1779 /* hijack match DN: use that of the group instead of the assertDN;
1780 * assertDN is now in the filter */
1781 sm.dn = &op.o_req_ndn;
1787 case LDAP_X_SCOPE_USERS:
1788 if ( !BER_BVISEMPTY( assertDN ) ) {
1791 rc = LDAP_INAPPROPRIATE_AUTH;
1799 /* Must run an internal search. */
1800 if ( op.ors_filter == NULL ) {
1801 rc = LDAP_FILTER_ERROR;
1805 Debug( LDAP_DEBUG_TRACE,
1806 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
1807 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1809 op.o_bd = select_backend( &op.o_req_ndn, 1 );
1810 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1811 rc = LDAP_INAPPROPRIATE_AUTH;
1815 op.o_hdr = opx->o_hdr;
1816 op.o_tag = LDAP_REQ_SEARCH;
1818 op.o_callback = &cb;
1819 slap_op_time( &op.o_time, &op.o_tincr );
1820 op.o_do_not_cache = 1;
1821 op.o_is_auth_check = 1;
1822 /* use req_ndn as req_dn instead of non-pretty base of uri */
1823 if( !BER_BVISNULL( &base ) ) {
1824 ch_free( base.bv_val );
1825 /* just in case... */
1826 BER_BVZERO( &base );
1828 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1829 op.ors_deref = LDAP_DEREF_NEVER;
1831 op.ors_tlimit = SLAP_NO_LIMIT;
1832 op.ors_attrs = slap_anlist_no_attrs;
1833 op.ors_attrsonly = 1;
1835 op.o_bd->be_search( &op, &rs );
1840 rc = LDAP_INAPPROPRIATE_AUTH;
1844 if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1845 if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1846 if( op.ors_filter ) filter_free_x( opx, op.ors_filter, 1 );
1847 if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1849 Debug( LDAP_DEBUG_TRACE,
1850 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
1857 * This function answers the question, "Can this ID authorize to that ID?",
1858 * based on authorization rules. The rules are stored in the *searchDN, in the
1859 * attribute named by *attr. If any of those rules map to the *assertDN, the
1860 * authorization is approved.
1862 * The DNs should not have the dn: prefix
1865 slap_sasl_check_authz( Operation *op,
1866 struct berval *searchDN,
1867 struct berval *assertDN,
1868 AttributeDescription *ad,
1869 struct berval *authc )
1872 do_not_cache = op->o_do_not_cache;
1873 BerVarray vals = NULL;
1875 Debug( LDAP_DEBUG_TRACE,
1876 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
1877 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1879 /* ITS#4760: don't cache group access */
1880 op->o_do_not_cache = 1;
1881 rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
1882 op->o_do_not_cache = do_not_cache;
1883 if( rc != LDAP_SUCCESS ) goto COMPLETE;
1885 /* Check if the *assertDN matches any *vals */
1886 rc = slap_sasl_matches( op, vals, assertDN, authc );
1889 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1891 Debug( LDAP_DEBUG_TRACE,
1892 "<==slap_sasl_check_authz: %s check returning %d\n",
1893 ad->ad_cname.bv_val, rc, 0);
1899 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
1900 * return the LDAP DN to which it matches. The SASL regexp rules in the config
1901 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
1902 * search with scope=base), just return the URI (or its searchbase). Otherwise
1903 * an internal search must be done, and if that search returns exactly one
1904 * entry, return the DN of that one entry.
1909 struct berval *saslname,
1910 struct berval *sasldn,
1914 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
1916 SlapReply rs = {REP_RESULT};
1917 struct berval regout = BER_BVNULL;
1918 struct berval base = BER_BVNULL;
1920 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1921 "converting SASL name %s to a DN\n",
1922 saslname->bv_val, 0,0 );
1924 BER_BVZERO( sasldn );
1925 cb.sc_private = sasldn;
1927 /* Convert the SASL name into a minimal URI */
1928 if( !slap_authz_regexp( saslname, ®out, flags, opx->o_tmpmemctx ) ) {
1932 /* NOTE: always normalize regout because it results
1933 * from string submatch expansion */
1934 rc = slap_parseURI( opx, ®out, &base, &op.o_req_ndn,
1935 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 );
1936 if ( !BER_BVISNULL( ®out ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
1937 if ( rc != LDAP_SUCCESS ) {
1941 /* Must do an internal search */
1942 op.o_bd = select_backend( &op.o_req_ndn, 1 );
1944 switch ( op.ors_scope ) {
1945 case LDAP_X_SCOPE_EXACT:
1946 *sasldn = op.o_req_ndn;
1947 BER_BVZERO( &op.o_req_ndn );
1948 /* intentionally continue to next case */
1950 case LDAP_X_SCOPE_REGEX:
1951 case LDAP_X_SCOPE_SUBTREE:
1952 case LDAP_X_SCOPE_CHILDREN:
1953 case LDAP_X_SCOPE_ONELEVEL:
1954 case LDAP_X_SCOPE_GROUP:
1955 case LDAP_X_SCOPE_USERS:
1956 /* correctly parsed, but illegal */
1959 case LDAP_SCOPE_BASE:
1960 case LDAP_SCOPE_ONELEVEL:
1961 case LDAP_SCOPE_SUBTREE:
1962 case LDAP_SCOPE_SUBORDINATE:
1967 /* catch unhandled cases (there shouldn't be) */
1971 Debug( LDAP_DEBUG_TRACE,
1972 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1973 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1975 if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
1979 /* Must run an internal search. */
1980 if ( op.ors_filter == NULL ) {
1981 rc = LDAP_FILTER_ERROR;
1985 op.o_hdr = opx->o_hdr;
1986 op.o_tag = LDAP_REQ_SEARCH;
1987 op.o_ndn = opx->o_conn->c_ndn;
1988 op.o_callback = &cb;
1989 slap_op_time( &op.o_time, &op.o_tincr );
1990 op.o_do_not_cache = 1;
1991 op.o_is_auth_check = 1;
1992 op.ors_deref = LDAP_DEREF_NEVER;
1994 op.ors_tlimit = SLAP_NO_LIMIT;
1995 op.ors_attrs = slap_anlist_no_attrs;
1996 op.ors_attrsonly = 1;
1997 /* use req_ndn as req_dn instead of non-pretty base of uri */
1998 if( !BER_BVISNULL( &base ) ) {
1999 ch_free( base.bv_val );
2000 /* just in case... */
2001 BER_BVZERO( &base );
2003 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
2005 op.o_bd->be_search( &op, &rs );
2008 if( !BER_BVISEMPTY( sasldn ) ) {
2009 opx->o_conn->c_authz_backend = op.o_bd;
2011 if( !BER_BVISNULL( &op.o_req_dn ) ) {
2012 slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
2014 if( !BER_BVISNULL( &op.o_req_ndn ) ) {
2015 slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
2017 if( op.ors_filter ) {
2018 filter_free_x( opx, op.ors_filter, 1 );
2020 if( !BER_BVISNULL( &op.ors_filterstr ) ) {
2021 ch_free( op.ors_filterstr.bv_val );
2024 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
2025 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
2031 /* Check if a bind can SASL authorize to another identity.
2032 * The DNs should not have the dn: prefix
2035 int slap_sasl_authorized( Operation *op,
2036 struct berval *authcDN, struct berval *authzDN )
2038 int rc = LDAP_INAPPROPRIATE_AUTH;
2040 /* User binding as anonymous */
2041 if ( !authzDN || !authzDN->bv_len || !authzDN->bv_val ) {
2046 /* User is anonymous */
2047 if ( !authcDN || !authcDN->bv_len || !authcDN->bv_val ) {
2051 Debug( LDAP_DEBUG_TRACE,
2052 "==>slap_sasl_authorized: can %s become %s?\n",
2053 authcDN->bv_len ? authcDN->bv_val : "(null)",
2054 authzDN->bv_len ? authzDN->bv_val : "(null)", 0 );
2056 /* If person is authorizing to self, succeed */
2057 if ( dn_match( authcDN, authzDN ) ) {
2062 /* Allow the manager to authorize as any DN. */
2063 if( op->o_conn->c_authz_backend &&
2064 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
2070 /* Check source rules */
2071 if( authz_policy & SASL_AUTHZ_TO ) {
2072 rc = slap_sasl_check_authz( op, authcDN, authzDN,
2073 slap_schema.si_ad_saslAuthzTo, authcDN );
2074 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
2079 /* Check destination rules */
2080 if( authz_policy & SASL_AUTHZ_FROM ) {
2081 rc = slap_sasl_check_authz( op, authzDN, authcDN,
2082 slap_schema.si_ad_saslAuthzFrom, authcDN );
2083 if( rc == LDAP_SUCCESS ) {
2088 rc = LDAP_INAPPROPRIATE_AUTH;
2092 Debug( LDAP_DEBUG_TRACE,
2093 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );