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 *op, SlapReply *rs )
1575 struct berval *ndn = op->o_callback->sc_private;
1577 if ( rs->sr_type != REP_SEARCH ) return LDAP_SUCCESS;
1579 /* We only want to be called once */
1580 if ( !BER_BVISNULL( ndn ) ) {
1581 op->o_tmpfree( ndn->bv_val, op->o_tmpmemctx );
1584 Debug( LDAP_DEBUG_TRACE,
1585 "%s: slap_sc_sasl2dn: search DN returned more than 1 entry\n",
1586 op->o_log_prefix, 0, 0 );
1590 ber_dupbv_x( ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
1591 return LDAP_SUCCESS;
1595 typedef struct smatch_info {
1600 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
1602 smatch_info *sm = o->o_callback->sc_private;
1604 if ( rs->sr_type != REP_SEARCH ) {
1605 if ( rs->sr_err != LDAP_SUCCESS ) {
1611 if ( sm->match == 1 ) {
1616 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
1627 slap_sasl_matches( Operation *op, BerVarray rules,
1628 struct berval *assertDN, struct berval *authc )
1630 int rc = LDAP_INAPPROPRIATE_AUTH;
1632 if ( rules != NULL ) {
1635 for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
1636 rc = slap_sasl_match( op, &rules[i], assertDN, authc );
1637 if ( rc == LDAP_SUCCESS ) break;
1645 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
1646 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
1647 * the rule must be used as an internal search for entries. If that search
1648 * returns the *assertDN entry, the match is successful.
1650 * The assertDN should not have the dn: prefix
1654 slap_sasl_match( Operation *opx, struct berval *rule,
1655 struct berval *assertDN, struct berval *authc )
1660 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
1662 SlapReply rs = {REP_RESULT};
1663 struct berval base = BER_BVNULL;
1667 cb.sc_private = &sm;
1669 Debug( LDAP_DEBUG_TRACE,
1670 "===>slap_sasl_match: comparing DN %s to rule %s\n",
1671 assertDN->bv_val, rule->bv_val, 0 );
1673 /* NOTE: don't normalize rule if authz syntax is enabled */
1674 rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn,
1675 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 0 );
1677 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
1679 switch ( op.ors_scope ) {
1680 case LDAP_X_SCOPE_EXACT:
1682 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
1685 rc = LDAP_INAPPROPRIATE_AUTH;
1689 case LDAP_X_SCOPE_CHILDREN:
1690 case LDAP_X_SCOPE_SUBTREE:
1691 case LDAP_X_SCOPE_ONELEVEL:
1693 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
1695 rc = LDAP_INAPPROPRIATE_AUTH;
1697 if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
1700 } else if ( d > 0 ) {
1703 /* leave room for at least one char of attributeType,
1704 * one for '=' and one for ',' */
1705 if ( d < STRLENOF( "x=,") ) {
1709 bv.bv_len = op.o_req_ndn.bv_len;
1710 bv.bv_val = assertDN->bv_val + d;
1712 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
1713 switch ( op.ors_scope ) {
1714 case LDAP_X_SCOPE_SUBTREE:
1715 case LDAP_X_SCOPE_CHILDREN:
1719 case LDAP_X_SCOPE_ONELEVEL:
1723 dnParent( assertDN, &pdn );
1724 /* the common portion of the DN
1725 * already matches, so only check
1726 * if parent DN of assertedDN
1727 * is all the pattern */
1728 if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
1734 /* at present, impossible */
1742 case LDAP_X_SCOPE_REGEX:
1743 rc = regcomp(®, op.o_req_ndn.bv_val,
1744 REG_EXTENDED|REG_ICASE|REG_NOSUB);
1746 rc = regexec(®, assertDN->bv_val, 0, NULL, 0);
1752 rc = LDAP_INAPPROPRIATE_AUTH;
1756 case LDAP_X_SCOPE_GROUP: {
1759 /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
1760 * we need to append the <assertDN> so that the <group_dn> is searched
1761 * with scope "base", and the filter ensures that <assertDN> is
1762 * member of the group */
1763 tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len +
1764 assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 );
1765 if ( tmp == NULL ) {
1766 rc = LDAP_NO_MEMORY;
1769 op.ors_filterstr.bv_val = tmp;
1771 tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
1772 tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
1774 /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
1775 op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
1776 if ( op.ors_filter == NULL ) {
1777 rc = LDAP_PROTOCOL_ERROR;
1780 op.ors_scope = LDAP_SCOPE_BASE;
1782 /* hijack match DN: use that of the group instead of the assertDN;
1783 * assertDN is now in the filter */
1784 sm.dn = &op.o_req_ndn;
1790 case LDAP_X_SCOPE_USERS:
1791 if ( !BER_BVISEMPTY( assertDN ) ) {
1794 rc = LDAP_INAPPROPRIATE_AUTH;
1802 /* Must run an internal search. */
1803 if ( op.ors_filter == NULL ) {
1804 rc = LDAP_FILTER_ERROR;
1808 Debug( LDAP_DEBUG_TRACE,
1809 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
1810 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1812 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1813 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1814 rc = LDAP_INAPPROPRIATE_AUTH;
1818 op.o_hdr = opx->o_hdr;
1819 op.o_tag = LDAP_REQ_SEARCH;
1821 op.o_callback = &cb;
1822 slap_op_time( &op.o_time, &op.o_tincr );
1823 op.o_do_not_cache = 1;
1824 op.o_is_auth_check = 1;
1825 /* use req_ndn as req_dn instead of non-pretty base of uri */
1826 if( !BER_BVISNULL( &base ) ) {
1827 ch_free( base.bv_val );
1828 /* just in case... */
1829 BER_BVZERO( &base );
1831 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1832 op.ors_deref = LDAP_DEREF_NEVER;
1834 op.ors_tlimit = SLAP_NO_LIMIT;
1835 op.ors_attrs = slap_anlist_no_attrs;
1836 op.ors_attrsonly = 1;
1838 op.o_bd->be_search( &op, &rs );
1840 if (sm.match == 1) {
1843 rc = LDAP_INAPPROPRIATE_AUTH;
1847 if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1848 if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1849 if( op.ors_filter ) filter_free_x( opx, op.ors_filter );
1850 if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1852 Debug( LDAP_DEBUG_TRACE,
1853 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
1860 * This function answers the question, "Can this ID authorize to that ID?",
1861 * based on authorization rules. The rules are stored in the *searchDN, in the
1862 * attribute named by *attr. If any of those rules map to the *assertDN, the
1863 * authorization is approved.
1865 * The DNs should not have the dn: prefix
1868 slap_sasl_check_authz( Operation *op,
1869 struct berval *searchDN,
1870 struct berval *assertDN,
1871 AttributeDescription *ad,
1872 struct berval *authc )
1875 BerVarray vals = NULL;
1877 Debug( LDAP_DEBUG_TRACE,
1878 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
1879 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1881 rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
1882 if( rc != LDAP_SUCCESS ) goto COMPLETE;
1884 /* Check if the *assertDN matches any *vals */
1885 rc = slap_sasl_matches( op, vals, assertDN, authc );
1888 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1890 Debug( LDAP_DEBUG_TRACE,
1891 "<==slap_sasl_check_authz: %s check returning %d\n",
1892 ad->ad_cname.bv_val, rc, 0);
1898 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
1899 * return the LDAP DN to which it matches. The SASL regexp rules in the config
1900 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
1901 * search with scope=base), just return the URI (or its searchbase). Otherwise
1902 * an internal search must be done, and if that search returns exactly one
1903 * entry, return the DN of that one entry.
1908 struct berval *saslname,
1909 struct berval *sasldn,
1913 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
1915 SlapReply rs = {REP_RESULT};
1916 struct berval regout = BER_BVNULL;
1917 struct berval base = BER_BVNULL;
1919 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1920 "converting SASL name %s to a DN\n",
1921 saslname->bv_val, 0,0 );
1923 BER_BVZERO( sasldn );
1924 cb.sc_private = sasldn;
1926 /* Convert the SASL name into a minimal URI */
1927 if( !slap_authz_regexp( saslname, ®out, flags, opx->o_tmpmemctx ) ) {
1931 /* NOTE: always normalize regout because it results
1932 * from string submatch expansion */
1933 rc = slap_parseURI( opx, ®out, &base, &op.o_req_ndn,
1934 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 );
1935 if ( !BER_BVISNULL( ®out ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
1936 if ( rc != LDAP_SUCCESS ) {
1940 /* Must do an internal search */
1941 op.o_bd = select_backend( &op.o_req_ndn, 0, 1 );
1943 switch ( op.ors_scope ) {
1944 case LDAP_X_SCOPE_EXACT:
1945 *sasldn = op.o_req_ndn;
1946 BER_BVZERO( &op.o_req_ndn );
1947 /* intentionally continue to next case */
1949 case LDAP_X_SCOPE_REGEX:
1950 case LDAP_X_SCOPE_SUBTREE:
1951 case LDAP_X_SCOPE_CHILDREN:
1952 case LDAP_X_SCOPE_ONELEVEL:
1953 case LDAP_X_SCOPE_GROUP:
1954 case LDAP_X_SCOPE_USERS:
1955 /* correctly parsed, but illegal */
1958 case LDAP_SCOPE_BASE:
1959 case LDAP_SCOPE_ONELEVEL:
1960 case LDAP_SCOPE_SUBTREE:
1961 case LDAP_SCOPE_SUBORDINATE:
1966 /* catch unhandled cases (there shouldn't be) */
1970 Debug( LDAP_DEBUG_TRACE,
1971 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1972 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1974 if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
1978 /* Must run an internal search. */
1979 if ( op.ors_filter == NULL ) {
1980 rc = LDAP_FILTER_ERROR;
1984 op.o_hdr = opx->o_hdr;
1985 op.o_tag = LDAP_REQ_SEARCH;
1986 op.o_ndn = opx->o_conn->c_ndn;
1987 op.o_callback = &cb;
1988 slap_op_time( &op.o_time, &op.o_tincr );
1989 op.o_do_not_cache = 1;
1990 op.o_is_auth_check = 1;
1991 op.ors_deref = LDAP_DEREF_NEVER;
1993 op.ors_tlimit = SLAP_NO_LIMIT;
1994 op.ors_attrs = slap_anlist_no_attrs;
1995 op.ors_attrsonly = 1;
1996 /* use req_ndn as req_dn instead of non-pretty base of uri */
1997 if( !BER_BVISNULL( &base ) ) {
1998 ch_free( base.bv_val );
1999 /* just in case... */
2000 BER_BVZERO( &base );
2002 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
2004 op.o_bd->be_search( &op, &rs );
2007 if( !BER_BVISEMPTY( sasldn ) ) {
2008 opx->o_conn->c_authz_backend = op.o_bd;
2010 if( !BER_BVISNULL( &op.o_req_dn ) ) {
2011 slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
2013 if( !BER_BVISNULL( &op.o_req_ndn ) ) {
2014 slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
2016 if( op.ors_filter ) {
2017 filter_free_x( opx, op.ors_filter );
2019 if( !BER_BVISNULL( &op.ors_filterstr ) ) {
2020 ch_free( op.ors_filterstr.bv_val );
2023 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
2024 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
2030 /* Check if a bind can SASL authorize to another identity.
2031 * The DNs should not have the dn: prefix
2034 int slap_sasl_authorized( Operation *op,
2035 struct berval *authcDN, struct berval *authzDN )
2037 int rc = LDAP_INAPPROPRIATE_AUTH;
2039 /* User binding as anonymous */
2040 if ( authzDN == NULL ) {
2045 Debug( LDAP_DEBUG_TRACE,
2046 "==>slap_sasl_authorized: can %s become %s?\n",
2047 authcDN->bv_len ? authcDN->bv_val : "(null)",
2048 authzDN->bv_len ? authzDN->bv_val : "(null)", 0 );
2050 /* If person is authorizing to self, succeed */
2051 if ( dn_match( authcDN, authzDN ) ) {
2056 /* Allow the manager to authorize as any DN. */
2057 if( op->o_conn->c_authz_backend &&
2058 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
2064 /* Check source rules */
2065 if( authz_policy & SASL_AUTHZ_TO ) {
2066 rc = slap_sasl_check_authz( op, authcDN, authzDN,
2067 slap_schema.si_ad_saslAuthzTo, authcDN );
2068 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
2073 /* Check destination rules */
2074 if( authz_policy & SASL_AUTHZ_FROM ) {
2075 rc = slap_sasl_check_authz( op, authzDN, authcDN,
2076 slap_schema.si_ad_saslAuthzFrom, authcDN );
2077 if( rc == LDAP_SUCCESS ) {
2082 rc = LDAP_INAPPROPRIATE_AUTH;
2086 Debug( LDAP_DEBUG_TRACE,
2087 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );