2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1998-2007 The OpenLDAP Foundation.
5 * Portions Copyright 2000 Mark Adamson, Carnegie Mellon.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
12 * A copy of this license is available in the file LICENSE in the
13 * top-level directory of the distribution or, alternatively, at
14 * <http://www.OpenLDAP.org/license.html>.
24 #include <ac/stdlib.h>
25 #include <ac/string.h>
32 #define SASLREGEX_REPLACE 10
34 #define LDAP_X_SCOPE_EXACT ((ber_int_t) 0x0010)
35 #define LDAP_X_SCOPE_REGEX ((ber_int_t) 0x0020)
36 #define LDAP_X_SCOPE_CHILDREN ((ber_int_t) 0x0030)
37 #define LDAP_X_SCOPE_SUBTREE ((ber_int_t) 0x0040)
38 #define LDAP_X_SCOPE_ONELEVEL ((ber_int_t) 0x0050)
39 #define LDAP_X_SCOPE_GROUP ((ber_int_t) 0x0060)
40 #define LDAP_X_SCOPE_USERS ((ber_int_t) 0x0070)
43 * IDs in DNauthzid form can now have a type specifier, that
44 * influences how they are used in related operations.
46 * syntax: dn[.{exact|regex}]:<val>
48 * dn.exact: the value must pass normalization and is used
50 * dn.regex: the value is treated as a regular expression
51 * in matching DN values in authz{To|From}
53 * dn: for backwards compatibility reasons, the value
54 * is treated as a regular expression, and thus
55 * it is not normalized nor validated; it is used
56 * in exact or regex comparisons based on the
59 * IDs in DNauthzid form can now have a type specifier, that
60 * influences how they are used in related operations.
62 * syntax: u[.mech[/realm]]:<val>
64 * where mech is a SIMPLE, AUTHZ, or a SASL mechanism name
65 * and realm is mechanism specific realm (separate to those
66 * which are representable as part of the principal).
69 typedef struct sasl_regexp {
70 char *sr_match; /* regexp match pattern */
71 char *sr_replace; /* regexp replace pattern */
72 regex_t sr_workspace; /* workspace for regexp engine */
73 int sr_offset[SASLREGEX_REPLACE+2]; /* offsets of $1,$2... in *replace */
76 static int nSaslRegexp = 0;
77 static SaslRegexp_t *SaslRegexp = NULL;
79 #ifdef SLAP_AUTH_REWRITE
81 struct rewrite_info *sasl_rwinfo = NULL;
82 #define AUTHID_CONTEXT "authid"
83 #endif /* SLAP_AUTH_REWRITE */
85 /* What SASL proxy authorization policies are allowed? */
86 #define SASL_AUTHZ_NONE 0x00
87 #define SASL_AUTHZ_FROM 0x01
88 #define SASL_AUTHZ_TO 0x02
89 #define SASL_AUTHZ_AND 0x10
91 static const char *policy_txt[] = {
92 "none", "from", "to", "any"
95 static int authz_policy = SASL_AUTHZ_NONE;
98 slap_sasl_match( Operation *opx, struct berval *rule,
99 struct berval *assertDN, struct berval *authc );
101 int slap_sasl_setpolicy( const char *arg )
103 int rc = LDAP_SUCCESS;
105 if ( strcasecmp( arg, "none" ) == 0 ) {
106 authz_policy = SASL_AUTHZ_NONE;
107 } else if ( strcasecmp( arg, "from" ) == 0 ) {
108 authz_policy = SASL_AUTHZ_FROM;
109 } else if ( strcasecmp( arg, "to" ) == 0 ) {
110 authz_policy = SASL_AUTHZ_TO;
111 } else if ( strcasecmp( arg, "both" ) == 0 || strcasecmp( arg, "any" ) == 0 ) {
112 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO;
113 } else if ( strcasecmp( arg, "all" ) == 0 ) {
114 authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND;
121 const char * slap_sasl_getpolicy()
123 if ( authz_policy == (SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND) )
126 return policy_txt[authz_policy];
129 int slap_parse_user( struct berval *id, struct berval *user,
130 struct berval *realm, struct berval *mech )
134 assert( id != NULL );
135 assert( !BER_BVISNULL( id ) );
136 assert( user != NULL );
137 assert( realm != NULL );
138 assert( mech != NULL );
142 if ( u != 'u' && u != 'U' ) {
143 /* called with something other than u: */
144 return LDAP_PROTOCOL_ERROR;
148 * u[.mech[/realm]]:user
151 user->bv_val = ber_bvchr( id, ':' );
152 if ( BER_BVISNULL( user ) ) {
153 return LDAP_PROTOCOL_ERROR;
155 user->bv_val[ 0 ] = '\0';
157 user->bv_len = id->bv_len - ( user->bv_val - id->bv_val );
159 mech->bv_val = ber_bvchr( id, '.' );
160 if ( !BER_BVISNULL( mech ) ) {
161 mech->bv_val[ 0 ] = '\0';
163 mech->bv_len = user->bv_val - mech->bv_val - 1;
165 realm->bv_val = ber_bvchr( mech, '/' );
167 if ( !BER_BVISNULL( realm ) ) {
168 realm->bv_val[ 0 ] = '\0';
170 mech->bv_len = realm->bv_val - mech->bv_val - 1;
171 realm->bv_len = user->bv_val - realm->bv_val - 1;
178 if ( id->bv_val[ 1 ] != '\0' ) {
179 return LDAP_PROTOCOL_ERROR;
182 if ( !BER_BVISNULL( mech ) ) {
183 assert( mech->bv_val == id->bv_val + 2 );
185 AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
189 if ( !BER_BVISNULL( realm ) ) {
190 assert( realm->bv_val >= id->bv_val + 2 );
192 AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
196 /* leave "u:" before user */
199 user->bv_val[ 0 ] = u;
200 user->bv_val[ 1 ] = ':';
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 );
1133 BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
1136 group_dn.bv_len = uri->bv_len - ( group_dn.bv_val - uri->bv_val );
1139 rc = dnNormalize( 0, NULL, NULL, &group_dn, nbase, op->o_tmpmemctx );
1140 if ( rc != LDAP_SUCCESS ) {
1145 ber_dupbv_x( nbase, &group_dn, op->o_tmpmemctx );
1148 *scope = LDAP_X_SCOPE_GROUP;
1150 /* FIXME: caller needs to add value of member attribute
1151 * and close brackets twice */
1152 fstr->bv_len = STRLENOF( "(&(objectClass=)(=" /* )) */ )
1153 + group_oc.bv_len + member_at.bv_len;
1154 fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
1156 tmp = lutil_strncopy( fstr->bv_val, "(&(objectClass=" /* )) */ ,
1157 STRLENOF( "(&(objectClass=" /* )) */ ) );
1158 tmp = lutil_strncopy( tmp, group_oc.bv_val, group_oc.bv_len );
1159 tmp = lutil_strncopy( tmp, /* ( */ ")(" /* ) */ ,
1160 STRLENOF( /* ( */ ")(" /* ) */ ) );
1161 tmp = lutil_strncopy( tmp, member_at.bv_val, member_at.bv_len );
1162 tmp = lutil_strncopy( tmp, "=", STRLENOF( "=" ) );
1168 * ldap:///<base>??<scope>?<filter>
1169 * <scope> ::= {base|one|subtree}
1171 * <scope> defaults to "base"
1172 * <base> must pass DN normalization
1173 * <filter> must pass str2filter()
1175 rc = ldap_url_parse( uri->bv_val, &ludp );
1177 case LDAP_URL_SUCCESS:
1178 /* FIXME: the check is pedantic, but I think it's necessary,
1179 * because people tend to use things like ldaps:// which
1180 * gives the idea SSL is being used. Maybe we could
1181 * accept ldapi:// as well, but the point is that we use
1182 * an URL as an easy means to define bits of a search with
1185 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
1189 rc = LDAP_PROTOCOL_ERROR;
1194 case LDAP_URL_ERR_BADSCHEME:
1196 * last chance: assume it's a(n exact) DN ...
1198 * NOTE: must pass DN normalization
1200 ldap_free_urldesc( ludp );
1201 bv.bv_val = uri->bv_val;
1202 *scope = LDAP_X_SCOPE_EXACT;
1206 rc = LDAP_PROTOCOL_ERROR;
1210 if ( ( ludp->lud_host && *ludp->lud_host )
1211 || ludp->lud_attrs || ludp->lud_exts )
1213 /* host part must be empty */
1214 /* attrs and extensions parts must be empty */
1215 rc = LDAP_PROTOCOL_ERROR;
1219 /* Grab the scope */
1220 *scope = ludp->lud_scope;
1222 /* Grab the filter */
1223 if ( ludp->lud_filter ) {
1224 *filter = str2filter_x( op, ludp->lud_filter );
1225 if ( *filter == NULL ) {
1226 rc = LDAP_PROTOCOL_ERROR;
1229 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
1232 /* Grab the searchbase */
1233 ber_str2bv( ludp->lud_dn, 0, 0, base );
1235 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
1237 ber_dupbv_x( nbase, base, op->o_tmpmemctx );
1242 if( rc != LDAP_SUCCESS ) {
1243 if( *filter ) filter_free_x( op, *filter );
1247 /* Don't free these, return them to caller */
1248 ludp->lud_filter = NULL;
1249 ludp->lud_dn = NULL;
1252 ldap_free_urldesc( ludp );
1256 #ifndef SLAP_AUTH_REWRITE
1257 static int slap_sasl_rx_off(char *rep, int *off)
1262 /* Precompile replace pattern. Find the $<n> placeholders */
1265 for ( c = rep; *c; c++ ) {
1266 if ( *c == '\\' && c[1] ) {
1271 if ( n == SASLREGEX_REPLACE ) {
1272 Debug( LDAP_DEBUG_ANY,
1273 "SASL replace pattern %s has too many $n "
1274 "placeholders (max %d)\n",
1275 rep, SASLREGEX_REPLACE, 0 );
1277 return( LDAP_OTHER );
1284 /* Final placeholder, after the last $n */
1288 return( LDAP_SUCCESS );
1290 #endif /* ! SLAP_AUTH_REWRITE */
1292 #ifdef SLAP_AUTH_REWRITE
1293 int slap_sasl_rewrite_config(
1303 /* init at first call */
1304 if ( sasl_rwinfo == NULL ) {
1305 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1308 /* strip "authid-" prefix for parsing */
1310 argv[0] += STRLENOF( "authid-" );
1311 rc = rewrite_parse( sasl_rwinfo, fname, lineno, argc, argv );
1318 slap_sasl_rewrite_destroy( void )
1320 if ( sasl_rwinfo ) {
1321 rewrite_info_delete( &sasl_rwinfo );
1328 int slap_sasl_regexp_rewrite_config(
1332 const char *replace,
1333 const char *context )
1336 char *argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL };
1338 /* init at first call */
1339 if ( sasl_rwinfo == NULL ) {
1340 char *argvEngine[] = { "rewriteEngine", "on", NULL };
1341 char *argvContext[] = { "rewriteContext", NULL, NULL };
1343 /* initialize rewrite engine */
1344 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1346 /* switch on rewrite engine */
1347 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine );
1348 if (rc != LDAP_SUCCESS) {
1352 /* create generic authid context */
1353 argvContext[1] = AUTHID_CONTEXT;
1354 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvContext );
1355 if (rc != LDAP_SUCCESS) {
1360 argvRule[1] = (char *)match;
1361 argvRule[2] = (char *)replace;
1362 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule );
1366 #endif /* SLAP_AUTH_REWRITE */
1368 int slap_sasl_regexp_config( const char *match, const char *replace )
1373 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
1374 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
1376 reg = &SaslRegexp[nSaslRegexp];
1378 #ifdef SLAP_AUTH_REWRITE
1379 rc = slap_sasl_regexp_rewrite_config( "sasl-regexp", 0,
1380 match, replace, AUTHID_CONTEXT );
1381 #else /* ! SLAP_AUTH_REWRITE */
1383 /* Precompile matching pattern */
1384 rc = regcomp( ®->sr_workspace, match, REG_EXTENDED|REG_ICASE );
1386 Debug( LDAP_DEBUG_ANY,
1387 "SASL match pattern %s could not be compiled by regexp engine\n",
1390 #ifdef ENABLE_REWRITE
1391 /* Dummy block to force symbol references in librewrite */
1392 if ( slapMode == ( SLAP_SERVER_MODE|SLAP_TOOL_MODE )) {
1393 rewrite_info_init( 0 );
1396 return( LDAP_OTHER );
1399 rc = slap_sasl_rx_off( replace, reg->sr_offset );
1400 #endif /* ! SLAP_AUTH_REWRITE */
1401 if ( rc == LDAP_SUCCESS ) {
1402 reg->sr_match = ch_strdup( match );
1403 reg->sr_replace = ch_strdup( replace );
1412 slap_sasl_regexp_destroy( void )
1417 for ( n = 0; n < nSaslRegexp; n++ ) {
1418 ch_free( SaslRegexp[ n ].sr_match );
1419 ch_free( SaslRegexp[ n ].sr_replace );
1420 #ifndef SLAP_AUTH_REWRITE
1421 regfree( &SaslRegexp[ n ].sr_workspace );
1422 #endif /* SLAP_AUTH_REWRITE */
1425 ch_free( SaslRegexp );
1428 #ifdef SLAP_AUTH_REWRITE
1429 slap_sasl_rewrite_destroy();
1430 #endif /* SLAP_AUTH_REWRITE */
1433 void slap_sasl_regexp_unparse( BerVarray *out )
1436 BerVarray bva = NULL;
1437 char ibuf[32], *ptr;
1440 if ( !nSaslRegexp ) return;
1443 bva = ch_malloc( (nSaslRegexp+1) * sizeof(struct berval) );
1444 BER_BVZERO(bva+nSaslRegexp);
1445 for ( i=0; i<nSaslRegexp; i++ ) {
1446 idx.bv_len = sprintf( idx.bv_val, "{%d}", i);
1447 bva[i].bv_len = idx.bv_len + strlen( SaslRegexp[i].sr_match ) +
1448 strlen( SaslRegexp[i].sr_replace ) + 5;
1449 bva[i].bv_val = ch_malloc( bva[i].bv_len+1 );
1450 ptr = lutil_strcopy( bva[i].bv_val, ibuf );
1452 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match );
1453 ptr = lutil_strcopy( ptr, "\" \"" );
1454 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace );
1461 #ifndef SLAP_AUTH_REWRITE
1462 /* Perform replacement on regexp matches */
1463 static void slap_sasl_rx_exp(
1467 const char *saslname,
1471 int i, n, len, insert;
1473 /* Get the total length of the final URI */
1477 while( off[n] >= 0 ) {
1478 /* Len of next section from replacement string (x,y,z above) */
1479 len += off[n] - off[n-1] - 2;
1483 /* Len of string from saslname that matched next $i (b,d above) */
1484 i = rep[ off[n] + 1 ] - '0';
1485 len += str[i].rm_eo - str[i].rm_so;
1488 out->bv_val = slap_sl_malloc( len + 1, ctx );
1491 /* Fill in URI with replace string, replacing $i as we go */
1494 while( off[n] >= 0) {
1495 /* Paste in next section from replacement string (x,y,z above) */
1496 len = off[n] - off[n-1] - 2;
1497 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
1502 /* Paste in string from saslname that matched next $i (b,d above) */
1503 i = rep[ off[n] + 1 ] - '0';
1504 len = str[i].rm_eo - str[i].rm_so;
1505 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
1511 out->bv_val[insert] = '\0';
1513 #endif /* ! SLAP_AUTH_REWRITE */
1515 /* Take the passed in SASL name and attempt to convert it into an
1516 LDAP URI to find the matching LDAP entry, using the pattern matching
1517 strings given in the saslregexp config file directive(s) */
1519 static int slap_authz_regexp( struct berval *in, struct berval *out,
1520 int flags, void *ctx )
1522 #ifdef SLAP_AUTH_REWRITE
1523 const char *context = AUTHID_CONTEXT;
1525 if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
1529 /* FIXME: if aware of authc/authz mapping,
1530 * we could use different contexts ... */
1531 switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL,
1534 case REWRITE_REGEXEC_OK:
1535 if ( !BER_BVISNULL( out ) ) {
1536 char *val = out->bv_val;
1537 ber_str2bv_x( val, 0, 1, out, ctx );
1538 if ( val != in->bv_val ) {
1542 ber_dupbv_x( out, in, ctx );
1544 Debug( LDAP_DEBUG_ARGS,
1545 "[rw] %s: \"%s\" -> \"%s\"\n",
1546 context, in->bv_val, out->bv_val );
1549 case REWRITE_REGEXEC_UNWILLING:
1550 case REWRITE_REGEXEC_ERR:
1555 #else /* ! SLAP_AUTH_REWRITE */
1556 char *saslname = in->bv_val;
1558 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
1561 memset( out, 0, sizeof( *out ) );
1563 Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
1566 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
1570 /* Match the normalized SASL name to the saslregexp patterns */
1571 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
1572 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE,
1573 sr_strings, 0) == 0 )
1577 if( i >= nSaslRegexp ) return( 0 );
1580 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
1581 * replace pattern of the form "x$1y$2z". The returned string needs
1582 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
1584 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
1585 sr_strings, saslname, out, ctx );
1587 Debug( LDAP_DEBUG_TRACE,
1588 "slap_authz_regexp: converted SASL name to %s\n",
1589 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
1592 #endif /* ! SLAP_AUTH_REWRITE */
1595 /* This callback actually does some work...*/
1596 static int sasl_sc_sasl2dn( Operation *op, SlapReply *rs )
1598 struct berval *ndn = op->o_callback->sc_private;
1600 if ( rs->sr_type != REP_SEARCH ) return LDAP_SUCCESS;
1602 /* We only want to be called once */
1603 if ( !BER_BVISNULL( ndn ) ) {
1604 op->o_tmpfree( ndn->bv_val, op->o_tmpmemctx );
1607 Debug( LDAP_DEBUG_TRACE,
1608 "%s: slap_sc_sasl2dn: search DN returned more than 1 entry\n",
1609 op->o_log_prefix, 0, 0 );
1610 return LDAP_UNAVAILABLE; /* short-circuit the search */
1613 ber_dupbv_x( ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
1614 return LDAP_SUCCESS;
1618 typedef struct smatch_info {
1623 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
1625 smatch_info *sm = o->o_callback->sc_private;
1627 if (rs->sr_type != REP_SEARCH) return 0;
1629 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
1631 return LDAP_UNAVAILABLE; /* short-circuit the search */
1638 slap_sasl_matches( Operation *op, BerVarray rules,
1639 struct berval *assertDN, struct berval *authc )
1641 int rc = LDAP_INAPPROPRIATE_AUTH;
1643 if ( rules != NULL ) {
1646 for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
1647 rc = slap_sasl_match( op, &rules[i], assertDN, authc );
1648 if ( rc == LDAP_SUCCESS ) break;
1656 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
1657 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
1658 * the rule must be used as an internal search for entries. If that search
1659 * returns the *assertDN entry, the match is successful.
1661 * The assertDN should not have the dn: prefix
1665 slap_sasl_match( Operation *opx, struct berval *rule,
1666 struct berval *assertDN, struct berval *authc )
1671 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
1673 SlapReply rs = {REP_RESULT};
1674 struct berval base = BER_BVNULL;
1678 cb.sc_private = &sm;
1680 Debug( LDAP_DEBUG_TRACE,
1681 "===>slap_sasl_match: comparing DN %s to rule %s\n",
1682 assertDN->bv_val, rule->bv_val, 0 );
1684 /* NOTE: don't normalize rule if authz syntax is enabled */
1685 rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn,
1686 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 0 );
1688 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
1690 switch ( op.ors_scope ) {
1691 case LDAP_X_SCOPE_EXACT:
1693 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
1696 rc = LDAP_INAPPROPRIATE_AUTH;
1700 case LDAP_X_SCOPE_CHILDREN:
1701 case LDAP_X_SCOPE_SUBTREE:
1702 case LDAP_X_SCOPE_ONELEVEL:
1704 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
1706 rc = LDAP_INAPPROPRIATE_AUTH;
1708 if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
1711 } else if ( d > 0 ) {
1714 /* leave room for at least one char of attributeType,
1715 * one for '=' and one for ',' */
1716 if ( d < STRLENOF( "x=,") ) {
1720 bv.bv_len = op.o_req_ndn.bv_len;
1721 bv.bv_val = assertDN->bv_val + d;
1723 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
1724 switch ( op.ors_scope ) {
1725 case LDAP_X_SCOPE_SUBTREE:
1726 case LDAP_X_SCOPE_CHILDREN:
1730 case LDAP_X_SCOPE_ONELEVEL:
1734 dnParent( assertDN, &pdn );
1735 /* the common portion of the DN
1736 * already matches, so only check
1737 * if parent DN of assertedDN
1738 * is all the pattern */
1739 if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
1745 /* at present, impossible */
1753 case LDAP_X_SCOPE_REGEX:
1754 rc = regcomp(®, op.o_req_ndn.bv_val,
1755 REG_EXTENDED|REG_ICASE|REG_NOSUB);
1757 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
1763 rc = LDAP_INAPPROPRIATE_AUTH;
1767 case LDAP_X_SCOPE_GROUP: {
1770 /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
1771 * we need to append the <assertDN> so that the <group_dn> is searched
1772 * with scope "base", and the filter ensures that <assertDN> is
1773 * member of the group */
1774 tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len +
1775 assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 );
1776 if ( tmp == NULL ) {
1777 rc = LDAP_NO_MEMORY;
1780 op.ors_filterstr.bv_val = tmp;
1782 tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
1783 tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
1785 /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
1786 op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
1787 if ( op.ors_filter == NULL ) {
1788 rc = LDAP_PROTOCOL_ERROR;
1791 op.ors_scope = LDAP_SCOPE_BASE;
1793 /* hijack match DN: use that of the group instead of the assertDN;
1794 * assertDN is now in the filter */
1795 sm.dn = &op.o_req_ndn;
1801 case LDAP_X_SCOPE_USERS:
1802 if ( !BER_BVISEMPTY( assertDN ) ) {
1805 rc = LDAP_INAPPROPRIATE_AUTH;
1813 /* Must run an internal search. */
1814 if ( op.ors_filter == NULL ) {
1815 rc = LDAP_FILTER_ERROR;
1819 Debug( LDAP_DEBUG_TRACE,
1820 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
1821 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1823 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1824 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1825 rc = LDAP_INAPPROPRIATE_AUTH;
1829 op.o_hdr = opx->o_hdr;
1830 op.o_tag = LDAP_REQ_SEARCH;
1832 op.o_callback = &cb;
1833 slap_op_time( &op.o_time, &op.o_tincr );
1834 op.o_do_not_cache = 1;
1835 op.o_is_auth_check = 1;
1836 /* use req_ndn as req_dn instead of non-pretty base of uri */
1837 if( !BER_BVISNULL( &base ) ) {
1838 ch_free( base.bv_val );
1839 /* just in case... */
1840 BER_BVZERO( &base );
1842 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1843 op.ors_deref = LDAP_DEREF_NEVER;
1845 op.ors_tlimit = SLAP_NO_LIMIT;
1846 op.ors_attrs = slap_anlist_no_attrs;
1847 op.ors_attrsonly = 1;
1849 op.o_bd->be_search( &op, &rs );
1854 rc = LDAP_INAPPROPRIATE_AUTH;
1858 if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1859 if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1860 if( op.ors_filter ) filter_free_x( opx, op.ors_filter );
1861 if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1863 Debug( LDAP_DEBUG_TRACE,
1864 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
1871 * This function answers the question, "Can this ID authorize to that ID?",
1872 * based on authorization rules. The rules are stored in the *searchDN, in the
1873 * attribute named by *attr. If any of those rules map to the *assertDN, the
1874 * authorization is approved.
1876 * The DNs should not have the dn: prefix
1879 slap_sasl_check_authz( Operation *op,
1880 struct berval *searchDN,
1881 struct berval *assertDN,
1882 AttributeDescription *ad,
1883 struct berval *authc )
1886 do_not_cache = op->o_do_not_cache;
1887 BerVarray vals = NULL;
1889 Debug( LDAP_DEBUG_TRACE,
1890 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
1891 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1893 /* ITS#4760: don't cache group access */
1894 op->o_do_not_cache = 1;
1895 rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
1896 op->o_do_not_cache = do_not_cache;
1897 if( rc != LDAP_SUCCESS ) goto COMPLETE;
1899 /* Check if the *assertDN matches any *vals */
1900 rc = slap_sasl_matches( op, vals, assertDN, authc );
1903 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1905 Debug( LDAP_DEBUG_TRACE,
1906 "<==slap_sasl_check_authz: %s check returning %d\n",
1907 ad->ad_cname.bv_val, rc, 0);
1913 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
1914 * return the LDAP DN to which it matches. The SASL regexp rules in the config
1915 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
1916 * search with scope=base), just return the URI (or its searchbase). Otherwise
1917 * an internal search must be done, and if that search returns exactly one
1918 * entry, return the DN of that one entry.
1923 struct berval *saslname,
1924 struct berval *sasldn,
1928 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
1930 SlapReply rs = {REP_RESULT};
1931 struct berval regout = BER_BVNULL;
1932 struct berval base = BER_BVNULL;
1934 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1935 "converting SASL name %s to a DN\n",
1936 saslname->bv_val, 0,0 );
1938 BER_BVZERO( sasldn );
1939 cb.sc_private = sasldn;
1941 /* Convert the SASL name into a minimal URI */
1942 if( !slap_authz_regexp( saslname, ®out, flags, opx->o_tmpmemctx ) ) {
1946 /* NOTE: always normalize regout because it results
1947 * from string submatch expansion */
1948 rc = slap_parseURI( opx, ®out, &base, &op.o_req_ndn,
1949 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 );
1950 if ( !BER_BVISNULL( ®out ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
1951 if ( rc != LDAP_SUCCESS ) {
1955 /* Must do an internal search */
1956 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1958 switch ( op.ors_scope ) {
1959 case LDAP_X_SCOPE_EXACT:
1960 *sasldn = op.o_req_ndn;
1961 BER_BVZERO( &op.o_req_ndn );
1962 /* intentionally continue to next case */
1964 case LDAP_X_SCOPE_REGEX:
1965 case LDAP_X_SCOPE_SUBTREE:
1966 case LDAP_X_SCOPE_CHILDREN:
1967 case LDAP_X_SCOPE_ONELEVEL:
1968 case LDAP_X_SCOPE_GROUP:
1969 case LDAP_X_SCOPE_USERS:
1970 /* correctly parsed, but illegal */
1973 case LDAP_SCOPE_BASE:
1974 case LDAP_SCOPE_ONELEVEL:
1975 case LDAP_SCOPE_SUBTREE:
1976 case LDAP_SCOPE_SUBORDINATE:
1981 /* catch unhandled cases (there shouldn't be) */
1985 Debug( LDAP_DEBUG_TRACE,
1986 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1987 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1989 if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
1993 /* Must run an internal search. */
1994 if ( op.ors_filter == NULL ) {
1995 rc = LDAP_FILTER_ERROR;
1999 op.o_hdr = opx->o_hdr;
2000 op.o_tag = LDAP_REQ_SEARCH;
2001 op.o_ndn = opx->o_conn->c_ndn;
2002 op.o_callback = &cb;
2003 slap_op_time( &op.o_time, &op.o_tincr );
2004 op.o_do_not_cache = 1;
2005 op.o_is_auth_check = 1;
2006 op.ors_deref = LDAP_DEREF_NEVER;
2008 op.ors_tlimit = SLAP_NO_LIMIT;
2009 op.ors_attrs = slap_anlist_no_attrs;
2010 op.ors_attrsonly = 1;
2011 /* use req_ndn as req_dn instead of non-pretty base of uri */
2012 if( !BER_BVISNULL( &base ) ) {
2013 ch_free( base.bv_val );
2014 /* just in case... */
2015 BER_BVZERO( &base );
2017 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
2019 op.o_bd->be_search( &op, &rs );
2022 if( !BER_BVISEMPTY( sasldn ) ) {
2023 opx->o_conn->c_authz_backend = op.o_bd;
2025 if( !BER_BVISNULL( &op.o_req_dn ) ) {
2026 slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
2028 if( !BER_BVISNULL( &op.o_req_ndn ) ) {
2029 slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
2031 if( op.ors_filter ) {
2032 filter_free_x( opx, op.ors_filter );
2034 if( !BER_BVISNULL( &op.ors_filterstr ) ) {
2035 ch_free( op.ors_filterstr.bv_val );
2038 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
2039 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
2045 /* Check if a bind can SASL authorize to another identity.
2046 * The DNs should not have the dn: prefix
2049 int slap_sasl_authorized( Operation *op,
2050 struct berval *authcDN, struct berval *authzDN )
2052 int rc = LDAP_INAPPROPRIATE_AUTH;
2054 /* User binding as anonymous */
2055 if ( authzDN == NULL ) {
2060 Debug( LDAP_DEBUG_TRACE,
2061 "==>slap_sasl_authorized: can %s become %s?\n",
2062 authcDN->bv_len ? authcDN->bv_val : "(null)",
2063 authzDN->bv_len ? authzDN->bv_val : "(null)", 0 );
2065 /* If person is authorizing to self, succeed */
2066 if ( dn_match( authcDN, authzDN ) ) {
2071 /* Allow the manager to authorize as any DN. */
2072 if( op->o_conn->c_authz_backend &&
2073 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
2079 /* Check source rules */
2080 if( authz_policy & SASL_AUTHZ_TO ) {
2081 rc = slap_sasl_check_authz( op, authcDN, authzDN,
2082 slap_schema.si_ad_saslAuthzTo, authcDN );
2083 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
2088 /* Check destination rules */
2089 if( authz_policy & SASL_AUTHZ_FROM ) {
2090 rc = slap_sasl_check_authz( op, authzDN, authcDN,
2091 slap_schema.si_ad_saslAuthzFrom, authcDN );
2092 if( rc == LDAP_SUCCESS ) {
2097 rc = LDAP_INAPPROPRIATE_AUTH;
2101 Debug( LDAP_DEBUG_TRACE,
2102 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );