2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1998-2006 The OpenLDAP Foundation.
5 * Portions Copyright 2000 Mark Adamson, Carnegie Mellon.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
24 #include <ac/stdlib.h>
25 #include <ac/string.h>
32 #define SASLREGEX_REPLACE 10
34 #define LDAP_X_SCOPE_EXACT ((ber_int_t) 0x0010)
35 #define LDAP_X_SCOPE_REGEX ((ber_int_t) 0x0020)
36 #define LDAP_X_SCOPE_CHILDREN ((ber_int_t) 0x0030)
37 #define LDAP_X_SCOPE_SUBTREE ((ber_int_t) 0x0040)
38 #define LDAP_X_SCOPE_ONELEVEL ((ber_int_t) 0x0050)
39 #define LDAP_X_SCOPE_GROUP ((ber_int_t) 0x0060)
40 #define LDAP_X_SCOPE_USERS ((ber_int_t) 0x0070)
43 * IDs in DNauthzid form can now have a type specifier, that
44 * influences how they are used in related operations.
46 * syntax: dn[.{exact|regex}]:<val>
48 * dn.exact: the value must pass normalization and is used
50 * dn.regex: the value is treated as a regular expression
51 * in matching DN values in authz{To|From}
53 * dn: for backwards compatibility reasons, the value
54 * is treated as a regular expression, and thus
55 * it is not normalized nor validated; it is used
56 * in exact or regex comparisons based on the
59 * IDs in DNauthzid form can now have a type specifier, that
60 * influences how they are used in related operations.
62 * syntax: u[.mech[/realm]]:<val>
64 * where mech is a SIMPLE, AUTHZ, or a SASL mechanism name
65 * and realm is mechanism specific realm (separate to those
66 * which are representable as part of the principal).
69 typedef struct sasl_regexp {
70 char *sr_match; /* regexp match pattern */
71 char *sr_replace; /* regexp replace pattern */
72 regex_t sr_workspace; /* workspace for regexp engine */
73 int sr_offset[SASLREGEX_REPLACE+2]; /* offsets of $1,$2... in *replace */
76 static int nSaslRegexp = 0;
77 static SaslRegexp_t *SaslRegexp = NULL;
79 #ifdef SLAP_AUTH_REWRITE
81 struct rewrite_info *sasl_rwinfo = NULL;
82 #define AUTHID_CONTEXT "authid"
83 #endif /* SLAP_AUTH_REWRITE */
85 /* What SASL proxy authorization policies are allowed? */
86 #define SASL_AUTHZ_NONE 0x00
87 #define SASL_AUTHZ_FROM 0x01
88 #define SASL_AUTHZ_TO 0x02
89 #define SASL_AUTHZ_AND 0x10
91 static const char *policy_txt[] = {
92 "none", "from", "to", "any"
95 static int authz_policy = SASL_AUTHZ_NONE;
98 slap_sasl_match( Operation *opx, struct berval *rule,
99 struct berval *assertDN, struct berval *authc );
101 int slap_sasl_setpolicy( const char *arg )
103 int rc = LDAP_SUCCESS;
105 if ( strcasecmp( arg, "none" ) == 0 ) {
106 authz_policy = SASL_AUTHZ_NONE;
107 } else if ( strcasecmp( arg, "from" ) == 0 ) {
108 authz_policy = SASL_AUTHZ_FROM;
109 } else if ( strcasecmp( arg, "to" ) == 0 ) {
110 authz_policy = SASL_AUTHZ_TO;
111 } else if ( strcasecmp( arg, "both" ) == 0 || strcasecmp( arg, "any" ) == 0 ) {
112 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO;
113 } else if ( strcasecmp( arg, "all" ) == 0 ) {
114 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND;
121 const char * slap_sasl_getpolicy()
123 if ( authz_policy == (SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND) )
126 return policy_txt[authz_policy];
129 int slap_parse_user( struct berval *id, struct berval *user,
130 struct berval *realm, struct berval *mech )
134 assert( id != NULL );
135 assert( !BER_BVISNULL( id ) );
136 assert( user != NULL );
137 assert( realm != NULL );
138 assert( mech != NULL );
142 if ( u != 'u' && u != 'U' ) {
143 /* called with something other than u: */
144 return LDAP_PROTOCOL_ERROR;
148 * u[.mech[/realm]]:user
151 user->bv_val = ber_bvchr( id, ':' );
152 if ( BER_BVISNULL( user ) ) {
153 return LDAP_PROTOCOL_ERROR;
155 user->bv_val[ 0 ] = '\0';
157 user->bv_len = id->bv_len - ( user->bv_val - id->bv_val );
159 mech->bv_val = ber_bvchr( id, '.' );
160 if ( !BER_BVISNULL( mech ) ) {
161 mech->bv_val[ 0 ] = '\0';
163 mech->bv_len = user->bv_val - mech->bv_val - 1;
165 realm->bv_val = ber_bvchr( mech, '/' );
167 if ( !BER_BVISNULL( realm ) ) {
168 realm->bv_val[ 0 ] = '\0';
170 mech->bv_len = realm->bv_val - mech->bv_val - 1;
171 realm->bv_len = user->bv_val - realm->bv_val - 1;
178 if ( id->bv_val[ 1 ] != '\0' ) {
179 return LDAP_PROTOCOL_ERROR;
182 if ( !BER_BVISNULL( mech ) ) {
183 assert( mech->bv_val == id->bv_val + 2 );
185 AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
189 if ( !BER_BVISNULL( realm ) ) {
190 assert( realm->bv_val >= id->bv_val + 2 );
192 AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
196 /* leave "u:" before user */
199 user->bv_val[ 0 ] = u;
200 user->bv_val[ 1 ] = ':';
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 );
468 struct berval *value,
469 void *assertedValue )
471 return octetStringMatch( matchp, flags, syntax, mr, value, assertedValue );
478 struct berval *normalized,
483 int rc = LDAP_INVALID_SYNTAX;
484 LDAPURLDesc *ludp = NULL;
491 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
492 * 3) dn.regex:<pattern>
493 * 4) u[.mech[/realm]]:<ID>
494 * 5) group[/<groupClass>[/<memberAttr>]]:<DN>
498 assert( val != NULL );
499 assert( !BER_BVISNULL( val ) );
502 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
503 * 3) dn.regex:<pattern>
505 * <DN> must pass DN normalization
507 if ( !strncasecmp( val->bv_val, "dn", STRLENOF( "dn" ) ) ) {
508 struct berval out = BER_BVNULL,
512 bv.bv_val = val->bv_val + STRLENOF( "dn" );
514 if ( bv.bv_val[ 0 ] == '.' ) {
517 if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
518 bv.bv_val += STRLENOF( "exact:" );
519 scope = LDAP_X_SCOPE_EXACT;
521 } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
522 bv.bv_val += STRLENOF( "regex:" );
523 scope = LDAP_X_SCOPE_REGEX;
525 } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
526 bv.bv_val += STRLENOF( "children:" );
527 scope = LDAP_X_SCOPE_CHILDREN;
529 } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
530 bv.bv_val += STRLENOF( "subtree:" );
531 scope = LDAP_X_SCOPE_SUBTREE;
533 } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
534 bv.bv_val += STRLENOF( "onelevel:" );
535 scope = LDAP_X_SCOPE_ONELEVEL;
538 return LDAP_INVALID_SYNTAX;
542 if ( bv.bv_val[ 0 ] != ':' ) {
543 return LDAP_INVALID_SYNTAX;
545 scope = LDAP_X_SCOPE_EXACT;
549 bv.bv_val += strspn( bv.bv_val, " " );
550 /* jump here in case no type specification was present
551 * and uri was not an URI... HEADS-UP: assuming EXACT */
552 is_dn: bv.bv_len = val->bv_len - ( bv.bv_val - val->bv_val );
554 /* a single '*' means any DN without using regexes */
555 if ( ber_bvccmp( &bv, '*' ) ) {
556 ber_str2bv_x( "dn:*", STRLENOF( "dn:*" ), 1, normalized, ctx );
561 case LDAP_X_SCOPE_EXACT:
562 case LDAP_X_SCOPE_CHILDREN:
563 case LDAP_X_SCOPE_SUBTREE:
564 case LDAP_X_SCOPE_ONELEVEL:
566 rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
568 rc = dnPretty( NULL, &bv, &out, ctx );
570 if( rc != LDAP_SUCCESS ) {
571 return LDAP_INVALID_SYNTAX;
575 case LDAP_X_SCOPE_REGEX:
576 normalized->bv_len = STRLENOF( "dn.regex:" ) + bv.bv_len;
577 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
578 ptr = lutil_strcopy( normalized->bv_val, "dn.regex:" );
579 ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
584 return LDAP_INVALID_SYNTAX;
589 case LDAP_X_SCOPE_EXACT:
590 BER_BVSTR( &prefix, "dn:" );
593 case LDAP_X_SCOPE_CHILDREN:
594 BER_BVSTR( &prefix, "dn.children:" );
597 case LDAP_X_SCOPE_SUBTREE:
598 BER_BVSTR( &prefix, "dn.subtree:" );
601 case LDAP_X_SCOPE_ONELEVEL:
602 BER_BVSTR( &prefix, "dn.onelevel:" );
610 normalized->bv_len = prefix.bv_len + out.bv_len;
611 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
613 ptr = lutil_strcopy( normalized->bv_val, prefix.bv_val );
614 ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
616 ber_memfree_x( out.bv_val, ctx );
621 * 4) u[.mech[/realm]]:<ID>
623 } else if ( ( val->bv_val[ 0 ] == 'u' || val->bv_val[ 0 ] == 'U' )
624 && ( val->bv_val[ 1 ] == ':'
625 || val->bv_val[ 1 ] == '/'
626 || val->bv_val[ 1 ] == '.' ) )
628 char buf[ SLAP_LDAPDN_MAXLEN ];
634 if ( sizeof( buf ) <= val->bv_len ) {
635 return LDAP_INVALID_SYNTAX;
638 id.bv_len = val->bv_len;
640 strncpy( buf, val->bv_val, sizeof( buf ) );
642 rc = slap_parse_user( &id, &user, &realm, &mech );
643 if ( rc != LDAP_SUCCESS ) {
644 return LDAP_INVALID_SYNTAX;
647 ber_dupbv_x( normalized, val, ctx );
652 * 5) group[/groupClass[/memberAttr]]:<DN>
654 * <groupClass> defaults to "groupOfNames"
655 * <memberAttr> defaults to "member"
657 * <DN> must pass DN normalization
659 } else if ( strncasecmp( val->bv_val, "group", STRLENOF( "group" ) ) == 0 )
661 struct berval group_dn = BER_BVNULL,
662 group_oc = BER_BVNULL,
663 member_at = BER_BVNULL,
667 bv.bv_val = val->bv_val + STRLENOF( "group" );
668 bv.bv_len = val->bv_len - STRLENOF( "group" );
669 group_dn.bv_val = ber_bvchr( &bv, ':' );
670 if ( group_dn.bv_val == NULL ) {
671 /* last chance: assume it's a(n exact) DN ... */
672 bv.bv_val = val->bv_val;
673 scope = LDAP_X_SCOPE_EXACT;
678 * FIXME: we assume that "member" and "groupOfNames"
679 * are present in schema...
681 if ( bv.bv_val[ 0 ] == '/' ) {
682 ObjectClass *oc = NULL;
684 group_oc.bv_val = &bv.bv_val[ 1 ];
685 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
687 member_at.bv_val = ber_bvchr( &group_oc, '/' );
688 if ( member_at.bv_val ) {
689 AttributeDescription *ad = NULL;
690 const char *text = NULL;
692 group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
694 member_at.bv_len = group_dn.bv_val - member_at.bv_val;
695 rc = slap_bv2ad( &member_at, &ad, &text );
696 if ( rc != LDAP_SUCCESS ) {
700 member_at = ad->ad_cname;
704 oc = oc_bvfind( &group_oc );
706 return LDAP_INVALID_SYNTAX;
709 group_oc = oc->soc_cname;
713 group_dn.bv_len = val->bv_len - ( group_dn.bv_val - val->bv_val );
716 rc = dnNormalize( 0, NULL, NULL, &group_dn, &out, ctx );
718 rc = dnPretty( NULL, &group_dn, &out, ctx );
720 if ( rc != LDAP_SUCCESS ) {
724 normalized->bv_len = STRLENOF( "group" ":" ) + out.bv_len;
725 if ( !BER_BVISNULL( &group_oc ) ) {
726 normalized->bv_len += STRLENOF( "/" ) + group_oc.bv_len;
727 if ( !BER_BVISNULL( &member_at ) ) {
728 normalized->bv_len += STRLENOF( "/" ) + member_at.bv_len;
732 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
733 ptr = lutil_strcopy( normalized->bv_val, "group" );
734 if ( !BER_BVISNULL( &group_oc ) ) {
737 ptr = lutil_strncopy( ptr, group_oc.bv_val, group_oc.bv_len );
738 if ( !BER_BVISNULL( &member_at ) ) {
741 ptr = lutil_strncopy( ptr, member_at.bv_val, member_at.bv_len );
746 ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
748 ber_memfree_x( out.bv_val, ctx );
754 * ldap:///<base>??<scope>?<filter>
755 * <scope> ::= {base|one|subtree}
757 * <scope> defaults to "base"
758 * <base> must pass DN normalization
759 * <filter> must pass str2filter()
761 rc = ldap_url_parse( val->bv_val, &ludp );
763 case LDAP_URL_SUCCESS:
764 /* FIXME: the check is pedantic, but I think it's necessary,
765 * because people tend to use things like ldaps:// which
766 * gives the idea SSL is being used. Maybe we could
767 * accept ldapi:// as well, but the point is that we use
768 * an URL as an easy means to define bits of a search with
771 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
775 rc = LDAP_INVALID_SYNTAX;
779 AC_MEMCPY( ludp->lud_scheme, "ldap", STRLENOF( "ldap" ) );
782 case LDAP_URL_ERR_BADSCHEME:
784 * last chance: assume it's a(n exact) DN ...
786 * NOTE: must pass DN normalization
788 ldap_free_urldesc( ludp );
789 bv.bv_val = val->bv_val;
790 scope = LDAP_X_SCOPE_EXACT;
794 rc = LDAP_INVALID_SYNTAX;
798 if ( ( ludp->lud_host && *ludp->lud_host )
799 || ludp->lud_attrs || ludp->lud_exts )
801 /* host part must be empty */
802 /* attrs and extensions parts must be empty */
803 rc = LDAP_INVALID_SYNTAX;
807 /* Grab the filter */
808 if ( ludp->lud_filter ) {
809 struct berval filterstr;
812 lud_filter = ludp->lud_filter;
814 f = str2filter( lud_filter );
816 rc = LDAP_INVALID_SYNTAX;
819 filter2bv( f, &filterstr );
821 if ( BER_BVISNULL( &filterstr ) ) {
822 rc = LDAP_INVALID_SYNTAX;
826 ludp->lud_filter = filterstr.bv_val;
829 /* Grab the searchbase */
830 assert( ludp->lud_dn != NULL );
831 if ( ludp->lud_dn ) {
832 struct berval out = BER_BVNULL;
834 lud_dn = ludp->lud_dn;
836 ber_str2bv( lud_dn, 0, 0, &bv );
838 rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
840 rc = dnPretty( NULL, &bv, &out, ctx );
843 if ( rc != LDAP_SUCCESS ) {
847 ludp->lud_dn = out.bv_val;
851 normalized->bv_val = ldap_url_desc2str( ludp );
852 if ( normalized->bv_val ) {
853 normalized->bv_len = strlen( normalized->bv_val );
856 rc = LDAP_INVALID_SYNTAX;
861 if ( ludp->lud_filter != lud_filter ) {
862 ber_memfree( ludp->lud_filter );
864 ludp->lud_filter = lud_filter;
868 if ( ludp->lud_dn != lud_dn ) {
869 ber_memfree( ludp->lud_dn );
871 ludp->lud_dn = lud_dn;
874 ldap_free_urldesc( ludp );
885 struct berval *normalized,
890 Debug( LDAP_DEBUG_TRACE, ">>> authzNormalize: <%s>\n",
893 rc = authzPrettyNormal( val, normalized, ctx, 1 );
895 Debug( LDAP_DEBUG_TRACE, "<<< authzNormalize: <%s> (%d)\n",
896 normalized->bv_val, rc, 0 );
910 Debug( LDAP_DEBUG_TRACE, ">>> authzPretty: <%s>\n",
913 rc = authzPrettyNormal( val, out, ctx, 0 );
915 Debug( LDAP_DEBUG_TRACE, "<<< authzPretty: <%s> (%d)\n",
916 out->bv_val, rc, 0 );
927 struct berval *nbase,
939 assert( uri != NULL && !BER_BVISNULL( uri ) );
946 Debug( LDAP_DEBUG_TRACE,
947 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
949 rc = LDAP_PROTOCOL_ERROR;
952 if ( idx.bv_val[ 0 ] == '{' ) {
955 ptr = ber_bvchr( &idx, '}' ) + 1;
957 assert( ptr != (void *)1 );
959 idx.bv_len -= ptr - idx.bv_val;
965 * dn[.<dnstyle>]:<dnpattern>
966 * <dnstyle> ::= {exact|regex|children|subtree|onelevel}
968 * <dnstyle> defaults to "exact"
969 * if <dnstyle> is not "regex", <dnpattern> must pass DN normalization
971 if ( !strncasecmp( uri->bv_val, "dn", STRLENOF( "dn" ) ) ) {
972 bv.bv_val = uri->bv_val + STRLENOF( "dn" );
974 if ( bv.bv_val[ 0 ] == '.' ) {
977 if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
978 bv.bv_val += STRLENOF( "exact:" );
979 *scope = LDAP_X_SCOPE_EXACT;
981 } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
982 bv.bv_val += STRLENOF( "regex:" );
983 *scope = LDAP_X_SCOPE_REGEX;
985 } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
986 bv.bv_val += STRLENOF( "children:" );
987 *scope = LDAP_X_SCOPE_CHILDREN;
989 } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
990 bv.bv_val += STRLENOF( "subtree:" );
991 *scope = LDAP_X_SCOPE_SUBTREE;
993 } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
994 bv.bv_val += STRLENOF( "onelevel:" );
995 *scope = LDAP_X_SCOPE_ONELEVEL;
998 return LDAP_PROTOCOL_ERROR;
1002 if ( bv.bv_val[ 0 ] != ':' ) {
1003 return LDAP_PROTOCOL_ERROR;
1005 *scope = LDAP_X_SCOPE_EXACT;
1009 bv.bv_val += strspn( bv.bv_val, " " );
1010 /* jump here in case no type specification was present
1011 * and uri was not an URI... HEADS-UP: assuming EXACT */
1012 is_dn: bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
1014 /* a single '*' means any DN without using regexes */
1015 if ( ber_bvccmp( &bv, '*' ) ) {
1016 *scope = LDAP_X_SCOPE_USERS;
1020 case LDAP_X_SCOPE_EXACT:
1021 case LDAP_X_SCOPE_CHILDREN:
1022 case LDAP_X_SCOPE_SUBTREE:
1023 case LDAP_X_SCOPE_ONELEVEL:
1025 rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
1026 if( rc != LDAP_SUCCESS ) {
1030 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1035 case LDAP_X_SCOPE_REGEX:
1036 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1038 case LDAP_X_SCOPE_USERS:
1052 } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
1053 && ( uri->bv_val[ 1 ] == ':'
1054 || uri->bv_val[ 1 ] == '/'
1055 || uri->bv_val[ 1 ] == '.' ) )
1057 Connection c = *op->o_conn;
1058 char buf[ SLAP_LDAPDN_MAXLEN ];
1064 if ( sizeof( buf ) <= uri->bv_len ) {
1065 return LDAP_INVALID_SYNTAX;
1068 id.bv_len = uri->bv_len;
1070 strncpy( buf, uri->bv_val, sizeof( buf ) );
1072 rc = slap_parse_user( &id, &user, &realm, &mech );
1073 if ( rc != LDAP_SUCCESS ) {
1077 if ( !BER_BVISNULL( &mech ) ) {
1078 c.c_sasl_bind_mech = mech;
1080 BER_BVSTR( &c.c_sasl_bind_mech, "AUTHZ" );
1083 rc = slap_sasl_getdn( &c, op, &user,
1084 realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
1086 if ( rc == LDAP_SUCCESS ) {
1087 *scope = LDAP_X_SCOPE_EXACT;
1093 * group[/<groupoc>[/<groupat>]]:<groupdn>
1095 * groupoc defaults to "groupOfNames"
1096 * groupat defaults to "member"
1098 * <groupdn> must pass DN normalization
1100 } else if ( strncasecmp( uri->bv_val, "group", STRLENOF( "group" ) ) == 0 )
1102 struct berval group_dn = BER_BVNULL,
1103 group_oc = BER_BVNULL,
1104 member_at = BER_BVNULL;
1107 bv.bv_val = uri->bv_val + STRLENOF( "group" );
1108 bv.bv_len = uri->bv_len - STRLENOF( "group" );
1109 group_dn.bv_val = ber_bvchr( &bv, ':' );
1110 if ( group_dn.bv_val == NULL ) {
1111 /* last chance: assume it's a(n exact) DN ... */
1112 bv.bv_val = uri->bv_val;
1113 *scope = LDAP_X_SCOPE_EXACT;
1117 if ( bv.bv_val[ 0 ] == '/' ) {
1118 group_oc.bv_val = &bv.bv_val[ 1 ];
1119 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
1121 member_at.bv_val = ber_bvchr( &group_oc, '/' );
1122 if ( member_at.bv_val ) {
1123 group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
1125 member_at.bv_len = group_dn.bv_val - member_at.bv_val;
1128 BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
1132 BER_BVSTR( &group_oc, SLAPD_GROUP_CLASS );
1135 group_dn.bv_len = uri->bv_len - ( group_dn.bv_val - uri->bv_val );
1138 rc = dnNormalize( 0, NULL, NULL, &group_dn, nbase, op->o_tmpmemctx );
1139 if ( rc != LDAP_SUCCESS ) {
1144 ber_dupbv_x( nbase, &group_dn, op->o_tmpmemctx );
1147 *scope = LDAP_X_SCOPE_GROUP;
1149 /* FIXME: caller needs to add value of member attribute
1150 * and close brackets twice */
1151 fstr->bv_len = STRLENOF( "(&(objectClass=)(=" /* )) */ )
1152 + group_oc.bv_len + member_at.bv_len;
1153 fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
1155 tmp = lutil_strncopy( fstr->bv_val, "(&(objectClass=" /* )) */ ,
1156 STRLENOF( "(&(objectClass=" /* )) */ ) );
1157 tmp = lutil_strncopy( tmp, group_oc.bv_val, group_oc.bv_len );
1158 tmp = lutil_strncopy( tmp, /* ( */ ")(" /* ) */ ,
1159 STRLENOF( /* ( */ ")(" /* ) */ ) );
1160 tmp = lutil_strncopy( tmp, member_at.bv_val, member_at.bv_len );
1161 tmp = lutil_strncopy( tmp, "=", STRLENOF( "=" ) );
1167 * ldap:///<base>??<scope>?<filter>
1168 * <scope> ::= {base|one|subtree}
1170 * <scope> defaults to "base"
1171 * <base> must pass DN normalization
1172 * <filter> must pass str2filter()
1174 rc = ldap_url_parse( uri->bv_val, &ludp );
1176 case LDAP_URL_SUCCESS:
1177 /* FIXME: the check is pedantic, but I think it's necessary,
1178 * because people tend to use things like ldaps:// which
1179 * gives the idea SSL is being used. Maybe we could
1180 * accept ldapi:// as well, but the point is that we use
1181 * an URL as an easy means to define bits of a search with
1184 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
1188 rc = LDAP_PROTOCOL_ERROR;
1193 case LDAP_URL_ERR_BADSCHEME:
1195 * last chance: assume it's a(n exact) DN ...
1197 * NOTE: must pass DN normalization
1199 ldap_free_urldesc( ludp );
1200 bv.bv_val = uri->bv_val;
1201 *scope = LDAP_X_SCOPE_EXACT;
1205 rc = LDAP_PROTOCOL_ERROR;
1209 if ( ( ludp->lud_host && *ludp->lud_host )
1210 || ludp->lud_attrs || ludp->lud_exts )
1212 /* host part must be empty */
1213 /* attrs and extensions parts must be empty */
1214 rc = LDAP_PROTOCOL_ERROR;
1218 /* Grab the scope */
1219 *scope = ludp->lud_scope;
1221 /* Grab the filter */
1222 if ( ludp->lud_filter ) {
1223 *filter = str2filter_x( op, ludp->lud_filter );
1224 if ( *filter == NULL ) {
1225 rc = LDAP_PROTOCOL_ERROR;
1228 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
1231 /* Grab the searchbase */
1232 ber_str2bv( ludp->lud_dn, 0, 0, base );
1234 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
1236 ber_dupbv_x( nbase, base, op->o_tmpmemctx );
1241 if( rc != LDAP_SUCCESS ) {
1242 if( *filter ) filter_free_x( op, *filter );
1246 /* Don't free these, return them to caller */
1247 ludp->lud_filter = NULL;
1248 ludp->lud_dn = NULL;
1251 ldap_free_urldesc( ludp );
1255 #ifndef SLAP_AUTH_REWRITE
1256 static int slap_sasl_rx_off(char *rep, int *off)
1261 /* Precompile replace pattern. Find the $<n> placeholders */
1264 for ( c = rep; *c; c++ ) {
1265 if ( *c == '\\' && c[1] ) {
1270 if ( n == SASLREGEX_REPLACE ) {
1271 Debug( LDAP_DEBUG_ANY,
1272 "SASL replace pattern %s has too many $n "
1273 "placeholders (max %d)\n",
1274 rep, SASLREGEX_REPLACE, 0 );
1276 return( LDAP_OTHER );
1283 /* Final placeholder, after the last $n */
1287 return( LDAP_SUCCESS );
1289 #endif /* ! SLAP_AUTH_REWRITE */
1291 #ifdef SLAP_AUTH_REWRITE
1292 int slap_sasl_rewrite_config(
1302 /* init at first call */
1303 if ( sasl_rwinfo == NULL ) {
1304 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1307 /* strip "authid-" prefix for parsing */
1309 argv[0] += STRLENOF( "authid-" );
1310 rc = rewrite_parse( sasl_rwinfo, fname, lineno, argc, argv );
1317 slap_sasl_rewrite_destroy( void )
1319 if ( sasl_rwinfo ) {
1320 rewrite_info_delete( &sasl_rwinfo );
1327 int slap_sasl_regexp_rewrite_config(
1331 const char *replace,
1332 const char *context )
1335 char *argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL };
1337 /* init at first call */
1338 if ( sasl_rwinfo == NULL ) {
1339 char *argvEngine[] = { "rewriteEngine", "on", NULL };
1340 char *argvContext[] = { "rewriteContext", NULL, NULL };
1342 /* initialize rewrite engine */
1343 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1345 /* switch on rewrite engine */
1346 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine );
1347 if (rc != LDAP_SUCCESS) {
1351 /* create generic authid context */
1352 argvContext[1] = AUTHID_CONTEXT;
1353 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvContext );
1354 if (rc != LDAP_SUCCESS) {
1359 argvRule[1] = (char *)match;
1360 argvRule[2] = (char *)replace;
1361 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule );
1365 #endif /* SLAP_AUTH_REWRITE */
1367 int slap_sasl_regexp_config( const char *match, const char *replace )
1372 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
1373 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
1375 reg = &SaslRegexp[nSaslRegexp];
1377 #ifdef SLAP_AUTH_REWRITE
1378 rc = slap_sasl_regexp_rewrite_config( "sasl-regexp", 0,
1379 match, replace, AUTHID_CONTEXT );
1380 #else /* ! SLAP_AUTH_REWRITE */
1382 /* Precompile matching pattern */
1383 rc = regcomp( ®->sr_workspace, match, REG_EXTENDED|REG_ICASE );
1385 Debug( LDAP_DEBUG_ANY,
1386 "SASL match pattern %s could not be compiled by regexp engine\n",
1389 #ifdef ENABLE_REWRITE
1390 /* Dummy block to force symbol references in librewrite */
1391 if ( slapMode == ( SLAP_SERVER_MODE|SLAP_TOOL_MODE )) {
1392 rewrite_info_init( 0 );
1395 return( LDAP_OTHER );
1398 rc = slap_sasl_rx_off( replace, reg->sr_offset );
1399 #endif /* ! SLAP_AUTH_REWRITE */
1400 if ( rc == LDAP_SUCCESS ) {
1401 reg->sr_match = ch_strdup( match );
1402 reg->sr_replace = ch_strdup( replace );
1411 slap_sasl_regexp_destroy( void )
1416 for ( n = 0; n < nSaslRegexp; n++ ) {
1417 ch_free( SaslRegexp[ n ].sr_match );
1418 ch_free( SaslRegexp[ n ].sr_replace );
1419 #ifndef SLAP_AUTH_REWRITE
1420 regfree( &SaslRegexp[ n ].sr_workspace );
1421 #endif /* SLAP_AUTH_REWRITE */
1424 ch_free( SaslRegexp );
1427 #ifdef SLAP_AUTH_REWRITE
1428 slap_sasl_rewrite_destroy();
1429 #endif /* SLAP_AUTH_REWRITE */
1432 void slap_sasl_regexp_unparse( BerVarray *out )
1435 BerVarray bva = NULL;
1436 char ibuf[32], *ptr;
1439 if ( !nSaslRegexp ) return;
1442 bva = ch_malloc( (nSaslRegexp+1) * sizeof(struct berval) );
1443 BER_BVZERO(bva+nSaslRegexp);
1444 for ( i=0; i<nSaslRegexp; i++ ) {
1445 idx.bv_len = sprintf( idx.bv_val, "{%d}", i);
1446 bva[i].bv_len = idx.bv_len + strlen( SaslRegexp[i].sr_match ) +
1447 strlen( SaslRegexp[i].sr_replace ) + 5;
1448 bva[i].bv_val = ch_malloc( bva[i].bv_len+1 );
1449 ptr = lutil_strcopy( bva[i].bv_val, ibuf );
1451 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match );
1452 ptr = lutil_strcopy( ptr, "\" \"" );
1453 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace );
1460 #ifndef SLAP_AUTH_REWRITE
1461 /* Perform replacement on regexp matches */
1462 static void slap_sasl_rx_exp(
1466 const char *saslname,
1470 int i, n, len, insert;
1472 /* Get the total length of the final URI */
1476 while( off[n] >= 0 ) {
1477 /* Len of next section from replacement string (x,y,z above) */
1478 len += off[n] - off[n-1] - 2;
1482 /* Len of string from saslname that matched next $i (b,d above) */
1483 i = rep[ off[n] + 1 ] - '0';
1484 len += str[i].rm_eo - str[i].rm_so;
1487 out->bv_val = slap_sl_malloc( len + 1, ctx );
1490 /* Fill in URI with replace string, replacing $i as we go */
1493 while( off[n] >= 0) {
1494 /* Paste in next section from replacement string (x,y,z above) */
1495 len = off[n] - off[n-1] - 2;
1496 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
1501 /* Paste in string from saslname that matched next $i (b,d above) */
1502 i = rep[ off[n] + 1 ] - '0';
1503 len = str[i].rm_eo - str[i].rm_so;
1504 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
1510 out->bv_val[insert] = '\0';
1512 #endif /* ! SLAP_AUTH_REWRITE */
1514 /* Take the passed in SASL name and attempt to convert it into an
1515 LDAP URI to find the matching LDAP entry, using the pattern matching
1516 strings given in the saslregexp config file directive(s) */
1518 static int slap_authz_regexp( struct berval *in, struct berval *out,
1519 int flags, void *ctx )
1521 #ifdef SLAP_AUTH_REWRITE
1522 const char *context = AUTHID_CONTEXT;
1524 if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
1528 /* FIXME: if aware of authc/authz mapping,
1529 * we could use different contexts ... */
1530 switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL,
1533 case REWRITE_REGEXEC_OK:
1534 if ( !BER_BVISNULL( out ) ) {
1535 char *val = out->bv_val;
1536 ber_str2bv_x( val, 0, 1, out, ctx );
1537 if ( val != in->bv_val ) {
1541 ber_dupbv_x( out, in, ctx );
1543 Debug( LDAP_DEBUG_ARGS,
1544 "[rw] %s: \"%s\" -> \"%s\"\n",
1545 context, in->bv_val, out->bv_val );
1548 case REWRITE_REGEXEC_UNWILLING:
1549 case REWRITE_REGEXEC_ERR:
1554 #else /* ! SLAP_AUTH_REWRITE */
1555 char *saslname = in->bv_val;
1557 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
1560 memset( out, 0, sizeof( *out ) );
1562 Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
1565 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
1569 /* Match the normalized SASL name to the saslregexp patterns */
1570 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
1571 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE,
1572 sr_strings, 0) == 0 )
1576 if( i >= nSaslRegexp ) return( 0 );
1579 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
1580 * replace pattern of the form "x$1y$2z". The returned string needs
1581 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
1583 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
1584 sr_strings, saslname, out, ctx );
1586 Debug( LDAP_DEBUG_TRACE,
1587 "slap_authz_regexp: converted SASL name to %s\n",
1588 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
1591 #endif /* ! SLAP_AUTH_REWRITE */
1594 /* This callback actually does some work...*/
1595 static int sasl_sc_sasl2dn( Operation *op, SlapReply *rs )
1597 struct berval *ndn = op->o_callback->sc_private;
1599 if ( rs->sr_type != REP_SEARCH ) return LDAP_SUCCESS;
1601 /* We only want to be called once */
1602 if ( !BER_BVISNULL( ndn ) ) {
1603 op->o_tmpfree( ndn->bv_val, op->o_tmpmemctx );
1606 Debug( LDAP_DEBUG_TRACE,
1607 "%s: slap_sc_sasl2dn: search DN returned more than 1 entry\n",
1608 op->o_log_prefix, 0, 0 );
1612 ber_dupbv_x( ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
1613 return LDAP_SUCCESS;
1617 typedef struct smatch_info {
1622 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
1624 smatch_info *sm = o->o_callback->sc_private;
1626 if ( rs->sr_type != REP_SEARCH ) {
1627 if ( rs->sr_err != LDAP_SUCCESS ) {
1633 if ( sm->match == 1 ) {
1638 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
1649 slap_sasl_matches( Operation *op, BerVarray rules,
1650 struct berval *assertDN, struct berval *authc )
1652 int rc = LDAP_INAPPROPRIATE_AUTH;
1654 if ( rules != NULL ) {
1657 for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
1658 rc = slap_sasl_match( op, &rules[i], assertDN, authc );
1659 if ( rc == LDAP_SUCCESS ) break;
1667 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
1668 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
1669 * the rule must be used as an internal search for entries. If that search
1670 * returns the *assertDN entry, the match is successful.
1672 * The assertDN should not have the dn: prefix
1676 slap_sasl_match( Operation *opx, struct berval *rule,
1677 struct berval *assertDN, struct berval *authc )
1682 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
1684 SlapReply rs = {REP_RESULT};
1685 struct berval base = BER_BVNULL;
1689 cb.sc_private = &sm;
1691 Debug( LDAP_DEBUG_TRACE,
1692 "===>slap_sasl_match: comparing DN %s to rule %s\n",
1693 assertDN->bv_val, rule->bv_val, 0 );
1695 /* NOTE: don't normalize rule if authz syntax is enabled */
1696 rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn,
1697 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 0 );
1699 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
1701 switch ( op.ors_scope ) {
1702 case LDAP_X_SCOPE_EXACT:
1704 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
1707 rc = LDAP_INAPPROPRIATE_AUTH;
1711 case LDAP_X_SCOPE_CHILDREN:
1712 case LDAP_X_SCOPE_SUBTREE:
1713 case LDAP_X_SCOPE_ONELEVEL:
1715 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
1717 rc = LDAP_INAPPROPRIATE_AUTH;
1719 if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
1722 } else if ( d > 0 ) {
1725 /* leave room for at least one char of attributeType,
1726 * one for '=' and one for ',' */
1727 if ( d < STRLENOF( "x=,") ) {
1731 bv.bv_len = op.o_req_ndn.bv_len;
1732 bv.bv_val = assertDN->bv_val + d;
1734 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
1735 switch ( op.ors_scope ) {
1736 case LDAP_X_SCOPE_SUBTREE:
1737 case LDAP_X_SCOPE_CHILDREN:
1741 case LDAP_X_SCOPE_ONELEVEL:
1745 dnParent( assertDN, &pdn );
1746 /* the common portion of the DN
1747 * already matches, so only check
1748 * if parent DN of assertedDN
1749 * is all the pattern */
1750 if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
1756 /* at present, impossible */
1764 case LDAP_X_SCOPE_REGEX:
1765 rc = regcomp(®, op.o_req_ndn.bv_val,
1766 REG_EXTENDED|REG_ICASE|REG_NOSUB);
1768 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
1774 rc = LDAP_INAPPROPRIATE_AUTH;
1778 case LDAP_X_SCOPE_GROUP: {
1781 /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
1782 * we need to append the <assertDN> so that the <group_dn> is searched
1783 * with scope "base", and the filter ensures that <assertDN> is
1784 * member of the group */
1785 tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len +
1786 assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 );
1787 if ( tmp == NULL ) {
1788 rc = LDAP_NO_MEMORY;
1791 op.ors_filterstr.bv_val = tmp;
1793 tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
1794 tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
1796 /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
1797 op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
1798 if ( op.ors_filter == NULL ) {
1799 rc = LDAP_PROTOCOL_ERROR;
1802 op.ors_scope = LDAP_SCOPE_BASE;
1804 /* hijack match DN: use that of the group instead of the assertDN;
1805 * assertDN is now in the filter */
1806 sm.dn = &op.o_req_ndn;
1812 case LDAP_X_SCOPE_USERS:
1813 if ( !BER_BVISEMPTY( assertDN ) ) {
1816 rc = LDAP_INAPPROPRIATE_AUTH;
1824 /* Must run an internal search. */
1825 if ( op.ors_filter == NULL ) {
1826 rc = LDAP_FILTER_ERROR;
1830 Debug( LDAP_DEBUG_TRACE,
1831 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
1832 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1834 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1835 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1836 rc = LDAP_INAPPROPRIATE_AUTH;
1840 op.o_hdr = opx->o_hdr;
1841 op.o_tag = LDAP_REQ_SEARCH;
1843 op.o_callback = &cb;
1844 slap_op_time( &op.o_time, &op.o_tincr );
1845 op.o_do_not_cache = 1;
1846 op.o_is_auth_check = 1;
1847 /* use req_ndn as req_dn instead of non-pretty base of uri */
1848 if( !BER_BVISNULL( &base ) ) {
1849 ch_free( base.bv_val );
1850 /* just in case... */
1851 BER_BVZERO( &base );
1853 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1854 op.ors_deref = LDAP_DEREF_NEVER;
1856 op.ors_tlimit = SLAP_NO_LIMIT;
1857 op.ors_attrs = slap_anlist_no_attrs;
1858 op.ors_attrsonly = 1;
1860 op.o_bd->be_search( &op, &rs );
1862 if (sm.match == 1) {
1865 rc = LDAP_INAPPROPRIATE_AUTH;
1869 if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1870 if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1871 if( op.ors_filter ) filter_free_x( opx, op.ors_filter );
1872 if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1874 Debug( LDAP_DEBUG_TRACE,
1875 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
1882 * This function answers the question, "Can this ID authorize to that ID?",
1883 * based on authorization rules. The rules are stored in the *searchDN, in the
1884 * attribute named by *attr. If any of those rules map to the *assertDN, the
1885 * authorization is approved.
1887 * The DNs should not have the dn: prefix
1890 slap_sasl_check_authz( Operation *op,
1891 struct berval *searchDN,
1892 struct berval *assertDN,
1893 AttributeDescription *ad,
1894 struct berval *authc )
1897 BerVarray vals = NULL;
1899 Debug( LDAP_DEBUG_TRACE,
1900 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
1901 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1903 rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
1904 if( rc != LDAP_SUCCESS ) goto COMPLETE;
1906 /* Check if the *assertDN matches any *vals */
1907 rc = slap_sasl_matches( op, vals, assertDN, authc );
1910 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1912 Debug( LDAP_DEBUG_TRACE,
1913 "<==slap_sasl_check_authz: %s check returning %d\n",
1914 ad->ad_cname.bv_val, rc, 0);
1920 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
1921 * return the LDAP DN to which it matches. The SASL regexp rules in the config
1922 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
1923 * search with scope=base), just return the URI (or its searchbase). Otherwise
1924 * an internal search must be done, and if that search returns exactly one
1925 * entry, return the DN of that one entry.
1930 struct berval *saslname,
1931 struct berval *sasldn,
1935 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
1937 SlapReply rs = {REP_RESULT};
1938 struct berval regout = BER_BVNULL;
1939 struct berval base = BER_BVNULL;
1941 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1942 "converting SASL name %s to a DN\n",
1943 saslname->bv_val, 0,0 );
1945 BER_BVZERO( sasldn );
1946 cb.sc_private = sasldn;
1948 /* Convert the SASL name into a minimal URI */
1949 if( !slap_authz_regexp( saslname, ®out, flags, opx->o_tmpmemctx ) ) {
1953 /* NOTE: always normalize regout because it results
1954 * from string submatch expansion */
1955 rc = slap_parseURI( opx, ®out, &base, &op.o_req_ndn,
1956 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 );
1957 if ( !BER_BVISNULL( ®out ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
1958 if ( rc != LDAP_SUCCESS ) {
1962 /* Must do an internal search */
1963 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1965 switch ( op.ors_scope ) {
1966 case LDAP_X_SCOPE_EXACT:
1967 *sasldn = op.o_req_ndn;
1968 BER_BVZERO( &op.o_req_ndn );
1969 /* intentionally continue to next case */
1971 case LDAP_X_SCOPE_REGEX:
1972 case LDAP_X_SCOPE_SUBTREE:
1973 case LDAP_X_SCOPE_CHILDREN:
1974 case LDAP_X_SCOPE_ONELEVEL:
1975 case LDAP_X_SCOPE_GROUP:
1976 case LDAP_X_SCOPE_USERS:
1977 /* correctly parsed, but illegal */
1980 case LDAP_SCOPE_BASE:
1981 case LDAP_SCOPE_ONELEVEL:
1982 case LDAP_SCOPE_SUBTREE:
1983 case LDAP_SCOPE_SUBORDINATE:
1988 /* catch unhandled cases (there shouldn't be) */
1992 Debug( LDAP_DEBUG_TRACE,
1993 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1994 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1996 if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
2000 /* Must run an internal search. */
2001 if ( op.ors_filter == NULL ) {
2002 rc = LDAP_FILTER_ERROR;
2006 op.o_hdr = opx->o_hdr;
2007 op.o_tag = LDAP_REQ_SEARCH;
2008 op.o_ndn = opx->o_conn->c_ndn;
2009 op.o_callback = &cb;
2010 slap_op_time( &op.o_time, &op.o_tincr );
2011 op.o_do_not_cache = 1;
2012 op.o_is_auth_check = 1;
2013 op.ors_deref = LDAP_DEREF_NEVER;
2015 op.ors_tlimit = SLAP_NO_LIMIT;
2016 op.ors_attrs = slap_anlist_no_attrs;
2017 op.ors_attrsonly = 1;
2018 /* use req_ndn as req_dn instead of non-pretty base of uri */
2019 if( !BER_BVISNULL( &base ) ) {
2020 ch_free( base.bv_val );
2021 /* just in case... */
2022 BER_BVZERO( &base );
2024 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
2026 op.o_bd->be_search( &op, &rs );
2029 if( !BER_BVISEMPTY( sasldn ) ) {
2030 opx->o_conn->c_authz_backend = op.o_bd;
2032 if( !BER_BVISNULL( &op.o_req_dn ) ) {
2033 slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
2035 if( !BER_BVISNULL( &op.o_req_ndn ) ) {
2036 slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
2038 if( op.ors_filter ) {
2039 filter_free_x( opx, op.ors_filter );
2041 if( !BER_BVISNULL( &op.ors_filterstr ) ) {
2042 ch_free( op.ors_filterstr.bv_val );
2045 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
2046 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
2052 /* Check if a bind can SASL authorize to another identity.
2053 * The DNs should not have the dn: prefix
2056 int slap_sasl_authorized( Operation *op,
2057 struct berval *authcDN, struct berval *authzDN )
2059 int rc = LDAP_INAPPROPRIATE_AUTH;
2061 /* User binding as anonymous */
2062 if ( authzDN == NULL ) {
2067 Debug( LDAP_DEBUG_TRACE,
2068 "==>slap_sasl_authorized: can %s become %s?\n",
2069 authcDN->bv_len ? authcDN->bv_val : "(null)",
2070 authzDN->bv_len ? authzDN->bv_val : "(null)", 0 );
2072 /* If person is authorizing to self, succeed */
2073 if ( dn_match( authcDN, authzDN ) ) {
2078 /* Allow the manager to authorize as any DN. */
2079 if( op->o_conn->c_authz_backend &&
2080 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
2086 /* Check source rules */
2087 if( authz_policy & SASL_AUTHZ_TO ) {
2088 rc = slap_sasl_check_authz( op, authcDN, authzDN,
2089 slap_schema.si_ad_saslAuthzTo, authcDN );
2090 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
2095 /* Check destination rules */
2096 if( authz_policy & SASL_AUTHZ_FROM ) {
2097 rc = slap_sasl_check_authz( op, authzDN, authcDN,
2098 slap_schema.si_ad_saslAuthzFrom, authcDN );
2099 if( rc == LDAP_SUCCESS ) {
2104 rc = LDAP_INAPPROPRIATE_AUTH;
2108 Debug( LDAP_DEBUG_TRACE,
2109 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );