2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1998-2015 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 ) {
1230 filter_free_x( op, *filter, 1 );
1236 /* Don't free these, return them to caller */
1237 ludp->lud_filter = NULL;
1238 ludp->lud_dn = NULL;
1241 ldap_free_urldesc( ludp );
1245 #ifndef SLAP_AUTH_REWRITE
1246 static int slap_sasl_rx_off(char *rep, int *off)
1251 /* Precompile replace pattern. Find the $<n> placeholders */
1254 for ( c = rep; *c; c++ ) {
1255 if ( *c == '\\' && c[1] ) {
1260 if ( n == SASLREGEX_REPLACE ) {
1261 Debug( LDAP_DEBUG_ANY,
1262 "SASL replace pattern %s has too many $n "
1263 "placeholders (max %d)\n",
1264 rep, SASLREGEX_REPLACE, 0 );
1266 return( LDAP_OTHER );
1273 /* Final placeholder, after the last $n */
1277 return( LDAP_SUCCESS );
1279 #endif /* ! SLAP_AUTH_REWRITE */
1281 #ifdef SLAP_AUTH_REWRITE
1282 int slap_sasl_rewrite_config(
1292 /* init at first call */
1293 if ( sasl_rwinfo == NULL ) {
1294 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1297 /* strip "authid-" prefix for parsing */
1299 argv[0] += STRLENOF( "authid-" );
1300 rc = rewrite_parse( sasl_rwinfo, fname, lineno, argc, argv );
1307 slap_sasl_rewrite_destroy( void )
1309 if ( sasl_rwinfo ) {
1310 rewrite_info_delete( &sasl_rwinfo );
1317 int slap_sasl_regexp_rewrite_config(
1321 const char *replace,
1322 const char *context )
1325 char *argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL };
1327 /* init at first call */
1328 if ( sasl_rwinfo == NULL ) {
1329 char *argvEngine[] = { "rewriteEngine", "on", NULL };
1330 char *argvContext[] = { "rewriteContext", NULL, NULL };
1332 /* initialize rewrite engine */
1333 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1335 /* switch on rewrite engine */
1336 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine );
1337 if (rc != LDAP_SUCCESS) {
1341 /* create generic authid context */
1342 argvContext[1] = AUTHID_CONTEXT;
1343 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvContext );
1344 if (rc != LDAP_SUCCESS) {
1349 argvRule[1] = (char *)match;
1350 argvRule[2] = (char *)replace;
1351 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule );
1355 #endif /* SLAP_AUTH_REWRITE */
1357 int slap_sasl_regexp_config( const char *match, const char *replace )
1362 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
1363 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
1365 reg = &SaslRegexp[nSaslRegexp];
1367 #ifdef SLAP_AUTH_REWRITE
1368 rc = slap_sasl_regexp_rewrite_config( "sasl-regexp", 0,
1369 match, replace, AUTHID_CONTEXT );
1370 #else /* ! SLAP_AUTH_REWRITE */
1372 /* Precompile matching pattern */
1373 rc = regcomp( ®->sr_workspace, match, REG_EXTENDED|REG_ICASE );
1375 Debug( LDAP_DEBUG_ANY,
1376 "SASL match pattern %s could not be compiled by regexp engine\n",
1379 #ifdef ENABLE_REWRITE
1380 /* Dummy block to force symbol references in librewrite */
1381 if ( slapMode == ( SLAP_SERVER_MODE|SLAP_TOOL_MODE )) {
1382 rewrite_info_init( 0 );
1385 return( LDAP_OTHER );
1388 rc = slap_sasl_rx_off( replace, reg->sr_offset );
1389 #endif /* ! SLAP_AUTH_REWRITE */
1390 if ( rc == LDAP_SUCCESS ) {
1391 reg->sr_match = ch_strdup( match );
1392 reg->sr_replace = ch_strdup( replace );
1401 slap_sasl_regexp_destroy( void )
1406 for ( n = 0; n < nSaslRegexp; n++ ) {
1407 ch_free( SaslRegexp[ n ].sr_match );
1408 ch_free( SaslRegexp[ n ].sr_replace );
1409 #ifndef SLAP_AUTH_REWRITE
1410 regfree( &SaslRegexp[ n ].sr_workspace );
1411 #endif /* SLAP_AUTH_REWRITE */
1414 ch_free( SaslRegexp );
1417 #ifdef SLAP_AUTH_REWRITE
1418 slap_sasl_rewrite_destroy();
1419 #endif /* SLAP_AUTH_REWRITE */
1422 void slap_sasl_regexp_unparse( BerVarray *out )
1425 BerVarray bva = NULL;
1426 char ibuf[32], *ptr;
1429 if ( !nSaslRegexp ) return;
1432 bva = ch_malloc( (nSaslRegexp+1) * sizeof(struct berval) );
1433 BER_BVZERO(bva+nSaslRegexp);
1434 for ( i=0; i<nSaslRegexp; i++ ) {
1435 idx.bv_len = sprintf( idx.bv_val, "{%d}", i);
1436 bva[i].bv_len = idx.bv_len + strlen( SaslRegexp[i].sr_match ) +
1437 strlen( SaslRegexp[i].sr_replace ) + 5;
1438 bva[i].bv_val = ch_malloc( bva[i].bv_len+1 );
1439 ptr = lutil_strcopy( bva[i].bv_val, ibuf );
1441 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match );
1442 ptr = lutil_strcopy( ptr, "\" \"" );
1443 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace );
1450 #ifndef SLAP_AUTH_REWRITE
1451 /* Perform replacement on regexp matches */
1452 static void slap_sasl_rx_exp(
1456 const char *saslname,
1460 int i, n, len, insert;
1462 /* Get the total length of the final URI */
1466 while( off[n] >= 0 ) {
1467 /* Len of next section from replacement string (x,y,z above) */
1468 len += off[n] - off[n-1] - 2;
1472 /* Len of string from saslname that matched next $i (b,d above) */
1473 i = rep[ off[n] + 1 ] - '0';
1474 len += str[i].rm_eo - str[i].rm_so;
1477 out->bv_val = slap_sl_malloc( len + 1, ctx );
1480 /* Fill in URI with replace string, replacing $i as we go */
1483 while( off[n] >= 0) {
1484 /* Paste in next section from replacement string (x,y,z above) */
1485 len = off[n] - off[n-1] - 2;
1486 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
1491 /* Paste in string from saslname that matched next $i (b,d above) */
1492 i = rep[ off[n] + 1 ] - '0';
1493 len = str[i].rm_eo - str[i].rm_so;
1494 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
1500 out->bv_val[insert] = '\0';
1502 #endif /* ! SLAP_AUTH_REWRITE */
1504 /* Take the passed in SASL name and attempt to convert it into an
1505 LDAP URI to find the matching LDAP entry, using the pattern matching
1506 strings given in the saslregexp config file directive(s) */
1508 static int slap_authz_regexp( struct berval *in, struct berval *out,
1509 int flags, void *ctx )
1511 #ifdef SLAP_AUTH_REWRITE
1512 const char *context = AUTHID_CONTEXT;
1514 if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
1518 /* FIXME: if aware of authc/authz mapping,
1519 * we could use different contexts ... */
1520 switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL,
1523 case REWRITE_REGEXEC_OK:
1524 if ( !BER_BVISNULL( out ) ) {
1525 char *val = out->bv_val;
1526 ber_str2bv_x( val, 0, 1, out, ctx );
1527 if ( val != in->bv_val ) {
1531 ber_dupbv_x( out, in, ctx );
1533 Debug( LDAP_DEBUG_ARGS,
1534 "[rw] %s: \"%s\" -> \"%s\"\n",
1535 context, in->bv_val, out->bv_val );
1538 case REWRITE_REGEXEC_UNWILLING:
1539 case REWRITE_REGEXEC_ERR:
1544 #else /* ! SLAP_AUTH_REWRITE */
1545 char *saslname = in->bv_val;
1547 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
1550 memset( out, 0, sizeof( *out ) );
1552 Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
1555 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
1559 /* Match the normalized SASL name to the saslregexp patterns */
1560 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
1561 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE,
1562 sr_strings, 0) == 0 )
1566 if( i >= nSaslRegexp ) return( 0 );
1569 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
1570 * replace pattern of the form "x$1y$2z". The returned string needs
1571 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
1573 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
1574 sr_strings, saslname, out, ctx );
1576 Debug( LDAP_DEBUG_TRACE,
1577 "slap_authz_regexp: converted SASL name to %s\n",
1578 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
1581 #endif /* ! SLAP_AUTH_REWRITE */
1584 /* This callback actually does some work...*/
1585 static int sasl_sc_sasl2dn( Operation *op, SlapReply *rs )
1587 struct berval *ndn = op->o_callback->sc_private;
1589 if ( rs->sr_type != REP_SEARCH ) return LDAP_SUCCESS;
1591 /* We only want to be called once */
1592 if ( !BER_BVISNULL( ndn ) ) {
1593 op->o_tmpfree( ndn->bv_val, op->o_tmpmemctx );
1596 Debug( LDAP_DEBUG_TRACE,
1597 "%s: slap_sc_sasl2dn: search DN returned more than 1 entry\n",
1598 op->o_log_prefix, 0, 0 );
1599 return LDAP_UNAVAILABLE; /* short-circuit the search */
1602 ber_dupbv_x( ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
1603 return LDAP_SUCCESS;
1607 typedef struct smatch_info {
1612 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
1614 smatch_info *sm = o->o_callback->sc_private;
1616 if (rs->sr_type != REP_SEARCH) return 0;
1618 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
1620 return LDAP_UNAVAILABLE; /* short-circuit the search */
1627 slap_sasl_matches( Operation *op, BerVarray rules,
1628 struct berval *assertDN, struct berval *authc )
1630 int rc = LDAP_INAPPROPRIATE_AUTH;
1632 if ( rules != NULL ) {
1635 for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
1636 rc = slap_sasl_match( op, &rules[i], assertDN, authc );
1637 if ( rc == LDAP_SUCCESS ) break;
1645 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
1646 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
1647 * the rule must be used as an internal search for entries. If that search
1648 * returns the *assertDN entry, the match is successful.
1650 * The assertDN should not have the dn: prefix
1654 slap_sasl_match( Operation *opx, struct berval *rule,
1655 struct berval *assertDN, struct berval *authc )
1660 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
1662 SlapReply rs = {REP_RESULT};
1663 struct berval base = BER_BVNULL;
1667 cb.sc_private = &sm;
1669 Debug( LDAP_DEBUG_TRACE,
1670 "===>slap_sasl_match: comparing DN %s to rule %s\n",
1671 assertDN->bv_len ? assertDN->bv_val : "(null)", rule->bv_val, 0 );
1673 /* NOTE: don't normalize rule if authz syntax is enabled */
1674 rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn,
1675 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 0 );
1677 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
1679 switch ( op.ors_scope ) {
1680 case LDAP_X_SCOPE_EXACT:
1682 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
1685 rc = LDAP_INAPPROPRIATE_AUTH;
1689 case LDAP_X_SCOPE_CHILDREN:
1690 case LDAP_X_SCOPE_SUBTREE:
1691 case LDAP_X_SCOPE_ONELEVEL:
1693 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
1695 rc = LDAP_INAPPROPRIATE_AUTH;
1697 if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
1700 } else if ( d > 0 ) {
1703 /* leave room for at least one char of attributeType,
1704 * one for '=' and one for ',' */
1705 if ( d < (int) STRLENOF( "x=,") ) {
1709 bv.bv_len = op.o_req_ndn.bv_len;
1710 bv.bv_val = assertDN->bv_val + d;
1712 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
1713 switch ( op.ors_scope ) {
1714 case LDAP_X_SCOPE_SUBTREE:
1715 case LDAP_X_SCOPE_CHILDREN:
1719 case LDAP_X_SCOPE_ONELEVEL:
1723 dnParent( assertDN, &pdn );
1724 /* the common portion of the DN
1725 * already matches, so only check
1726 * if parent DN of assertedDN
1727 * is all the pattern */
1728 if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
1734 /* at present, impossible */
1742 case LDAP_X_SCOPE_REGEX:
1743 rc = regcomp(®, op.o_req_ndn.bv_val,
1744 REG_EXTENDED|REG_ICASE|REG_NOSUB);
1746 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
1752 rc = LDAP_INAPPROPRIATE_AUTH;
1756 case LDAP_X_SCOPE_GROUP: {
1759 /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
1760 * we need to append the <assertDN> so that the <group_dn> is searched
1761 * with scope "base", and the filter ensures that <assertDN> is
1762 * member of the group */
1763 tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len +
1764 assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 );
1765 if ( tmp == NULL ) {
1766 rc = LDAP_NO_MEMORY;
1769 op.ors_filterstr.bv_val = tmp;
1771 tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
1772 tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
1774 /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
1775 op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
1776 if ( op.ors_filter == NULL ) {
1777 rc = LDAP_PROTOCOL_ERROR;
1780 op.ors_scope = LDAP_SCOPE_BASE;
1782 /* hijack match DN: use that of the group instead of the assertDN;
1783 * assertDN is now in the filter */
1784 sm.dn = &op.o_req_ndn;
1790 case LDAP_X_SCOPE_USERS:
1791 if ( !BER_BVISEMPTY( assertDN ) ) {
1794 rc = LDAP_INAPPROPRIATE_AUTH;
1802 /* Must run an internal search. */
1803 if ( op.ors_filter == NULL ) {
1804 rc = LDAP_FILTER_ERROR;
1808 Debug( LDAP_DEBUG_TRACE,
1809 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
1810 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1812 op.o_bd = select_backend( &op.o_req_ndn, 1 );
1813 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1814 rc = LDAP_INAPPROPRIATE_AUTH;
1818 op.o_hdr = opx->o_hdr;
1819 op.o_tag = LDAP_REQ_SEARCH;
1821 op.o_callback = &cb;
1822 slap_op_time( &op.o_time, &op.o_tincr );
1823 op.o_do_not_cache = 1;
1824 op.o_is_auth_check = 1;
1825 /* use req_ndn as req_dn instead of non-pretty base of uri */
1826 if( !BER_BVISNULL( &base ) ) {
1827 ch_free( base.bv_val );
1828 /* just in case... */
1829 BER_BVZERO( &base );
1831 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1832 op.ors_deref = LDAP_DEREF_NEVER;
1834 op.ors_tlimit = SLAP_NO_LIMIT;
1835 op.ors_attrs = slap_anlist_no_attrs;
1836 op.ors_attrsonly = 1;
1838 op.o_bd->be_search( &op, &rs );
1843 rc = LDAP_INAPPROPRIATE_AUTH;
1847 if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1848 if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1849 if( op.ors_filter ) filter_free_x( opx, op.ors_filter, 1 );
1850 if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1852 Debug( LDAP_DEBUG_TRACE,
1853 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
1860 * This function answers the question, "Can this ID authorize to that ID?",
1861 * based on authorization rules. The rules are stored in the *searchDN, in the
1862 * attribute named by *attr. If any of those rules map to the *assertDN, the
1863 * authorization is approved.
1865 * The DNs should not have the dn: prefix
1868 slap_sasl_check_authz( Operation *op,
1869 struct berval *searchDN,
1870 struct berval *assertDN,
1871 AttributeDescription *ad,
1872 struct berval *authc )
1875 do_not_cache = op->o_do_not_cache;
1876 BerVarray vals = NULL;
1878 Debug( LDAP_DEBUG_TRACE,
1879 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
1880 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1882 /* ITS#4760: don't cache group access */
1883 op->o_do_not_cache = 1;
1884 rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
1885 op->o_do_not_cache = do_not_cache;
1886 if( rc != LDAP_SUCCESS ) goto COMPLETE;
1888 /* Check if the *assertDN matches any *vals */
1889 rc = slap_sasl_matches( op, vals, assertDN, authc );
1892 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1894 Debug( LDAP_DEBUG_TRACE,
1895 "<==slap_sasl_check_authz: %s check returning %d\n",
1896 ad->ad_cname.bv_val, rc, 0);
1902 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
1903 * return the LDAP DN to which it matches. The SASL regexp rules in the config
1904 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
1905 * search with scope=base), just return the URI (or its searchbase). Otherwise
1906 * an internal search must be done, and if that search returns exactly one
1907 * entry, return the DN of that one entry.
1912 struct berval *saslname,
1913 struct berval *sasldn,
1917 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
1919 SlapReply rs = {REP_RESULT};
1920 struct berval regout = BER_BVNULL;
1921 struct berval base = BER_BVNULL;
1923 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1924 "converting SASL name %s to a DN\n",
1925 saslname->bv_val, 0,0 );
1927 BER_BVZERO( sasldn );
1928 cb.sc_private = sasldn;
1930 /* Convert the SASL name into a minimal URI */
1931 if( !slap_authz_regexp( saslname, ®out, flags, opx->o_tmpmemctx ) ) {
1935 /* NOTE: always normalize regout because it results
1936 * from string submatch expansion */
1937 rc = slap_parseURI( opx, ®out, &base, &op.o_req_ndn,
1938 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 );
1939 if ( !BER_BVISNULL( ®out ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
1940 if ( rc != LDAP_SUCCESS ) {
1944 /* Must do an internal search */
1945 op.o_bd = select_backend( &op.o_req_ndn, 1 );
1947 switch ( op.ors_scope ) {
1948 case LDAP_X_SCOPE_EXACT:
1949 *sasldn = op.o_req_ndn;
1950 BER_BVZERO( &op.o_req_ndn );
1951 /* intentionally continue to next case */
1953 case LDAP_X_SCOPE_REGEX:
1954 case LDAP_X_SCOPE_SUBTREE:
1955 case LDAP_X_SCOPE_CHILDREN:
1956 case LDAP_X_SCOPE_ONELEVEL:
1957 case LDAP_X_SCOPE_GROUP:
1958 case LDAP_X_SCOPE_USERS:
1959 /* correctly parsed, but illegal */
1962 case LDAP_SCOPE_BASE:
1963 case LDAP_SCOPE_ONELEVEL:
1964 case LDAP_SCOPE_SUBTREE:
1965 case LDAP_SCOPE_SUBORDINATE:
1970 /* catch unhandled cases (there shouldn't be) */
1974 Debug( LDAP_DEBUG_TRACE,
1975 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1976 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1978 if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
1982 /* Must run an internal search. */
1983 if ( op.ors_filter == NULL ) {
1984 rc = LDAP_FILTER_ERROR;
1988 op.o_hdr = opx->o_hdr;
1989 op.o_tag = LDAP_REQ_SEARCH;
1990 op.o_ndn = opx->o_conn->c_ndn;
1991 op.o_callback = &cb;
1992 slap_op_time( &op.o_time, &op.o_tincr );
1993 op.o_do_not_cache = 1;
1994 op.o_is_auth_check = 1;
1995 op.ors_deref = LDAP_DEREF_NEVER;
1997 op.ors_tlimit = SLAP_NO_LIMIT;
1998 op.ors_attrs = slap_anlist_no_attrs;
1999 op.ors_attrsonly = 1;
2000 /* use req_ndn as req_dn instead of non-pretty base of uri */
2001 if( !BER_BVISNULL( &base ) ) {
2002 ch_free( base.bv_val );
2003 /* just in case... */
2004 BER_BVZERO( &base );
2006 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
2008 op.o_bd->be_search( &op, &rs );
2011 if( opx == opx->o_conn->c_sasl_bindop && !BER_BVISEMPTY( sasldn ) ) {
2012 opx->o_conn->c_authz_backend = op.o_bd;
2014 if( !BER_BVISNULL( &op.o_req_dn ) ) {
2015 slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
2017 if( !BER_BVISNULL( &op.o_req_ndn ) ) {
2018 slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
2020 if( op.ors_filter ) {
2021 filter_free_x( opx, op.ors_filter, 1 );
2023 if( !BER_BVISNULL( &op.ors_filterstr ) ) {
2024 ch_free( op.ors_filterstr.bv_val );
2027 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
2028 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
2034 /* Check if a bind can SASL authorize to another identity.
2035 * The DNs should not have the dn: prefix
2038 int slap_sasl_authorized( Operation *op,
2039 struct berval *authcDN, struct berval *authzDN )
2041 int rc = LDAP_INAPPROPRIATE_AUTH;
2043 /* User binding as anonymous */
2044 if ( !authzDN || !authzDN->bv_len || !authzDN->bv_val ) {
2049 /* User is anonymous */
2050 if ( !authcDN || !authcDN->bv_len || !authcDN->bv_val ) {
2054 Debug( LDAP_DEBUG_TRACE,
2055 "==>slap_sasl_authorized: can %s become %s?\n",
2056 authcDN->bv_len ? authcDN->bv_val : "(null)",
2057 authzDN->bv_len ? authzDN->bv_val : "(null)", 0 );
2059 /* If person is authorizing to self, succeed */
2060 if ( dn_match( authcDN, authzDN ) ) {
2065 /* Allow the manager to authorize as any DN. */
2066 if( op->o_conn->c_authz_backend &&
2067 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
2073 /* Check source rules */
2074 if( authz_policy & SASL_AUTHZ_TO ) {
2075 rc = slap_sasl_check_authz( op, authcDN, authzDN,
2076 slap_schema.si_ad_saslAuthzTo, authcDN );
2077 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
2082 /* Check destination rules */
2083 if( authz_policy & SASL_AUTHZ_FROM ) {
2084 rc = slap_sasl_check_authz( op, authzDN, authcDN,
2085 slap_schema.si_ad_saslAuthzFrom, authcDN );
2086 if( rc == LDAP_SUCCESS ) {
2091 rc = LDAP_INAPPROPRIATE_AUTH;
2095 Debug( LDAP_DEBUG_TRACE,
2096 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );