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 );
1316 int slap_sasl_rewrite_destroy( void )
1318 if ( sasl_rwinfo ) {
1319 rewrite_info_delete( &sasl_rwinfo );
1326 int slap_sasl_regexp_rewrite_config(
1330 const char *replace,
1331 const char *context )
1334 char *argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL };
1336 /* init at first call */
1337 if ( sasl_rwinfo == NULL ) {
1338 char *argvEngine[] = { "rewriteEngine", "on", NULL };
1339 char *argvContext[] = { "rewriteContext", NULL, NULL };
1341 /* initialize rewrite engine */
1342 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1344 /* switch on rewrite engine */
1345 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine );
1346 if (rc != LDAP_SUCCESS) {
1350 /* create generic authid context */
1351 argvContext[1] = AUTHID_CONTEXT;
1352 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvContext );
1353 if (rc != LDAP_SUCCESS) {
1358 argvRule[1] = (char *)match;
1359 argvRule[2] = (char *)replace;
1360 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule );
1364 #endif /* SLAP_AUTH_REWRITE */
1366 int slap_sasl_regexp_config( const char *match, const char *replace )
1371 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
1372 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
1374 reg = &SaslRegexp[nSaslRegexp];
1376 reg->sr_match = ch_strdup( match );
1377 reg->sr_replace = ch_strdup( replace );
1379 #ifdef SLAP_AUTH_REWRITE
1380 rc = slap_sasl_regexp_rewrite_config( "sasl-regexp", 0,
1381 match, replace, AUTHID_CONTEXT );
1382 if ( rc == LDAP_SUCCESS ) nSaslRegexp++;
1384 #else /* ! SLAP_AUTH_REWRITE */
1386 /* Precompile matching pattern */
1387 rc = regcomp( ®->sr_workspace, reg->sr_match, REG_EXTENDED|REG_ICASE );
1389 Debug( LDAP_DEBUG_ANY,
1390 "SASL match pattern %s could not be compiled by regexp engine\n",
1391 reg->sr_match, 0, 0 );
1393 #ifdef ENABLE_REWRITE
1394 /* Dummy block to force symbol references in librewrite */
1395 if ( slapMode == ( SLAP_SERVER_MODE|SLAP_TOOL_MODE )) {
1396 rewrite_info_init( 0 );
1399 return( LDAP_OTHER );
1402 rc = slap_sasl_rx_off( reg->sr_replace, reg->sr_offset );
1403 if ( rc != LDAP_SUCCESS ) return rc;
1406 return( LDAP_SUCCESS );
1407 #endif /* ! SLAP_AUTH_REWRITE */
1410 void slap_sasl_regexp_unparse( BerVarray *out )
1413 BerVarray bva = NULL;
1414 char ibuf[32], *ptr;
1417 if ( !nSaslRegexp ) return;
1420 bva = ch_malloc( (nSaslRegexp+1) * sizeof(struct berval) );
1421 BER_BVZERO(bva+nSaslRegexp);
1422 for ( i=0; i<nSaslRegexp; i++ ) {
1423 idx.bv_len = sprintf( idx.bv_val, "{%d}", i);
1424 bva[i].bv_len = idx.bv_len + strlen( SaslRegexp[i].sr_match ) +
1425 strlen( SaslRegexp[i].sr_replace ) + 5;
1426 bva[i].bv_val = ch_malloc( bva[i].bv_len+1 );
1427 ptr = lutil_strcopy( bva[i].bv_val, ibuf );
1429 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match );
1430 ptr = lutil_strcopy( ptr, "\" \"" );
1431 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace );
1438 #ifndef SLAP_AUTH_REWRITE
1439 /* Perform replacement on regexp matches */
1440 static void slap_sasl_rx_exp(
1444 const char *saslname,
1448 int i, n, len, insert;
1450 /* Get the total length of the final URI */
1454 while( off[n] >= 0 ) {
1455 /* Len of next section from replacement string (x,y,z above) */
1456 len += off[n] - off[n-1] - 2;
1460 /* Len of string from saslname that matched next $i (b,d above) */
1461 i = rep[ off[n] + 1 ] - '0';
1462 len += str[i].rm_eo - str[i].rm_so;
1465 out->bv_val = slap_sl_malloc( len + 1, ctx );
1468 /* Fill in URI with replace string, replacing $i as we go */
1471 while( off[n] >= 0) {
1472 /* Paste in next section from replacement string (x,y,z above) */
1473 len = off[n] - off[n-1] - 2;
1474 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
1479 /* Paste in string from saslname that matched next $i (b,d above) */
1480 i = rep[ off[n] + 1 ] - '0';
1481 len = str[i].rm_eo - str[i].rm_so;
1482 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
1488 out->bv_val[insert] = '\0';
1490 #endif /* ! SLAP_AUTH_REWRITE */
1492 /* Take the passed in SASL name and attempt to convert it into an
1493 LDAP URI to find the matching LDAP entry, using the pattern matching
1494 strings given in the saslregexp config file directive(s) */
1496 static int slap_authz_regexp( struct berval *in, struct berval *out,
1497 int flags, void *ctx )
1499 #ifdef SLAP_AUTH_REWRITE
1500 const char *context = AUTHID_CONTEXT;
1502 if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
1506 /* FIXME: if aware of authc/authz mapping,
1507 * we could use different contexts ... */
1508 switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL,
1511 case REWRITE_REGEXEC_OK:
1512 if ( !BER_BVISNULL( out ) ) {
1513 char *val = out->bv_val;
1514 ber_str2bv_x( val, 0, 1, out, ctx );
1515 if ( val != in->bv_val ) {
1519 ber_dupbv_x( out, in, ctx );
1521 Debug( LDAP_DEBUG_ARGS,
1522 "[rw] %s: \"%s\" -> \"%s\"\n",
1523 context, in->bv_val, out->bv_val );
1526 case REWRITE_REGEXEC_UNWILLING:
1527 case REWRITE_REGEXEC_ERR:
1532 #else /* ! SLAP_AUTH_REWRITE */
1533 char *saslname = in->bv_val;
1535 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
1538 memset( out, 0, sizeof( *out ) );
1540 Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
1543 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
1547 /* Match the normalized SASL name to the saslregexp patterns */
1548 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
1549 if ( regexec( ®->sr_workspace, saslname, SASLREGEX_REPLACE,
1550 sr_strings, 0) == 0 )
1554 if( i >= nSaslRegexp ) return( 0 );
1557 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
1558 * replace pattern of the form "x$1y$2z". The returned string needs
1559 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
1561 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
1562 sr_strings, saslname, out, ctx );
1564 Debug( LDAP_DEBUG_TRACE,
1565 "slap_authz_regexp: converted SASL name to %s\n",
1566 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
1569 #endif /* ! SLAP_AUTH_REWRITE */
1572 /* This callback actually does some work...*/
1573 static int sasl_sc_sasl2dn( Operation *o, SlapReply *rs )
1575 struct berval *ndn = o->o_callback->sc_private;
1577 if (rs->sr_type != REP_SEARCH) return 0;
1579 /* We only want to be called once */
1580 if ( !BER_BVISNULL( ndn ) ) {
1581 o->o_tmpfree(ndn->bv_val, o->o_tmpmemctx);
1584 Debug( LDAP_DEBUG_TRACE,
1585 "slap_sc_sasl2dn: search DN returned more than 1 entry\n", 0, 0, 0 );
1589 ber_dupbv_x(ndn, &rs->sr_entry->e_nname, o->o_tmpmemctx);
1594 typedef struct smatch_info {
1599 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
1601 smatch_info *sm = o->o_callback->sc_private;
1603 if ( rs->sr_type != REP_SEARCH ) {
1604 if ( rs->sr_err != LDAP_SUCCESS ) {
1610 if ( sm->match == 1 ) {
1615 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
1626 slap_sasl_matches( Operation *op, BerVarray rules,
1627 struct berval *assertDN, struct berval *authc )
1629 int rc = LDAP_INAPPROPRIATE_AUTH;
1631 if ( rules != NULL ) {
1634 for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
1635 rc = slap_sasl_match( op, &rules[i], assertDN, authc );
1636 if ( rc == LDAP_SUCCESS ) break;
1644 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
1645 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
1646 * the rule must be used as an internal search for entries. If that search
1647 * returns the *assertDN entry, the match is successful.
1649 * The assertDN should not have the dn: prefix
1653 slap_sasl_match( Operation *opx, struct berval *rule,
1654 struct berval *assertDN, struct berval *authc )
1659 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
1661 SlapReply rs = {REP_RESULT};
1662 struct berval base = BER_BVNULL;
1666 cb.sc_private = &sm;
1668 Debug( LDAP_DEBUG_TRACE,
1669 "===>slap_sasl_match: comparing DN %s to rule %s\n",
1670 assertDN->bv_val, rule->bv_val, 0 );
1672 /* NOTE: don't normalize rule if authz syntax is enabled */
1673 rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn,
1674 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 0 );
1676 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
1678 switch ( op.ors_scope ) {
1679 case LDAP_X_SCOPE_EXACT:
1681 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
1684 rc = LDAP_INAPPROPRIATE_AUTH;
1688 case LDAP_X_SCOPE_CHILDREN:
1689 case LDAP_X_SCOPE_SUBTREE:
1690 case LDAP_X_SCOPE_ONELEVEL:
1692 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
1694 rc = LDAP_INAPPROPRIATE_AUTH;
1696 if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
1699 } else if ( d > 0 ) {
1702 /* leave room for at least one char of attributeType,
1703 * one for '=' and one for ',' */
1704 if ( d < STRLENOF( "x=,") ) {
1708 bv.bv_len = op.o_req_ndn.bv_len;
1709 bv.bv_val = assertDN->bv_val + d;
1711 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
1712 switch ( op.ors_scope ) {
1713 case LDAP_X_SCOPE_SUBTREE:
1714 case LDAP_X_SCOPE_CHILDREN:
1718 case LDAP_X_SCOPE_ONELEVEL:
1722 dnParent( assertDN, &pdn );
1723 /* the common portion of the DN
1724 * already matches, so only check
1725 * if parent DN of assertedDN
1726 * is all the pattern */
1727 if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
1733 /* at present, impossible */
1741 case LDAP_X_SCOPE_REGEX:
1742 rc = regcomp(®, op.o_req_ndn.bv_val,
1743 REG_EXTENDED|REG_ICASE|REG_NOSUB);
1745 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
1751 rc = LDAP_INAPPROPRIATE_AUTH;
1755 case LDAP_X_SCOPE_GROUP: {
1758 /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
1759 * we need to append the <assertDN> so that the <group_dn> is searched
1760 * with scope "base", and the filter ensures that <assertDN> is
1761 * member of the group */
1762 tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len +
1763 assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 );
1764 if ( tmp == NULL ) {
1765 rc = LDAP_NO_MEMORY;
1768 op.ors_filterstr.bv_val = tmp;
1770 tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
1771 tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
1773 /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
1774 op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
1775 if ( op.ors_filter == NULL ) {
1776 rc = LDAP_PROTOCOL_ERROR;
1779 op.ors_scope = LDAP_SCOPE_BASE;
1781 /* hijack match DN: use that of the group instead of the assertDN;
1782 * assertDN is now in the filter */
1783 sm.dn = &op.o_req_ndn;
1789 case LDAP_X_SCOPE_USERS:
1790 if ( !BER_BVISEMPTY( assertDN ) ) {
1793 rc = LDAP_INAPPROPRIATE_AUTH;
1801 /* Must run an internal search. */
1802 if ( op.ors_filter == NULL ) {
1803 rc = LDAP_FILTER_ERROR;
1807 Debug( LDAP_DEBUG_TRACE,
1808 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
1809 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1811 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1812 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1813 rc = LDAP_INAPPROPRIATE_AUTH;
1817 op.o_hdr = opx->o_hdr;
1818 op.o_tag = LDAP_REQ_SEARCH;
1820 op.o_callback = &cb;
1821 slap_op_time( &op.o_time, &op.o_tincr );
1822 op.o_do_not_cache = 1;
1823 op.o_is_auth_check = 1;
1824 /* use req_ndn as req_dn instead of non-pretty base of uri */
1825 if( !BER_BVISNULL( &base ) ) {
1826 ch_free( base.bv_val );
1827 /* just in case... */
1828 BER_BVZERO( &base );
1830 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1831 op.ors_deref = LDAP_DEREF_NEVER;
1833 op.ors_tlimit = SLAP_NO_LIMIT;
1834 op.ors_attrs = slap_anlist_no_attrs;
1835 op.ors_attrsonly = 1;
1837 op.o_bd->be_search( &op, &rs );
1839 if (sm.match == 1) {
1842 rc = LDAP_INAPPROPRIATE_AUTH;
1846 if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1847 if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1848 if( op.ors_filter ) filter_free_x( opx, op.ors_filter );
1849 if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1851 Debug( LDAP_DEBUG_TRACE,
1852 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
1859 * This function answers the question, "Can this ID authorize to that ID?",
1860 * based on authorization rules. The rules are stored in the *searchDN, in the
1861 * attribute named by *attr. If any of those rules map to the *assertDN, the
1862 * authorization is approved.
1864 * The DNs should not have the dn: prefix
1867 slap_sasl_check_authz( Operation *op,
1868 struct berval *searchDN,
1869 struct berval *assertDN,
1870 AttributeDescription *ad,
1871 struct berval *authc )
1874 BerVarray vals = NULL;
1876 Debug( LDAP_DEBUG_TRACE,
1877 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
1878 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1880 rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
1881 if( rc != LDAP_SUCCESS ) goto COMPLETE;
1883 /* Check if the *assertDN matches any *vals */
1884 rc = slap_sasl_matches( op, vals, assertDN, authc );
1887 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1889 Debug( LDAP_DEBUG_TRACE,
1890 "<==slap_sasl_check_authz: %s check returning %d\n",
1891 ad->ad_cname.bv_val, rc, 0);
1897 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
1898 * return the LDAP DN to which it matches. The SASL regexp rules in the config
1899 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
1900 * search with scope=base), just return the URI (or its searchbase). Otherwise
1901 * an internal search must be done, and if that search returns exactly one
1902 * entry, return the DN of that one entry.
1907 struct berval *saslname,
1908 struct berval *sasldn,
1912 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
1914 SlapReply rs = {REP_RESULT};
1915 struct berval regout = BER_BVNULL;
1916 struct berval base = BER_BVNULL;
1918 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1919 "converting SASL name %s to a DN\n",
1920 saslname->bv_val, 0,0 );
1922 BER_BVZERO( sasldn );
1923 cb.sc_private = sasldn;
1925 /* Convert the SASL name into a minimal URI */
1926 if( !slap_authz_regexp( saslname, ®out, flags, opx->o_tmpmemctx ) ) {
1930 /* NOTE: always normalize regout because it results
1931 * from string submatch expansion */
1932 rc = slap_parseURI( opx, ®out, &base, &op.o_req_ndn,
1933 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 );
1934 if ( !BER_BVISNULL( ®out ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
1935 if ( rc != LDAP_SUCCESS ) {
1939 /* Must do an internal search */
1940 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1942 switch ( op.ors_scope ) {
1943 case LDAP_X_SCOPE_EXACT:
1944 *sasldn = op.o_req_ndn;
1945 BER_BVZERO( &op.o_req_ndn );
1946 /* intentionally continue to next case */
1948 case LDAP_X_SCOPE_REGEX:
1949 case LDAP_X_SCOPE_SUBTREE:
1950 case LDAP_X_SCOPE_CHILDREN:
1951 case LDAP_X_SCOPE_ONELEVEL:
1952 case LDAP_X_SCOPE_GROUP:
1953 case LDAP_X_SCOPE_USERS:
1954 /* correctly parsed, but illegal */
1957 case LDAP_SCOPE_BASE:
1958 case LDAP_SCOPE_ONELEVEL:
1959 case LDAP_SCOPE_SUBTREE:
1960 case LDAP_SCOPE_SUBORDINATE:
1965 /* catch unhandled cases (there shouldn't be) */
1969 Debug( LDAP_DEBUG_TRACE,
1970 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1971 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1973 if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
1977 /* Must run an internal search. */
1978 if ( op.ors_filter == NULL ) {
1979 rc = LDAP_FILTER_ERROR;
1983 op.o_hdr = opx->o_hdr;
1984 op.o_tag = LDAP_REQ_SEARCH;
1985 op.o_ndn = opx->o_conn->c_ndn;
1986 op.o_callback = &cb;
1987 slap_op_time( &op.o_time, &op.o_tincr );
1988 op.o_do_not_cache = 1;
1989 op.o_is_auth_check = 1;
1990 op.ors_deref = LDAP_DEREF_NEVER;
1992 op.ors_tlimit = SLAP_NO_LIMIT;
1993 op.ors_attrs = slap_anlist_no_attrs;
1994 op.ors_attrsonly = 1;
1995 /* use req_ndn as req_dn instead of non-pretty base of uri */
1996 if( !BER_BVISNULL( &base ) ) {
1997 ch_free( base.bv_val );
1998 /* just in case... */
1999 BER_BVZERO( &base );
2001 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
2003 op.o_bd->be_search( &op, &rs );
2006 if( !BER_BVISEMPTY( sasldn ) ) {
2007 opx->o_conn->c_authz_backend = op.o_bd;
2009 if( !BER_BVISNULL( &op.o_req_dn ) ) {
2010 slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
2012 if( !BER_BVISNULL( &op.o_req_ndn ) ) {
2013 slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
2015 if( op.ors_filter ) {
2016 filter_free_x( opx, op.ors_filter );
2018 if( !BER_BVISNULL( &op.ors_filterstr ) ) {
2019 ch_free( op.ors_filterstr.bv_val );
2022 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
2023 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
2029 /* Check if a bind can SASL authorize to another identity.
2030 * The DNs should not have the dn: prefix
2033 int slap_sasl_authorized( Operation *op,
2034 struct berval *authcDN, struct berval *authzDN )
2036 int rc = LDAP_INAPPROPRIATE_AUTH;
2038 /* User binding as anonymous */
2039 if ( authzDN == NULL ) {
2044 Debug( LDAP_DEBUG_TRACE,
2045 "==>slap_sasl_authorized: can %s become %s?\n",
2046 authcDN->bv_len ? authcDN->bv_val : "(null)",
2047 authzDN->bv_len ? authzDN->bv_val : "(null)", 0 );
2049 /* If person is authorizing to self, succeed */
2050 if ( dn_match( authcDN, authzDN ) ) {
2055 /* Allow the manager to authorize as any DN. */
2056 if( op->o_conn->c_authz_backend &&
2057 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
2063 /* Check source rules */
2064 if( authz_policy & SASL_AUTHZ_TO ) {
2065 rc = slap_sasl_check_authz( op, authcDN, authzDN,
2066 slap_schema.si_ad_saslAuthzTo, authcDN );
2067 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
2072 /* Check destination rules */
2073 if( authz_policy & SASL_AUTHZ_FROM ) {
2074 rc = slap_sasl_check_authz( op, authzDN, authcDN,
2075 slap_schema.si_ad_saslAuthzFrom, authcDN );
2076 if( rc == LDAP_SUCCESS ) {
2081 rc = LDAP_INAPPROPRIATE_AUTH;
2085 Debug( LDAP_DEBUG_TRACE,
2086 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );