Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / bsd / openldap / dist / servers / slapd / saslauthz.c
blob117285e0051fc58cea74fecf55bebb2ebb9575f4
1 /* $OpenLDAP: pkg/ldap/servers/slapd/saslauthz.c,v 1.163.2.8 2008/02/11 23:26:44 kurt Exp $ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 * Copyright 1998-2008 The OpenLDAP Foundation.
5 * Portions Copyright 2000 Mark Adamson, Carnegie Mellon.
6 * All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
10 * Public License.
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>.
17 #include "portable.h"
19 #include <stdio.h>
20 #ifdef HAVE_LIMITS_H
21 #include <limits.h>
22 #endif
24 #include <ac/stdlib.h>
25 #include <ac/string.h>
26 #include <ac/ctype.h>
28 #include "slap.h"
30 #include "lutil.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
49 * in exact DN match.
50 * dn.regex: the value is treated as a regular expression
51 * in matching DN values in authz{To|From}
52 * attributes.
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
57 * context.
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 */
74 } SaslRegexp_t;
76 static int nSaslRegexp = 0;
77 static SaslRegexp_t *SaslRegexp = NULL;
79 #ifdef SLAP_AUTH_REWRITE
80 #include "rewrite.h"
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;
97 static int
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;
115 } else {
116 rc = LDAP_OTHER;
118 return rc;
121 const char * slap_sasl_getpolicy()
123 if ( authz_policy == (SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND) )
124 return "all";
125 else
126 return policy_txt[authz_policy];
129 int slap_parse_user( struct berval *id, struct berval *user,
130 struct berval *realm, struct berval *mech )
132 char u;
134 assert( id != NULL );
135 assert( !BER_BVISNULL( id ) );
136 assert( user != NULL );
137 assert( realm != NULL );
138 assert( mech != NULL );
140 u = id->bv_val[ 0 ];
142 if ( u != 'u' && u != 'U' ) {
143 /* called with something other than u: */
144 return LDAP_PROTOCOL_ERROR;
147 /* uauthzid form:
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';
156 user->bv_val++;
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';
162 mech->bv_val++;
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';
169 realm->bv_val++;
170 mech->bv_len = realm->bv_val - mech->bv_val - 1;
171 realm->bv_len = user->bv_val - realm->bv_val - 1;
174 } else {
175 BER_BVZERO( realm );
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 );
186 mech->bv_val -= 2;
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 );
193 realm->bv_val -= 2;
196 /* leave "u:" before user */
197 user->bv_val -= 2;
198 user->bv_len += 2;
199 user->bv_val[ 0 ] = u;
200 user->bv_val[ 1 ] = ':';
202 return LDAP_SUCCESS;
206 authzValidate(
207 Syntax *syntax,
208 struct berval *in )
210 struct berval bv;
211 int rc = LDAP_INVALID_SYNTAX;
212 LDAPURLDesc *ludp = NULL;
213 int scope = -1;
216 * 1) <DN>
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>
221 * 6) <URL>
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 ] == '.' ) {
240 bv.bv_val++;
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;
262 } else {
263 return LDAP_INVALID_SYNTAX;
266 } else {
267 if ( bv.bv_val[ 0 ] != ':' ) {
268 return LDAP_INVALID_SYNTAX;
270 scope = LDAP_X_SCOPE_EXACT;
271 bv.bv_val++;
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 */
282 return LDAP_SUCCESS;
285 switch ( scope ) {
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:
293 return LDAP_SUCCESS;
296 return rc;
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 ];
307 struct berval id,
308 user = BER_BVNULL,
309 realm = BER_BVNULL,
310 mech = BER_BVNULL;
312 if ( sizeof( buf ) <= in->bv_len ) {
313 return LDAP_INVALID_SYNTAX;
316 id.bv_len = in->bv_len;
317 id.bv_val = buf;
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;
325 return rc;
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;
348 goto is_dn;
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;
365 member_at.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 ) {
369 return rc;
373 if ( oc_bvfind( &group_oc ) == NULL ) {
374 return LDAP_INVALID_SYNTAX;
378 group_dn.bv_val++;
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 ) {
383 return rc;
386 return rc;
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 );
398 switch ( rc ) {
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
405 * little parsing.
407 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
409 * must be ldap:///
411 rc = LDAP_INVALID_SYNTAX;
412 goto done;
414 break;
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;
425 goto is_dn;
427 default:
428 rc = LDAP_INVALID_SYNTAX;
429 goto done;
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;
438 goto done;
441 /* Grab the filter */
442 if ( ludp->lud_filter ) {
443 Filter *f = str2filter( ludp->lud_filter );
444 if ( f == NULL ) {
445 rc = LDAP_INVALID_SYNTAX;
446 goto done;
448 filter_free( f );
451 /* Grab the searchbase */
452 assert( ludp->lud_dn != NULL );
453 ber_str2bv( ludp->lud_dn, 0, 0, &bv );
454 rc = dnValidate( NULL, &bv );
456 done:
457 ldap_free_urldesc( ludp );
458 return( rc );
461 static int
462 authzPrettyNormal(
463 struct berval *val,
464 struct berval *normalized,
465 void *ctx,
466 int normalize )
468 struct berval bv;
469 int rc = LDAP_INVALID_SYNTAX;
470 LDAPURLDesc *ludp = NULL;
471 char *lud_dn = NULL,
472 *lud_filter = NULL;
473 int scope = -1;
476 * 1) <DN>
477 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
478 * 3) dn.regex:<pattern>
479 * 4) u[.mech[/realm]]:<ID>
480 * 5) group[/<groupClass>[/<memberAttr>]]:<DN>
481 * 6) <URL>
484 assert( val != NULL );
485 assert( !BER_BVISNULL( val ) );
488 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
489 * 3) dn.regex:<pattern>
491 * <DN> must pass DN normalization
493 if ( !strncasecmp( val->bv_val, "dn", STRLENOF( "dn" ) ) ) {
494 struct berval out = BER_BVNULL,
495 prefix = BER_BVNULL;
496 char *ptr;
498 bv.bv_val = val->bv_val + STRLENOF( "dn" );
500 if ( bv.bv_val[ 0 ] == '.' ) {
501 bv.bv_val++;
503 if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
504 bv.bv_val += STRLENOF( "exact:" );
505 scope = LDAP_X_SCOPE_EXACT;
507 } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
508 bv.bv_val += STRLENOF( "regex:" );
509 scope = LDAP_X_SCOPE_REGEX;
511 } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
512 bv.bv_val += STRLENOF( "children:" );
513 scope = LDAP_X_SCOPE_CHILDREN;
515 } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
516 bv.bv_val += STRLENOF( "subtree:" );
517 scope = LDAP_X_SCOPE_SUBTREE;
519 } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
520 bv.bv_val += STRLENOF( "onelevel:" );
521 scope = LDAP_X_SCOPE_ONELEVEL;
523 } else {
524 return LDAP_INVALID_SYNTAX;
527 } else {
528 if ( bv.bv_val[ 0 ] != ':' ) {
529 return LDAP_INVALID_SYNTAX;
531 scope = LDAP_X_SCOPE_EXACT;
532 bv.bv_val++;
535 bv.bv_val += strspn( bv.bv_val, " " );
536 /* jump here in case no type specification was present
537 * and uri was not an URI... HEADS-UP: assuming EXACT */
538 is_dn: bv.bv_len = val->bv_len - ( bv.bv_val - val->bv_val );
540 /* a single '*' means any DN without using regexes */
541 if ( ber_bvccmp( &bv, '*' ) ) {
542 ber_str2bv_x( "dn:*", STRLENOF( "dn:*" ), 1, normalized, ctx );
543 return LDAP_SUCCESS;
546 switch ( scope ) {
547 case LDAP_X_SCOPE_EXACT:
548 case LDAP_X_SCOPE_CHILDREN:
549 case LDAP_X_SCOPE_SUBTREE:
550 case LDAP_X_SCOPE_ONELEVEL:
551 if ( normalize ) {
552 rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
553 } else {
554 rc = dnPretty( NULL, &bv, &out, ctx );
556 if( rc != LDAP_SUCCESS ) {
557 return LDAP_INVALID_SYNTAX;
559 break;
561 case LDAP_X_SCOPE_REGEX:
562 normalized->bv_len = STRLENOF( "dn.regex:" ) + bv.bv_len;
563 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
564 ptr = lutil_strcopy( normalized->bv_val, "dn.regex:" );
565 ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
566 ptr[ 0 ] = '\0';
567 return LDAP_SUCCESS;
569 default:
570 return LDAP_INVALID_SYNTAX;
573 /* prepare prefix */
574 switch ( scope ) {
575 case LDAP_X_SCOPE_EXACT:
576 BER_BVSTR( &prefix, "dn:" );
577 break;
579 case LDAP_X_SCOPE_CHILDREN:
580 BER_BVSTR( &prefix, "dn.children:" );
581 break;
583 case LDAP_X_SCOPE_SUBTREE:
584 BER_BVSTR( &prefix, "dn.subtree:" );
585 break;
587 case LDAP_X_SCOPE_ONELEVEL:
588 BER_BVSTR( &prefix, "dn.onelevel:" );
589 break;
591 default:
592 assert( 0 );
593 break;
596 normalized->bv_len = prefix.bv_len + out.bv_len;
597 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
599 ptr = lutil_strcopy( normalized->bv_val, prefix.bv_val );
600 ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
601 ptr[ 0 ] = '\0';
602 ber_memfree_x( out.bv_val, ctx );
604 return LDAP_SUCCESS;
607 * 4) u[.mech[/realm]]:<ID>
609 } else if ( ( val->bv_val[ 0 ] == 'u' || val->bv_val[ 0 ] == 'U' )
610 && ( val->bv_val[ 1 ] == ':'
611 || val->bv_val[ 1 ] == '/'
612 || val->bv_val[ 1 ] == '.' ) )
614 char buf[ SLAP_LDAPDN_MAXLEN ];
615 struct berval id,
616 user = BER_BVNULL,
617 realm = BER_BVNULL,
618 mech = BER_BVNULL;
620 if ( sizeof( buf ) <= val->bv_len ) {
621 return LDAP_INVALID_SYNTAX;
624 id.bv_len = val->bv_len;
625 id.bv_val = buf;
626 strncpy( buf, val->bv_val, sizeof( buf ) );
628 rc = slap_parse_user( &id, &user, &realm, &mech );
629 if ( rc != LDAP_SUCCESS ) {
630 return LDAP_INVALID_SYNTAX;
633 ber_dupbv_x( normalized, val, ctx );
635 return rc;
638 * 5) group[/groupClass[/memberAttr]]:<DN>
640 * <groupClass> defaults to "groupOfNames"
641 * <memberAttr> defaults to "member"
643 * <DN> must pass DN normalization
645 } else if ( strncasecmp( val->bv_val, "group", STRLENOF( "group" ) ) == 0 )
647 struct berval group_dn = BER_BVNULL,
648 group_oc = BER_BVNULL,
649 member_at = BER_BVNULL,
650 out = BER_BVNULL;
651 char *ptr;
653 bv.bv_val = val->bv_val + STRLENOF( "group" );
654 bv.bv_len = val->bv_len - STRLENOF( "group" );
655 group_dn.bv_val = ber_bvchr( &bv, ':' );
656 if ( group_dn.bv_val == NULL ) {
657 /* last chance: assume it's a(n exact) DN ... */
658 bv.bv_val = val->bv_val;
659 scope = LDAP_X_SCOPE_EXACT;
660 goto is_dn;
664 * FIXME: we assume that "member" and "groupOfNames"
665 * are present in schema...
667 if ( bv.bv_val[ 0 ] == '/' ) {
668 ObjectClass *oc = NULL;
670 group_oc.bv_val = &bv.bv_val[ 1 ];
671 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
673 member_at.bv_val = ber_bvchr( &group_oc, '/' );
674 if ( member_at.bv_val ) {
675 AttributeDescription *ad = NULL;
676 const char *text = NULL;
678 group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
679 member_at.bv_val++;
680 member_at.bv_len = group_dn.bv_val - member_at.bv_val;
681 rc = slap_bv2ad( &member_at, &ad, &text );
682 if ( rc != LDAP_SUCCESS ) {
683 return rc;
686 member_at = ad->ad_cname;
690 oc = oc_bvfind( &group_oc );
691 if ( oc == NULL ) {
692 return LDAP_INVALID_SYNTAX;
695 group_oc = oc->soc_cname;
698 group_dn.bv_val++;
699 group_dn.bv_len = val->bv_len - ( group_dn.bv_val - val->bv_val );
701 if ( normalize ) {
702 rc = dnNormalize( 0, NULL, NULL, &group_dn, &out, ctx );
703 } else {
704 rc = dnPretty( NULL, &group_dn, &out, ctx );
706 if ( rc != LDAP_SUCCESS ) {
707 return rc;
710 normalized->bv_len = STRLENOF( "group" ":" ) + out.bv_len;
711 if ( !BER_BVISNULL( &group_oc ) ) {
712 normalized->bv_len += STRLENOF( "/" ) + group_oc.bv_len;
713 if ( !BER_BVISNULL( &member_at ) ) {
714 normalized->bv_len += STRLENOF( "/" ) + member_at.bv_len;
718 normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
719 ptr = lutil_strcopy( normalized->bv_val, "group" );
720 if ( !BER_BVISNULL( &group_oc ) ) {
721 ptr[ 0 ] = '/';
722 ptr++;
723 ptr = lutil_strncopy( ptr, group_oc.bv_val, group_oc.bv_len );
724 if ( !BER_BVISNULL( &member_at ) ) {
725 ptr[ 0 ] = '/';
726 ptr++;
727 ptr = lutil_strncopy( ptr, member_at.bv_val, member_at.bv_len );
730 ptr[ 0 ] = ':';
731 ptr++;
732 ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
733 ptr[ 0 ] = '\0';
734 ber_memfree_x( out.bv_val, ctx );
736 return rc;
740 * ldap:///<base>??<scope>?<filter>
741 * <scope> ::= {base|one|subtree}
743 * <scope> defaults to "base"
744 * <base> must pass DN normalization
745 * <filter> must pass str2filter()
747 rc = ldap_url_parse( val->bv_val, &ludp );
748 switch ( rc ) {
749 case LDAP_URL_SUCCESS:
750 /* FIXME: the check is pedantic, but I think it's necessary,
751 * because people tend to use things like ldaps:// which
752 * gives the idea SSL is being used. Maybe we could
753 * accept ldapi:// as well, but the point is that we use
754 * an URL as an easy means to define bits of a search with
755 * little parsing.
757 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
759 * must be ldap:///
761 rc = LDAP_INVALID_SYNTAX;
762 goto done;
765 AC_MEMCPY( ludp->lud_scheme, "ldap", STRLENOF( "ldap" ) );
766 break;
768 case LDAP_URL_ERR_BADSCHEME:
770 * last chance: assume it's a(n exact) DN ...
772 * NOTE: must pass DN normalization
774 ldap_free_urldesc( ludp );
775 bv.bv_val = val->bv_val;
776 scope = LDAP_X_SCOPE_EXACT;
777 goto is_dn;
779 default:
780 rc = LDAP_INVALID_SYNTAX;
781 goto done;
784 if ( ( ludp->lud_host && *ludp->lud_host )
785 || ludp->lud_attrs || ludp->lud_exts )
787 /* host part must be empty */
788 /* attrs and extensions parts must be empty */
789 rc = LDAP_INVALID_SYNTAX;
790 goto done;
793 /* Grab the filter */
794 if ( ludp->lud_filter ) {
795 struct berval filterstr;
796 Filter *f;
798 lud_filter = ludp->lud_filter;
800 f = str2filter( lud_filter );
801 if ( f == NULL ) {
802 rc = LDAP_INVALID_SYNTAX;
803 goto done;
805 filter2bv( f, &filterstr );
806 filter_free( f );
807 if ( BER_BVISNULL( &filterstr ) ) {
808 rc = LDAP_INVALID_SYNTAX;
809 goto done;
812 ludp->lud_filter = filterstr.bv_val;
815 /* Grab the searchbase */
816 assert( ludp->lud_dn != NULL );
817 if ( ludp->lud_dn ) {
818 struct berval out = BER_BVNULL;
820 lud_dn = ludp->lud_dn;
822 ber_str2bv( lud_dn, 0, 0, &bv );
823 if ( normalize ) {
824 rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
825 } else {
826 rc = dnPretty( NULL, &bv, &out, ctx );
829 if ( rc != LDAP_SUCCESS ) {
830 goto done;
833 ludp->lud_dn = out.bv_val;
836 ludp->lud_port = 0;
837 normalized->bv_val = ldap_url_desc2str( ludp );
838 if ( normalized->bv_val ) {
839 normalized->bv_len = strlen( normalized->bv_val );
841 } else {
842 rc = LDAP_INVALID_SYNTAX;
845 done:
846 if ( lud_filter ) {
847 if ( ludp->lud_filter != lud_filter ) {
848 ber_memfree( ludp->lud_filter );
850 ludp->lud_filter = lud_filter;
853 if ( lud_dn ) {
854 if ( ludp->lud_dn != lud_dn ) {
855 ber_memfree( ludp->lud_dn );
857 ludp->lud_dn = lud_dn;
860 ldap_free_urldesc( ludp );
862 return( rc );
866 authzNormalize(
867 slap_mask_t usage,
868 Syntax *syntax,
869 MatchingRule *mr,
870 struct berval *val,
871 struct berval *normalized,
872 void *ctx )
874 int rc;
876 Debug( LDAP_DEBUG_TRACE, ">>> authzNormalize: <%s>\n",
877 val->bv_val, 0, 0 );
879 rc = authzPrettyNormal( val, normalized, ctx, 1 );
881 Debug( LDAP_DEBUG_TRACE, "<<< authzNormalize: <%s> (%d)\n",
882 normalized->bv_val, rc, 0 );
884 return rc;
888 authzPretty(
889 Syntax *syntax,
890 struct berval *val,
891 struct berval *out,
892 void *ctx)
894 int rc;
896 Debug( LDAP_DEBUG_TRACE, ">>> authzPretty: <%s>\n",
897 val->bv_val, 0, 0 );
899 rc = authzPrettyNormal( val, out, ctx, 0 );
901 Debug( LDAP_DEBUG_TRACE, "<<< authzPretty: <%s> (%d)\n",
902 out->bv_val, rc, 0 );
904 return rc;
908 static int
909 slap_parseURI(
910 Operation *op,
911 struct berval *uri,
912 struct berval *base,
913 struct berval *nbase,
914 int *scope,
915 Filter **filter,
916 struct berval *fstr,
917 int normalize )
919 struct berval bv;
920 int rc;
921 LDAPURLDesc *ludp;
923 struct berval idx;
925 assert( uri != NULL && !BER_BVISNULL( uri ) );
926 BER_BVZERO( base );
927 BER_BVZERO( nbase );
928 BER_BVZERO( fstr );
929 *scope = -1;
930 *filter = NULL;
932 Debug( LDAP_DEBUG_TRACE,
933 "slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
935 rc = LDAP_PROTOCOL_ERROR;
937 idx = *uri;
938 if ( idx.bv_val[ 0 ] == '{' ) {
939 char *ptr;
941 ptr = ber_bvchr( &idx, '}' ) + 1;
943 assert( ptr != (void *)1 );
945 idx.bv_len -= ptr - idx.bv_val;
946 idx.bv_val = ptr;
947 uri = &idx;
951 * dn[.<dnstyle>]:<dnpattern>
952 * <dnstyle> ::= {exact|regex|children|subtree|onelevel}
954 * <dnstyle> defaults to "exact"
955 * if <dnstyle> is not "regex", <dnpattern> must pass DN normalization
957 if ( !strncasecmp( uri->bv_val, "dn", STRLENOF( "dn" ) ) ) {
958 bv.bv_val = uri->bv_val + STRLENOF( "dn" );
960 if ( bv.bv_val[ 0 ] == '.' ) {
961 bv.bv_val++;
963 if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
964 bv.bv_val += STRLENOF( "exact:" );
965 *scope = LDAP_X_SCOPE_EXACT;
967 } else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
968 bv.bv_val += STRLENOF( "regex:" );
969 *scope = LDAP_X_SCOPE_REGEX;
971 } else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
972 bv.bv_val += STRLENOF( "children:" );
973 *scope = LDAP_X_SCOPE_CHILDREN;
975 } else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
976 bv.bv_val += STRLENOF( "subtree:" );
977 *scope = LDAP_X_SCOPE_SUBTREE;
979 } else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
980 bv.bv_val += STRLENOF( "onelevel:" );
981 *scope = LDAP_X_SCOPE_ONELEVEL;
983 } else {
984 return LDAP_PROTOCOL_ERROR;
987 } else {
988 if ( bv.bv_val[ 0 ] != ':' ) {
989 return LDAP_PROTOCOL_ERROR;
991 *scope = LDAP_X_SCOPE_EXACT;
992 bv.bv_val++;
995 bv.bv_val += strspn( bv.bv_val, " " );
996 /* jump here in case no type specification was present
997 * and uri was not an URI... HEADS-UP: assuming EXACT */
998 is_dn: bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
1000 /* a single '*' means any DN without using regexes */
1001 if ( ber_bvccmp( &bv, '*' ) ) {
1002 *scope = LDAP_X_SCOPE_USERS;
1005 switch ( *scope ) {
1006 case LDAP_X_SCOPE_EXACT:
1007 case LDAP_X_SCOPE_CHILDREN:
1008 case LDAP_X_SCOPE_SUBTREE:
1009 case LDAP_X_SCOPE_ONELEVEL:
1010 if ( normalize ) {
1011 rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
1012 if( rc != LDAP_SUCCESS ) {
1013 *scope = -1;
1015 } else {
1016 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1017 rc = LDAP_SUCCESS;
1019 break;
1021 case LDAP_X_SCOPE_REGEX:
1022 ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1024 case LDAP_X_SCOPE_USERS:
1025 rc = LDAP_SUCCESS;
1026 break;
1028 default:
1029 *scope = -1;
1030 break;
1033 return rc;
1036 * u:<uid>
1038 } else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
1039 && ( uri->bv_val[ 1 ] == ':'
1040 || uri->bv_val[ 1 ] == '/'
1041 || uri->bv_val[ 1 ] == '.' ) )
1043 Connection c = *op->o_conn;
1044 char buf[ SLAP_LDAPDN_MAXLEN ];
1045 struct berval id,
1046 user = BER_BVNULL,
1047 realm = BER_BVNULL,
1048 mech = BER_BVNULL;
1050 if ( sizeof( buf ) <= uri->bv_len ) {
1051 return LDAP_INVALID_SYNTAX;
1054 id.bv_len = uri->bv_len;
1055 id.bv_val = buf;
1056 strncpy( buf, uri->bv_val, sizeof( buf ) );
1058 rc = slap_parse_user( &id, &user, &realm, &mech );
1059 if ( rc != LDAP_SUCCESS ) {
1060 return rc;
1063 if ( !BER_BVISNULL( &mech ) ) {
1064 c.c_sasl_bind_mech = mech;
1065 } else {
1066 BER_BVSTR( &c.c_sasl_bind_mech, "AUTHZ" );
1069 rc = slap_sasl_getdn( &c, op, &user,
1070 realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
1072 if ( rc == LDAP_SUCCESS ) {
1073 *scope = LDAP_X_SCOPE_EXACT;
1076 return rc;
1079 * group[/<groupoc>[/<groupat>]]:<groupdn>
1081 * groupoc defaults to "groupOfNames"
1082 * groupat defaults to "member"
1084 * <groupdn> must pass DN normalization
1086 } else if ( strncasecmp( uri->bv_val, "group", STRLENOF( "group" ) ) == 0 )
1088 struct berval group_dn = BER_BVNULL,
1089 group_oc = BER_BVNULL,
1090 member_at = BER_BVNULL;
1091 char *tmp;
1093 bv.bv_val = uri->bv_val + STRLENOF( "group" );
1094 bv.bv_len = uri->bv_len - STRLENOF( "group" );
1095 group_dn.bv_val = ber_bvchr( &bv, ':' );
1096 if ( group_dn.bv_val == NULL ) {
1097 /* last chance: assume it's a(n exact) DN ... */
1098 bv.bv_val = uri->bv_val;
1099 *scope = LDAP_X_SCOPE_EXACT;
1100 goto is_dn;
1103 if ( bv.bv_val[ 0 ] == '/' ) {
1104 group_oc.bv_val = &bv.bv_val[ 1 ];
1105 group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
1107 member_at.bv_val = ber_bvchr( &group_oc, '/' );
1108 if ( member_at.bv_val ) {
1109 group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
1110 member_at.bv_val++;
1111 member_at.bv_len = group_dn.bv_val - member_at.bv_val;
1113 } else {
1114 BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
1117 } else {
1118 BER_BVSTR( &group_oc, SLAPD_GROUP_CLASS );
1119 BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
1121 group_dn.bv_val++;
1122 group_dn.bv_len = uri->bv_len - ( group_dn.bv_val - uri->bv_val );
1124 if ( normalize ) {
1125 rc = dnNormalize( 0, NULL, NULL, &group_dn, nbase, op->o_tmpmemctx );
1126 if ( rc != LDAP_SUCCESS ) {
1127 *scope = -1;
1128 return rc;
1130 } else {
1131 ber_dupbv_x( nbase, &group_dn, op->o_tmpmemctx );
1132 rc = LDAP_SUCCESS;
1134 *scope = LDAP_X_SCOPE_GROUP;
1136 /* FIXME: caller needs to add value of member attribute
1137 * and close brackets twice */
1138 fstr->bv_len = STRLENOF( "(&(objectClass=)(=" /* )) */ )
1139 + group_oc.bv_len + member_at.bv_len;
1140 fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
1142 tmp = lutil_strncopy( fstr->bv_val, "(&(objectClass=" /* )) */ ,
1143 STRLENOF( "(&(objectClass=" /* )) */ ) );
1144 tmp = lutil_strncopy( tmp, group_oc.bv_val, group_oc.bv_len );
1145 tmp = lutil_strncopy( tmp, /* ( */ ")(" /* ) */ ,
1146 STRLENOF( /* ( */ ")(" /* ) */ ) );
1147 tmp = lutil_strncopy( tmp, member_at.bv_val, member_at.bv_len );
1148 tmp = lutil_strncopy( tmp, "=", STRLENOF( "=" ) );
1150 return rc;
1154 * ldap:///<base>??<scope>?<filter>
1155 * <scope> ::= {base|one|subtree}
1157 * <scope> defaults to "base"
1158 * <base> must pass DN normalization
1159 * <filter> must pass str2filter()
1161 rc = ldap_url_parse( uri->bv_val, &ludp );
1162 switch ( rc ) {
1163 case LDAP_URL_SUCCESS:
1164 /* FIXME: the check is pedantic, but I think it's necessary,
1165 * because people tend to use things like ldaps:// which
1166 * gives the idea SSL is being used. Maybe we could
1167 * accept ldapi:// as well, but the point is that we use
1168 * an URL as an easy means to define bits of a search with
1169 * little parsing.
1171 if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
1173 * must be ldap:///
1175 rc = LDAP_PROTOCOL_ERROR;
1176 goto done;
1178 break;
1180 case LDAP_URL_ERR_BADSCHEME:
1182 * last chance: assume it's a(n exact) DN ...
1184 * NOTE: must pass DN normalization
1186 ldap_free_urldesc( ludp );
1187 bv.bv_val = uri->bv_val;
1188 *scope = LDAP_X_SCOPE_EXACT;
1189 goto is_dn;
1191 default:
1192 rc = LDAP_PROTOCOL_ERROR;
1193 goto done;
1196 if ( ( ludp->lud_host && *ludp->lud_host )
1197 || ludp->lud_attrs || ludp->lud_exts )
1199 /* host part must be empty */
1200 /* attrs and extensions parts must be empty */
1201 rc = LDAP_PROTOCOL_ERROR;
1202 goto done;
1205 /* Grab the scope */
1206 *scope = ludp->lud_scope;
1208 /* Grab the filter */
1209 if ( ludp->lud_filter ) {
1210 *filter = str2filter_x( op, ludp->lud_filter );
1211 if ( *filter == NULL ) {
1212 rc = LDAP_PROTOCOL_ERROR;
1213 goto done;
1215 ber_str2bv( ludp->lud_filter, 0, 0, fstr );
1218 /* Grab the searchbase */
1219 ber_str2bv( ludp->lud_dn, 0, 0, base );
1220 if ( normalize ) {
1221 rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
1222 } else {
1223 ber_dupbv_x( nbase, base, op->o_tmpmemctx );
1224 rc = LDAP_SUCCESS;
1227 done:
1228 if( rc != LDAP_SUCCESS ) {
1229 if( *filter ) filter_free_x( op, *filter );
1230 BER_BVZERO( base );
1231 BER_BVZERO( fstr );
1232 } else {
1233 /* Don't free these, return them to caller */
1234 ludp->lud_filter = NULL;
1235 ludp->lud_dn = NULL;
1238 ldap_free_urldesc( ludp );
1239 return( rc );
1242 #ifndef SLAP_AUTH_REWRITE
1243 static int slap_sasl_rx_off(char *rep, int *off)
1245 const char *c;
1246 int n;
1248 /* Precompile replace pattern. Find the $<n> placeholders */
1249 off[0] = -2;
1250 n = 1;
1251 for ( c = rep; *c; c++ ) {
1252 if ( *c == '\\' && c[1] ) {
1253 c++;
1254 continue;
1256 if ( *c == '$' ) {
1257 if ( n == SASLREGEX_REPLACE ) {
1258 Debug( LDAP_DEBUG_ANY,
1259 "SASL replace pattern %s has too many $n "
1260 "placeholders (max %d)\n",
1261 rep, SASLREGEX_REPLACE, 0 );
1263 return( LDAP_OTHER );
1265 off[n] = c - rep;
1266 n++;
1270 /* Final placeholder, after the last $n */
1271 off[n] = c - rep;
1272 n++;
1273 off[n] = -1;
1274 return( LDAP_SUCCESS );
1276 #endif /* ! SLAP_AUTH_REWRITE */
1278 #ifdef SLAP_AUTH_REWRITE
1279 int slap_sasl_rewrite_config(
1280 const char *fname,
1281 int lineno,
1282 int argc,
1283 char **argv
1286 int rc;
1287 char *savearg0;
1289 /* init at first call */
1290 if ( sasl_rwinfo == NULL ) {
1291 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1294 /* strip "authid-" prefix for parsing */
1295 savearg0 = argv[0];
1296 argv[0] += STRLENOF( "authid-" );
1297 rc = rewrite_parse( sasl_rwinfo, fname, lineno, argc, argv );
1298 argv[0] = savearg0;
1300 return rc;
1303 static int
1304 slap_sasl_rewrite_destroy( void )
1306 if ( sasl_rwinfo ) {
1307 rewrite_info_delete( &sasl_rwinfo );
1308 sasl_rwinfo = NULL;
1311 return 0;
1314 int slap_sasl_regexp_rewrite_config(
1315 const char *fname,
1316 int lineno,
1317 const char *match,
1318 const char *replace,
1319 const char *context )
1321 int rc;
1322 char *argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL };
1324 /* init at first call */
1325 if ( sasl_rwinfo == NULL ) {
1326 char *argvEngine[] = { "rewriteEngine", "on", NULL };
1327 char *argvContext[] = { "rewriteContext", NULL, NULL };
1329 /* initialize rewrite engine */
1330 sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1332 /* switch on rewrite engine */
1333 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine );
1334 if (rc != LDAP_SUCCESS) {
1335 return rc;
1338 /* create generic authid context */
1339 argvContext[1] = AUTHID_CONTEXT;
1340 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvContext );
1341 if (rc != LDAP_SUCCESS) {
1342 return rc;
1346 argvRule[1] = (char *)match;
1347 argvRule[2] = (char *)replace;
1348 rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule );
1350 return rc;
1352 #endif /* SLAP_AUTH_REWRITE */
1354 int slap_sasl_regexp_config( const char *match, const char *replace )
1356 int rc;
1357 SaslRegexp_t *reg;
1359 SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
1360 (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
1362 reg = &SaslRegexp[nSaslRegexp];
1364 #ifdef SLAP_AUTH_REWRITE
1365 rc = slap_sasl_regexp_rewrite_config( "sasl-regexp", 0,
1366 match, replace, AUTHID_CONTEXT );
1367 #else /* ! SLAP_AUTH_REWRITE */
1369 /* Precompile matching pattern */
1370 rc = regcomp( &reg->sr_workspace, match, REG_EXTENDED|REG_ICASE );
1371 if ( rc ) {
1372 Debug( LDAP_DEBUG_ANY,
1373 "SASL match pattern %s could not be compiled by regexp engine\n",
1374 match, 0, 0 );
1376 #ifdef ENABLE_REWRITE
1377 /* Dummy block to force symbol references in librewrite */
1378 if ( slapMode == ( SLAP_SERVER_MODE|SLAP_TOOL_MODE )) {
1379 rewrite_info_init( 0 );
1381 #endif
1382 return( LDAP_OTHER );
1385 rc = slap_sasl_rx_off( replace, reg->sr_offset );
1386 #endif /* ! SLAP_AUTH_REWRITE */
1387 if ( rc == LDAP_SUCCESS ) {
1388 reg->sr_match = ch_strdup( match );
1389 reg->sr_replace = ch_strdup( replace );
1391 nSaslRegexp++;
1394 return rc;
1397 void
1398 slap_sasl_regexp_destroy( void )
1400 if ( SaslRegexp ) {
1401 int n;
1403 for ( n = 0; n < nSaslRegexp; n++ ) {
1404 ch_free( SaslRegexp[ n ].sr_match );
1405 ch_free( SaslRegexp[ n ].sr_replace );
1406 #ifndef SLAP_AUTH_REWRITE
1407 regfree( &SaslRegexp[ n ].sr_workspace );
1408 #endif /* SLAP_AUTH_REWRITE */
1411 ch_free( SaslRegexp );
1414 #ifdef SLAP_AUTH_REWRITE
1415 slap_sasl_rewrite_destroy();
1416 #endif /* SLAP_AUTH_REWRITE */
1419 void slap_sasl_regexp_unparse( BerVarray *out )
1421 int i;
1422 BerVarray bva = NULL;
1423 char ibuf[32], *ptr;
1424 struct berval idx;
1426 if ( !nSaslRegexp ) return;
1428 idx.bv_val = ibuf;
1429 bva = ch_malloc( (nSaslRegexp+1) * sizeof(struct berval) );
1430 BER_BVZERO(bva+nSaslRegexp);
1431 for ( i=0; i<nSaslRegexp; i++ ) {
1432 idx.bv_len = sprintf( idx.bv_val, "{%d}", i);
1433 bva[i].bv_len = idx.bv_len + strlen( SaslRegexp[i].sr_match ) +
1434 strlen( SaslRegexp[i].sr_replace ) + 5;
1435 bva[i].bv_val = ch_malloc( bva[i].bv_len+1 );
1436 ptr = lutil_strcopy( bva[i].bv_val, ibuf );
1437 *ptr++ = '"';
1438 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match );
1439 ptr = lutil_strcopy( ptr, "\" \"" );
1440 ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace );
1441 *ptr++ = '"';
1442 *ptr = '\0';
1444 *out = bva;
1447 #ifndef SLAP_AUTH_REWRITE
1448 /* Perform replacement on regexp matches */
1449 static void slap_sasl_rx_exp(
1450 const char *rep,
1451 const int *off,
1452 regmatch_t *str,
1453 const char *saslname,
1454 struct berval *out,
1455 void *ctx )
1457 int i, n, len, insert;
1459 /* Get the total length of the final URI */
1461 n=1;
1462 len = 0;
1463 while( off[n] >= 0 ) {
1464 /* Len of next section from replacement string (x,y,z above) */
1465 len += off[n] - off[n-1] - 2;
1466 if( off[n+1] < 0)
1467 break;
1469 /* Len of string from saslname that matched next $i (b,d above) */
1470 i = rep[ off[n] + 1 ] - '0';
1471 len += str[i].rm_eo - str[i].rm_so;
1472 n++;
1474 out->bv_val = slap_sl_malloc( len + 1, ctx );
1475 out->bv_len = len;
1477 /* Fill in URI with replace string, replacing $i as we go */
1478 n=1;
1479 insert = 0;
1480 while( off[n] >= 0) {
1481 /* Paste in next section from replacement string (x,y,z above) */
1482 len = off[n] - off[n-1] - 2;
1483 strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
1484 insert += len;
1485 if( off[n+1] < 0)
1486 break;
1488 /* Paste in string from saslname that matched next $i (b,d above) */
1489 i = rep[ off[n] + 1 ] - '0';
1490 len = str[i].rm_eo - str[i].rm_so;
1491 strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
1492 insert += len;
1494 n++;
1497 out->bv_val[insert] = '\0';
1499 #endif /* ! SLAP_AUTH_REWRITE */
1501 /* Take the passed in SASL name and attempt to convert it into an
1502 LDAP URI to find the matching LDAP entry, using the pattern matching
1503 strings given in the saslregexp config file directive(s) */
1505 static int slap_authz_regexp( struct berval *in, struct berval *out,
1506 int flags, void *ctx )
1508 #ifdef SLAP_AUTH_REWRITE
1509 const char *context = AUTHID_CONTEXT;
1511 if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
1512 return 0;
1515 /* FIXME: if aware of authc/authz mapping,
1516 * we could use different contexts ... */
1517 switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL,
1518 &out->bv_val ) )
1520 case REWRITE_REGEXEC_OK:
1521 if ( !BER_BVISNULL( out ) ) {
1522 char *val = out->bv_val;
1523 ber_str2bv_x( val, 0, 1, out, ctx );
1524 if ( val != in->bv_val ) {
1525 free( val );
1527 } else {
1528 ber_dupbv_x( out, in, ctx );
1530 Debug( LDAP_DEBUG_ARGS,
1531 "[rw] %s: \"%s\" -> \"%s\"\n",
1532 context, in->bv_val, out->bv_val );
1533 return 1;
1535 case REWRITE_REGEXEC_UNWILLING:
1536 case REWRITE_REGEXEC_ERR:
1537 default:
1538 return 0;
1541 #else /* ! SLAP_AUTH_REWRITE */
1542 char *saslname = in->bv_val;
1543 SaslRegexp_t *reg;
1544 regmatch_t sr_strings[SASLREGEX_REPLACE]; /* strings matching $1,$2 ... */
1545 int i;
1547 memset( out, 0, sizeof( *out ) );
1549 Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
1550 saslname, 0, 0 );
1552 if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
1553 return( 0 );
1556 /* Match the normalized SASL name to the saslregexp patterns */
1557 for( reg = SaslRegexp,i=0; i<nSaslRegexp; i++,reg++ ) {
1558 if ( regexec( &reg->sr_workspace, saslname, SASLREGEX_REPLACE,
1559 sr_strings, 0) == 0 )
1560 break;
1563 if( i >= nSaslRegexp ) return( 0 );
1566 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
1567 * replace pattern of the form "x$1y$2z". The returned string needs
1568 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
1570 slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
1571 sr_strings, saslname, out, ctx );
1573 Debug( LDAP_DEBUG_TRACE,
1574 "slap_authz_regexp: converted SASL name to %s\n",
1575 BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
1577 return( 1 );
1578 #endif /* ! SLAP_AUTH_REWRITE */
1581 /* This callback actually does some work...*/
1582 static int sasl_sc_sasl2dn( Operation *op, SlapReply *rs )
1584 struct berval *ndn = op->o_callback->sc_private;
1586 if ( rs->sr_type != REP_SEARCH ) return LDAP_SUCCESS;
1588 /* We only want to be called once */
1589 if ( !BER_BVISNULL( ndn ) ) {
1590 op->o_tmpfree( ndn->bv_val, op->o_tmpmemctx );
1591 BER_BVZERO( ndn );
1593 Debug( LDAP_DEBUG_TRACE,
1594 "%s: slap_sc_sasl2dn: search DN returned more than 1 entry\n",
1595 op->o_log_prefix, 0, 0 );
1596 return LDAP_UNAVAILABLE; /* short-circuit the search */
1599 ber_dupbv_x( ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
1600 return LDAP_SUCCESS;
1604 typedef struct smatch_info {
1605 struct berval *dn;
1606 int match;
1607 } smatch_info;
1609 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
1611 smatch_info *sm = o->o_callback->sc_private;
1613 if (rs->sr_type != REP_SEARCH) return 0;
1615 if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
1616 sm->match = 1;
1617 return LDAP_UNAVAILABLE; /* short-circuit the search */
1620 return 0;
1624 slap_sasl_matches( Operation *op, BerVarray rules,
1625 struct berval *assertDN, struct berval *authc )
1627 int rc = LDAP_INAPPROPRIATE_AUTH;
1629 if ( rules != NULL ) {
1630 int i;
1632 for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
1633 rc = slap_sasl_match( op, &rules[i], assertDN, authc );
1634 if ( rc == LDAP_SUCCESS ) break;
1638 return rc;
1642 * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
1643 * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
1644 * the rule must be used as an internal search for entries. If that search
1645 * returns the *assertDN entry, the match is successful.
1647 * The assertDN should not have the dn: prefix
1650 static int
1651 slap_sasl_match( Operation *opx, struct berval *rule,
1652 struct berval *assertDN, struct berval *authc )
1654 int rc;
1655 regex_t reg;
1656 smatch_info sm;
1657 slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
1658 Operation op = {0};
1659 SlapReply rs = {REP_RESULT};
1660 struct berval base = BER_BVNULL;
1662 sm.dn = assertDN;
1663 sm.match = 0;
1664 cb.sc_private = &sm;
1666 Debug( LDAP_DEBUG_TRACE,
1667 "===>slap_sasl_match: comparing DN %s to rule %s\n",
1668 assertDN->bv_len ? assertDN->bv_val : "(null)", rule->bv_val, 0 );
1670 /* NOTE: don't normalize rule if authz syntax is enabled */
1671 rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn,
1672 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 0 );
1674 if( rc != LDAP_SUCCESS ) goto CONCLUDED;
1676 switch ( op.ors_scope ) {
1677 case LDAP_X_SCOPE_EXACT:
1678 exact_match:
1679 if ( dn_match( &op.o_req_ndn, assertDN ) ) {
1680 rc = LDAP_SUCCESS;
1681 } else {
1682 rc = LDAP_INAPPROPRIATE_AUTH;
1684 goto CONCLUDED;
1686 case LDAP_X_SCOPE_CHILDREN:
1687 case LDAP_X_SCOPE_SUBTREE:
1688 case LDAP_X_SCOPE_ONELEVEL:
1690 int d = assertDN->bv_len - op.o_req_ndn.bv_len;
1692 rc = LDAP_INAPPROPRIATE_AUTH;
1694 if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
1695 goto exact_match;
1697 } else if ( d > 0 ) {
1698 struct berval bv;
1700 /* leave room for at least one char of attributeType,
1701 * one for '=' and one for ',' */
1702 if ( d < STRLENOF( "x=,") ) {
1703 goto CONCLUDED;
1706 bv.bv_len = op.o_req_ndn.bv_len;
1707 bv.bv_val = assertDN->bv_val + d;
1709 if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
1710 switch ( op.ors_scope ) {
1711 case LDAP_X_SCOPE_SUBTREE:
1712 case LDAP_X_SCOPE_CHILDREN:
1713 rc = LDAP_SUCCESS;
1714 break;
1716 case LDAP_X_SCOPE_ONELEVEL:
1718 struct berval pdn;
1720 dnParent( assertDN, &pdn );
1721 /* the common portion of the DN
1722 * already matches, so only check
1723 * if parent DN of assertedDN
1724 * is all the pattern */
1725 if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
1726 rc = LDAP_SUCCESS;
1728 break;
1730 default:
1731 /* at present, impossible */
1732 assert( 0 );
1736 goto CONCLUDED;
1739 case LDAP_X_SCOPE_REGEX:
1740 rc = regcomp(&reg, op.o_req_ndn.bv_val,
1741 REG_EXTENDED|REG_ICASE|REG_NOSUB);
1742 if ( rc == 0 ) {
1743 rc = regexec(&reg, assertDN->bv_val, 0, NULL, 0);
1744 regfree( &reg );
1746 if ( rc == 0 ) {
1747 rc = LDAP_SUCCESS;
1748 } else {
1749 rc = LDAP_INAPPROPRIATE_AUTH;
1751 goto CONCLUDED;
1753 case LDAP_X_SCOPE_GROUP: {
1754 char *tmp;
1756 /* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
1757 * we need to append the <assertDN> so that the <group_dn> is searched
1758 * with scope "base", and the filter ensures that <assertDN> is
1759 * member of the group */
1760 tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len +
1761 assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 );
1762 if ( tmp == NULL ) {
1763 rc = LDAP_NO_MEMORY;
1764 goto CONCLUDED;
1766 op.ors_filterstr.bv_val = tmp;
1768 tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
1769 tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
1771 /* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
1772 op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
1773 if ( op.ors_filter == NULL ) {
1774 rc = LDAP_PROTOCOL_ERROR;
1775 goto CONCLUDED;
1777 op.ors_scope = LDAP_SCOPE_BASE;
1779 /* hijack match DN: use that of the group instead of the assertDN;
1780 * assertDN is now in the filter */
1781 sm.dn = &op.o_req_ndn;
1783 /* do the search */
1784 break;
1787 case LDAP_X_SCOPE_USERS:
1788 if ( !BER_BVISEMPTY( assertDN ) ) {
1789 rc = LDAP_SUCCESS;
1790 } else {
1791 rc = LDAP_INAPPROPRIATE_AUTH;
1793 goto CONCLUDED;
1795 default:
1796 break;
1799 /* Must run an internal search. */
1800 if ( op.ors_filter == NULL ) {
1801 rc = LDAP_FILTER_ERROR;
1802 goto CONCLUDED;
1805 Debug( LDAP_DEBUG_TRACE,
1806 "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
1807 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1809 op.o_bd = select_backend( &op.o_req_ndn, 1 );
1810 if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1811 rc = LDAP_INAPPROPRIATE_AUTH;
1812 goto CONCLUDED;
1815 op.o_hdr = opx->o_hdr;
1816 op.o_tag = LDAP_REQ_SEARCH;
1817 op.o_ndn = *authc;
1818 op.o_callback = &cb;
1819 slap_op_time( &op.o_time, &op.o_tincr );
1820 op.o_do_not_cache = 1;
1821 op.o_is_auth_check = 1;
1822 /* use req_ndn as req_dn instead of non-pretty base of uri */
1823 if( !BER_BVISNULL( &base ) ) {
1824 ch_free( base.bv_val );
1825 /* just in case... */
1826 BER_BVZERO( &base );
1828 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1829 op.ors_deref = LDAP_DEREF_NEVER;
1830 op.ors_slimit = 1;
1831 op.ors_tlimit = SLAP_NO_LIMIT;
1832 op.ors_attrs = slap_anlist_no_attrs;
1833 op.ors_attrsonly = 1;
1835 op.o_bd->be_search( &op, &rs );
1837 if (sm.match) {
1838 rc = LDAP_SUCCESS;
1839 } else {
1840 rc = LDAP_INAPPROPRIATE_AUTH;
1843 CONCLUDED:
1844 if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1845 if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1846 if( op.ors_filter ) filter_free_x( opx, op.ors_filter );
1847 if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1849 Debug( LDAP_DEBUG_TRACE,
1850 "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
1852 return( rc );
1857 * This function answers the question, "Can this ID authorize to that ID?",
1858 * based on authorization rules. The rules are stored in the *searchDN, in the
1859 * attribute named by *attr. If any of those rules map to the *assertDN, the
1860 * authorization is approved.
1862 * The DNs should not have the dn: prefix
1864 static int
1865 slap_sasl_check_authz( Operation *op,
1866 struct berval *searchDN,
1867 struct berval *assertDN,
1868 AttributeDescription *ad,
1869 struct berval *authc )
1871 int rc,
1872 do_not_cache = op->o_do_not_cache;
1873 BerVarray vals = NULL;
1875 Debug( LDAP_DEBUG_TRACE,
1876 "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
1877 assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1879 /* ITS#4760: don't cache group access */
1880 op->o_do_not_cache = 1;
1881 rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
1882 op->o_do_not_cache = do_not_cache;
1883 if( rc != LDAP_SUCCESS ) goto COMPLETE;
1885 /* Check if the *assertDN matches any *vals */
1886 rc = slap_sasl_matches( op, vals, assertDN, authc );
1888 COMPLETE:
1889 if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1891 Debug( LDAP_DEBUG_TRACE,
1892 "<==slap_sasl_check_authz: %s check returning %d\n",
1893 ad->ad_cname.bv_val, rc, 0);
1895 return( rc );
1899 * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
1900 * return the LDAP DN to which it matches. The SASL regexp rules in the config
1901 * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
1902 * search with scope=base), just return the URI (or its searchbase). Otherwise
1903 * an internal search must be done, and if that search returns exactly one
1904 * entry, return the DN of that one entry.
1906 void
1907 slap_sasl2dn(
1908 Operation *opx,
1909 struct berval *saslname,
1910 struct berval *sasldn,
1911 int flags )
1913 int rc;
1914 slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
1915 Operation op = {0};
1916 SlapReply rs = {REP_RESULT};
1917 struct berval regout = BER_BVNULL;
1918 struct berval base = BER_BVNULL;
1920 Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1921 "converting SASL name %s to a DN\n",
1922 saslname->bv_val, 0,0 );
1924 BER_BVZERO( sasldn );
1925 cb.sc_private = sasldn;
1927 /* Convert the SASL name into a minimal URI */
1928 if( !slap_authz_regexp( saslname, &regout, flags, opx->o_tmpmemctx ) ) {
1929 goto FINISHED;
1932 /* NOTE: always normalize regout because it results
1933 * from string submatch expansion */
1934 rc = slap_parseURI( opx, &regout, &base, &op.o_req_ndn,
1935 &op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 );
1936 if ( !BER_BVISNULL( &regout ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
1937 if ( rc != LDAP_SUCCESS ) {
1938 goto FINISHED;
1941 /* Must do an internal search */
1942 op.o_bd = select_backend( &op.o_req_ndn, 1 );
1944 switch ( op.ors_scope ) {
1945 case LDAP_X_SCOPE_EXACT:
1946 *sasldn = op.o_req_ndn;
1947 BER_BVZERO( &op.o_req_ndn );
1948 /* intentionally continue to next case */
1950 case LDAP_X_SCOPE_REGEX:
1951 case LDAP_X_SCOPE_SUBTREE:
1952 case LDAP_X_SCOPE_CHILDREN:
1953 case LDAP_X_SCOPE_ONELEVEL:
1954 case LDAP_X_SCOPE_GROUP:
1955 case LDAP_X_SCOPE_USERS:
1956 /* correctly parsed, but illegal */
1957 goto FINISHED;
1959 case LDAP_SCOPE_BASE:
1960 case LDAP_SCOPE_ONELEVEL:
1961 case LDAP_SCOPE_SUBTREE:
1962 case LDAP_SCOPE_SUBORDINATE:
1963 /* do a search */
1964 break;
1966 default:
1967 /* catch unhandled cases (there shouldn't be) */
1968 assert( 0 );
1971 Debug( LDAP_DEBUG_TRACE,
1972 "slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1973 op.o_req_ndn.bv_val, op.ors_scope, 0 );
1975 if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
1976 goto FINISHED;
1979 /* Must run an internal search. */
1980 if ( op.ors_filter == NULL ) {
1981 rc = LDAP_FILTER_ERROR;
1982 goto FINISHED;
1985 op.o_hdr = opx->o_hdr;
1986 op.o_tag = LDAP_REQ_SEARCH;
1987 op.o_ndn = opx->o_conn->c_ndn;
1988 op.o_callback = &cb;
1989 slap_op_time( &op.o_time, &op.o_tincr );
1990 op.o_do_not_cache = 1;
1991 op.o_is_auth_check = 1;
1992 op.ors_deref = LDAP_DEREF_NEVER;
1993 op.ors_slimit = 1;
1994 op.ors_tlimit = SLAP_NO_LIMIT;
1995 op.ors_attrs = slap_anlist_no_attrs;
1996 op.ors_attrsonly = 1;
1997 /* use req_ndn as req_dn instead of non-pretty base of uri */
1998 if( !BER_BVISNULL( &base ) ) {
1999 ch_free( base.bv_val );
2000 /* just in case... */
2001 BER_BVZERO( &base );
2003 ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
2005 op.o_bd->be_search( &op, &rs );
2007 FINISHED:
2008 if( !BER_BVISEMPTY( sasldn ) ) {
2009 opx->o_conn->c_authz_backend = op.o_bd;
2011 if( !BER_BVISNULL( &op.o_req_dn ) ) {
2012 slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
2014 if( !BER_BVISNULL( &op.o_req_ndn ) ) {
2015 slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
2017 if( op.ors_filter ) {
2018 filter_free_x( opx, op.ors_filter );
2020 if( !BER_BVISNULL( &op.ors_filterstr ) ) {
2021 ch_free( op.ors_filterstr.bv_val );
2024 Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
2025 !BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
2027 return;
2031 /* Check if a bind can SASL authorize to another identity.
2032 * The DNs should not have the dn: prefix
2035 int slap_sasl_authorized( Operation *op,
2036 struct berval *authcDN, struct berval *authzDN )
2038 int rc = LDAP_INAPPROPRIATE_AUTH;
2040 /* User binding as anonymous */
2041 if ( !authzDN || !authzDN->bv_len || !authzDN->bv_val ) {
2042 rc = LDAP_SUCCESS;
2043 goto DONE;
2046 /* User is anonymous */
2047 if ( !authcDN || !authcDN->bv_len || !authcDN->bv_val ) {
2048 goto DONE;
2051 Debug( LDAP_DEBUG_TRACE,
2052 "==>slap_sasl_authorized: can %s become %s?\n",
2053 authcDN->bv_len ? authcDN->bv_val : "(null)",
2054 authzDN->bv_len ? authzDN->bv_val : "(null)", 0 );
2056 /* If person is authorizing to self, succeed */
2057 if ( dn_match( authcDN, authzDN ) ) {
2058 rc = LDAP_SUCCESS;
2059 goto DONE;
2062 /* Allow the manager to authorize as any DN. */
2063 if( op->o_conn->c_authz_backend &&
2064 be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
2066 rc = LDAP_SUCCESS;
2067 goto DONE;
2070 /* Check source rules */
2071 if( authz_policy & SASL_AUTHZ_TO ) {
2072 rc = slap_sasl_check_authz( op, authcDN, authzDN,
2073 slap_schema.si_ad_saslAuthzTo, authcDN );
2074 if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
2075 goto DONE;
2079 /* Check destination rules */
2080 if( authz_policy & SASL_AUTHZ_FROM ) {
2081 rc = slap_sasl_check_authz( op, authzDN, authcDN,
2082 slap_schema.si_ad_saslAuthzFrom, authcDN );
2083 if( rc == LDAP_SUCCESS ) {
2084 goto DONE;
2088 rc = LDAP_INAPPROPRIATE_AUTH;
2090 DONE:
2092 Debug( LDAP_DEBUG_TRACE,
2093 "<== slap_sasl_authorized: return %d\n", rc, 0, 0 );
2095 return( rc );