xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/saslauthz.c (revision 274254cdae52594c1aa480a736aef78313d15c9c)
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/>.
3  *
4  * Copyright 1998-2008 The OpenLDAP Foundation.
5  * Portions Copyright 2000 Mark Adamson, Carnegie Mellon.
6  * All rights reserved.
7  *
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.
11  *
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>.
15  */
16 
17 #include "portable.h"
18 
19 #include <stdio.h>
20 #ifdef HAVE_LIMITS_H
21 #include <limits.h>
22 #endif
23 
24 #include <ac/stdlib.h>
25 #include <ac/string.h>
26 #include <ac/ctype.h>
27 
28 #include "slap.h"
29 
30 #include "lutil.h"
31 
32 #define SASLREGEX_REPLACE 10
33 
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)
41 
42 /*
43  * IDs in DNauthzid form can now have a type specifier, that
44  * influences how they are used in related operations.
45  *
46  * syntax: dn[.{exact|regex}]:<val>
47  *
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.
58  *
59  * IDs in DNauthzid form can now have a type specifier, that
60  * influences how they are used in related operations.
61  *
62  * syntax: u[.mech[/realm]]:<val>
63  *
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).
67  */
68 
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;
75 
76 static int nSaslRegexp = 0;
77 static SaslRegexp_t *SaslRegexp = NULL;
78 
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 */
84 
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
90 
91 static const char *policy_txt[] = {
92 	"none", "from", "to", "any"
93 };
94 
95 static int authz_policy = SASL_AUTHZ_NONE;
96 
97 static int
98 slap_sasl_match( Operation *opx, struct berval *rule,
99 	struct berval *assertDN, struct berval *authc );
100 
101 int slap_sasl_setpolicy( const char *arg )
102 {
103 	int rc = LDAP_SUCCESS;
104 
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;
117 	}
118 	return rc;
119 }
120 
121 const char * slap_sasl_getpolicy()
122 {
123 	if ( authz_policy == (SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND) )
124 		return "all";
125 	else
126 		return policy_txt[authz_policy];
127 }
128 
129 int slap_parse_user( struct berval *id, struct berval *user,
130 		struct berval *realm, struct berval *mech )
131 {
132 	char	u;
133 
134 	assert( id != NULL );
135 	assert( !BER_BVISNULL( id ) );
136 	assert( user != NULL );
137 	assert( realm != NULL );
138 	assert( mech != NULL );
139 
140 	u = id->bv_val[ 0 ];
141 
142 	if ( u != 'u' && u != 'U' ) {
143 		/* called with something other than u: */
144 		return LDAP_PROTOCOL_ERROR;
145 	}
146 
147 	/* uauthzid form:
148 	 *		u[.mech[/realm]]:user
149 	 */
150 
151 	user->bv_val = ber_bvchr( id, ':' );
152 	if ( BER_BVISNULL( user ) ) {
153 		return LDAP_PROTOCOL_ERROR;
154 	}
155 	user->bv_val[ 0 ] = '\0';
156 	user->bv_val++;
157 	user->bv_len = id->bv_len - ( user->bv_val - id->bv_val );
158 
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;
164 
165 		realm->bv_val = ber_bvchr( mech, '/' );
166 
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;
172 		}
173 
174 	} else {
175 		BER_BVZERO( realm );
176 	}
177 
178 	if ( id->bv_val[ 1 ] != '\0' ) {
179 		return LDAP_PROTOCOL_ERROR;
180 	}
181 
182 	if ( !BER_BVISNULL( mech ) ) {
183 		assert( mech->bv_val == id->bv_val + 2 );
184 
185 		AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
186 		mech->bv_val -= 2;
187 	}
188 
189 	if ( !BER_BVISNULL( realm ) ) {
190 		assert( realm->bv_val >= id->bv_val + 2 );
191 
192 		AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
193 		realm->bv_val -= 2;
194 	}
195 
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 ] = ':';
201 
202 	return LDAP_SUCCESS;
203 }
204 
205 int
206 authzValidate(
207 	Syntax *syntax,
208 	struct berval *in )
209 {
210 	struct berval	bv;
211 	int		rc = LDAP_INVALID_SYNTAX;
212 	LDAPURLDesc	*ludp = NULL;
213 	int		scope = -1;
214 
215 	/*
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>
222 	 */
223 
224 	assert( in != NULL );
225 	assert( !BER_BVISNULL( in ) );
226 
227 	Debug( LDAP_DEBUG_TRACE,
228 		"authzValidate: parsing %s\n", in->bv_val, 0, 0 );
229 
230 	/*
231 	 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
232 	 * 3) dn.regex:<pattern>
233 	 *
234 	 * <DN> must pass DN normalization
235 	 */
236 	if ( !strncasecmp( in->bv_val, "dn", STRLENOF( "dn" ) ) ) {
237 		bv.bv_val = in->bv_val + STRLENOF( "dn" );
238 
239 		if ( bv.bv_val[ 0 ] == '.' ) {
240 			bv.bv_val++;
241 
242 			if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
243 				bv.bv_val += STRLENOF( "exact:" );
244 				scope = LDAP_X_SCOPE_EXACT;
245 
246 			} else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
247 				bv.bv_val += STRLENOF( "regex:" );
248 				scope = LDAP_X_SCOPE_REGEX;
249 
250 			} else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
251 				bv.bv_val += STRLENOF( "children:" );
252 				scope = LDAP_X_SCOPE_CHILDREN;
253 
254 			} else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
255 				bv.bv_val += STRLENOF( "subtree:" );
256 				scope = LDAP_X_SCOPE_SUBTREE;
257 
258 			} else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
259 				bv.bv_val += STRLENOF( "onelevel:" );
260 				scope = LDAP_X_SCOPE_ONELEVEL;
261 
262 			} else {
263 				return LDAP_INVALID_SYNTAX;
264 			}
265 
266 		} else {
267 			if ( bv.bv_val[ 0 ] != ':' ) {
268 				return LDAP_INVALID_SYNTAX;
269 			}
270 			scope = LDAP_X_SCOPE_EXACT;
271 			bv.bv_val++;
272 		}
273 
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 );
278 
279 		/* a single '*' means any DN without using regexes */
280 		if ( ber_bvccmp( &bv, '*' ) ) {
281 			/* LDAP_X_SCOPE_USERS */
282 			return LDAP_SUCCESS;
283 		}
284 
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 );
291 
292 		case LDAP_X_SCOPE_REGEX:
293 			return LDAP_SUCCESS;
294 		}
295 
296 		return rc;
297 
298 	/*
299 	 * 4) u[.mech[/realm]]:<ID>
300 	 */
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 ] == '.' ) )
305 	{
306 		char		buf[ SLAP_LDAPDN_MAXLEN ];
307 		struct berval	id,
308 				user = BER_BVNULL,
309 				realm = BER_BVNULL,
310 				mech = BER_BVNULL;
311 
312 		if ( sizeof( buf ) <= in->bv_len ) {
313 			return LDAP_INVALID_SYNTAX;
314 		}
315 
316 		id.bv_len = in->bv_len;
317 		id.bv_val = buf;
318 		strncpy( buf, in->bv_val, sizeof( buf ) );
319 
320 		rc = slap_parse_user( &id, &user, &realm, &mech );
321 		if ( rc != LDAP_SUCCESS ) {
322 			return LDAP_INVALID_SYNTAX;
323 		}
324 
325 		return rc;
326 
327 	/*
328 	 * 5) group[/groupClass[/memberAttr]]:<DN>
329 	 *
330 	 * <groupClass> defaults to "groupOfNames"
331 	 * <memberAttr> defaults to "member"
332 	 *
333 	 * <DN> must pass DN normalization
334 	 */
335 	} else if ( strncasecmp( in->bv_val, "group", STRLENOF( "group" ) ) == 0 )
336 	{
337 		struct berval	group_dn = BER_BVNULL,
338 				group_oc = BER_BVNULL,
339 				member_at = BER_BVNULL;
340 
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;
349 		}
350 
351 		/*
352 		 * FIXME: we assume that "member" and "groupOfNames"
353 		 * are present in schema...
354 		 */
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;
358 
359 			member_at.bv_val = ber_bvchr( &group_oc, '/' );
360 			if ( member_at.bv_val ) {
361 				AttributeDescription	*ad = NULL;
362 				const char		*text = NULL;
363 
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;
370 				}
371 			}
372 
373 			if ( oc_bvfind( &group_oc ) == NULL ) {
374 				return LDAP_INVALID_SYNTAX;
375 			}
376 		}
377 
378 		group_dn.bv_val++;
379 		group_dn.bv_len = in->bv_len - ( group_dn.bv_val - in->bv_val );
380 
381 		rc = dnValidate( NULL, &group_dn );
382 		if ( rc != LDAP_SUCCESS ) {
383 			return rc;
384 		}
385 
386 		return rc;
387 	}
388 
389 	/*
390 	 * ldap:///<base>??<scope>?<filter>
391 	 * <scope> ::= {base|one|subtree}
392 	 *
393 	 * <scope> defaults to "base"
394 	 * <base> must pass DN normalization
395 	 * <filter> must pass str2filter()
396 	 */
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.
406 		 */
407 		if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
408 			/*
409 			 * must be ldap:///
410 			 */
411 			rc = LDAP_INVALID_SYNTAX;
412 			goto done;
413 		}
414 		break;
415 
416 	case LDAP_URL_ERR_BADSCHEME:
417 		/*
418 		 * last chance: assume it's a(n exact) DN ...
419 		 *
420 		 * NOTE: must pass DN normalization
421 		 */
422 		ldap_free_urldesc( ludp );
423 		bv.bv_val = in->bv_val;
424 		scope = LDAP_X_SCOPE_EXACT;
425 		goto is_dn;
426 
427 	default:
428 		rc = LDAP_INVALID_SYNTAX;
429 		goto done;
430 	}
431 
432 	if ( ( ludp->lud_host && *ludp->lud_host )
433 		|| ludp->lud_attrs || ludp->lud_exts )
434 	{
435 		/* host part must be empty */
436 		/* attrs and extensions parts must be empty */
437 		rc = LDAP_INVALID_SYNTAX;
438 		goto done;
439 	}
440 
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;
447 		}
448 		filter_free( f );
449 	}
450 
451 	/* Grab the searchbase */
452 	assert( ludp->lud_dn != NULL );
453 	ber_str2bv( ludp->lud_dn, 0, 0, &bv );
454 	rc = dnValidate( NULL, &bv );
455 
456 done:
457 	ldap_free_urldesc( ludp );
458 	return( rc );
459 }
460 
461 static int
462 authzPrettyNormal(
463 	struct berval	*val,
464 	struct berval	*normalized,
465 	void		*ctx,
466 	int		normalize )
467 {
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;
474 
475 	/*
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>
482 	 */
483 
484 	assert( val != NULL );
485 	assert( !BER_BVISNULL( val ) );
486 
487 	/*
488 	 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
489 	 * 3) dn.regex:<pattern>
490 	 *
491 	 * <DN> must pass DN normalization
492 	 */
493 	if ( !strncasecmp( val->bv_val, "dn", STRLENOF( "dn" ) ) ) {
494 		struct berval	out = BER_BVNULL,
495 				prefix = BER_BVNULL;
496 		char		*ptr;
497 
498 		bv.bv_val = val->bv_val + STRLENOF( "dn" );
499 
500 		if ( bv.bv_val[ 0 ] == '.' ) {
501 			bv.bv_val++;
502 
503 			if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
504 				bv.bv_val += STRLENOF( "exact:" );
505 				scope = LDAP_X_SCOPE_EXACT;
506 
507 			} else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
508 				bv.bv_val += STRLENOF( "regex:" );
509 				scope = LDAP_X_SCOPE_REGEX;
510 
511 			} else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
512 				bv.bv_val += STRLENOF( "children:" );
513 				scope = LDAP_X_SCOPE_CHILDREN;
514 
515 			} else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
516 				bv.bv_val += STRLENOF( "subtree:" );
517 				scope = LDAP_X_SCOPE_SUBTREE;
518 
519 			} else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
520 				bv.bv_val += STRLENOF( "onelevel:" );
521 				scope = LDAP_X_SCOPE_ONELEVEL;
522 
523 			} else {
524 				return LDAP_INVALID_SYNTAX;
525 			}
526 
527 		} else {
528 			if ( bv.bv_val[ 0 ] != ':' ) {
529 				return LDAP_INVALID_SYNTAX;
530 			}
531 			scope = LDAP_X_SCOPE_EXACT;
532 			bv.bv_val++;
533 		}
534 
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 );
539 
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;
544 		}
545 
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 );
555 			}
556 			if( rc != LDAP_SUCCESS ) {
557 				return LDAP_INVALID_SYNTAX;
558 			}
559 			break;
560 
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;
568 
569 		default:
570 			return LDAP_INVALID_SYNTAX;
571 		}
572 
573 		/* prepare prefix */
574 		switch ( scope ) {
575 		case LDAP_X_SCOPE_EXACT:
576 			BER_BVSTR( &prefix, "dn:" );
577 			break;
578 
579 		case LDAP_X_SCOPE_CHILDREN:
580 			BER_BVSTR( &prefix, "dn.children:" );
581 			break;
582 
583 		case LDAP_X_SCOPE_SUBTREE:
584 			BER_BVSTR( &prefix, "dn.subtree:" );
585 			break;
586 
587 		case LDAP_X_SCOPE_ONELEVEL:
588 			BER_BVSTR( &prefix, "dn.onelevel:" );
589 			break;
590 
591 		default:
592 			assert( 0 );
593 			break;
594 		}
595 
596 		normalized->bv_len = prefix.bv_len + out.bv_len;
597 		normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
598 
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 );
603 
604 		return LDAP_SUCCESS;
605 
606 	/*
607 	 * 4) u[.mech[/realm]]:<ID>
608 	 */
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 ] == '.' ) )
613 	{
614 		char		buf[ SLAP_LDAPDN_MAXLEN ];
615 		struct berval	id,
616 				user = BER_BVNULL,
617 				realm = BER_BVNULL,
618 				mech = BER_BVNULL;
619 
620 		if ( sizeof( buf ) <= val->bv_len ) {
621 			return LDAP_INVALID_SYNTAX;
622 		}
623 
624 		id.bv_len = val->bv_len;
625 		id.bv_val = buf;
626 		strncpy( buf, val->bv_val, sizeof( buf ) );
627 
628 		rc = slap_parse_user( &id, &user, &realm, &mech );
629 		if ( rc != LDAP_SUCCESS ) {
630 			return LDAP_INVALID_SYNTAX;
631 		}
632 
633 		ber_dupbv_x( normalized, val, ctx );
634 
635 		return rc;
636 
637 	/*
638 	 * 5) group[/groupClass[/memberAttr]]:<DN>
639 	 *
640 	 * <groupClass> defaults to "groupOfNames"
641 	 * <memberAttr> defaults to "member"
642 	 *
643 	 * <DN> must pass DN normalization
644 	 */
645 	} else if ( strncasecmp( val->bv_val, "group", STRLENOF( "group" ) ) == 0 )
646 	{
647 		struct berval	group_dn = BER_BVNULL,
648 				group_oc = BER_BVNULL,
649 				member_at = BER_BVNULL,
650 				out = BER_BVNULL;
651 		char		*ptr;
652 
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;
661 		}
662 
663 		/*
664 		 * FIXME: we assume that "member" and "groupOfNames"
665 		 * are present in schema...
666 		 */
667 		if ( bv.bv_val[ 0 ] == '/' ) {
668 			ObjectClass		*oc = NULL;
669 
670 			group_oc.bv_val = &bv.bv_val[ 1 ];
671 			group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
672 
673 			member_at.bv_val = ber_bvchr( &group_oc, '/' );
674 			if ( member_at.bv_val ) {
675 				AttributeDescription	*ad = NULL;
676 				const char		*text = NULL;
677 
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;
684 				}
685 
686 				member_at = ad->ad_cname;
687 
688 			}
689 
690 			oc = oc_bvfind( &group_oc );
691 			if ( oc == NULL ) {
692 				return LDAP_INVALID_SYNTAX;
693 			}
694 
695 			group_oc = oc->soc_cname;
696 		}
697 
698 		group_dn.bv_val++;
699 		group_dn.bv_len = val->bv_len - ( group_dn.bv_val - val->bv_val );
700 
701 		if ( normalize ) {
702 			rc = dnNormalize( 0, NULL, NULL, &group_dn, &out, ctx );
703 		} else {
704 			rc = dnPretty( NULL, &group_dn, &out, ctx );
705 		}
706 		if ( rc != LDAP_SUCCESS ) {
707 			return rc;
708 		}
709 
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;
715 			}
716 		}
717 
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 );
728 			}
729 		}
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 );
735 
736 		return rc;
737 	}
738 
739 	/*
740 	 * ldap:///<base>??<scope>?<filter>
741 	 * <scope> ::= {base|one|subtree}
742 	 *
743 	 * <scope> defaults to "base"
744 	 * <base> must pass DN normalization
745 	 * <filter> must pass str2filter()
746 	 */
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.
756 		 */
757 		if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
758 			/*
759 			 * must be ldap:///
760 			 */
761 			rc = LDAP_INVALID_SYNTAX;
762 			goto done;
763 		}
764 
765 		AC_MEMCPY( ludp->lud_scheme, "ldap", STRLENOF( "ldap" ) );
766 		break;
767 
768 	case LDAP_URL_ERR_BADSCHEME:
769 		/*
770 		 * last chance: assume it's a(n exact) DN ...
771 		 *
772 		 * NOTE: must pass DN normalization
773 		 */
774 		ldap_free_urldesc( ludp );
775 		bv.bv_val = val->bv_val;
776 		scope = LDAP_X_SCOPE_EXACT;
777 		goto is_dn;
778 
779 	default:
780 		rc = LDAP_INVALID_SYNTAX;
781 		goto done;
782 	}
783 
784 	if ( ( ludp->lud_host && *ludp->lud_host )
785 		|| ludp->lud_attrs || ludp->lud_exts )
786 	{
787 		/* host part must be empty */
788 		/* attrs and extensions parts must be empty */
789 		rc = LDAP_INVALID_SYNTAX;
790 		goto done;
791 	}
792 
793 	/* Grab the filter */
794 	if ( ludp->lud_filter ) {
795 		struct berval	filterstr;
796 		Filter		*f;
797 
798 		lud_filter = ludp->lud_filter;
799 
800 		f = str2filter( lud_filter );
801 		if ( f == NULL ) {
802 			rc = LDAP_INVALID_SYNTAX;
803 			goto done;
804 		}
805 		filter2bv( f, &filterstr );
806 		filter_free( f );
807 		if ( BER_BVISNULL( &filterstr ) ) {
808 			rc = LDAP_INVALID_SYNTAX;
809 			goto done;
810 		}
811 
812 		ludp->lud_filter = filterstr.bv_val;
813 	}
814 
815 	/* Grab the searchbase */
816 	assert( ludp->lud_dn != NULL );
817 	if ( ludp->lud_dn ) {
818 		struct berval	out = BER_BVNULL;
819 
820 		lud_dn = ludp->lud_dn;
821 
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 );
827 		}
828 
829 		if ( rc != LDAP_SUCCESS ) {
830 			goto done;
831 		}
832 
833 		ludp->lud_dn = out.bv_val;
834 	}
835 
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 );
840 
841 	} else {
842 		rc = LDAP_INVALID_SYNTAX;
843 	}
844 
845 done:
846 	if ( lud_filter ) {
847 		if ( ludp->lud_filter != lud_filter ) {
848 			ber_memfree( ludp->lud_filter );
849 		}
850 		ludp->lud_filter = lud_filter;
851 	}
852 
853 	if ( lud_dn ) {
854 		if ( ludp->lud_dn != lud_dn ) {
855 			ber_memfree( ludp->lud_dn );
856 		}
857 		ludp->lud_dn = lud_dn;
858 	}
859 
860 	ldap_free_urldesc( ludp );
861 
862 	return( rc );
863 }
864 
865 int
866 authzNormalize(
867 	slap_mask_t	usage,
868 	Syntax		*syntax,
869 	MatchingRule	*mr,
870 	struct berval	*val,
871 	struct berval	*normalized,
872 	void		*ctx )
873 {
874 	int		rc;
875 
876 	Debug( LDAP_DEBUG_TRACE, ">>> authzNormalize: <%s>\n",
877 		val->bv_val, 0, 0 );
878 
879 	rc = authzPrettyNormal( val, normalized, ctx, 1 );
880 
881 	Debug( LDAP_DEBUG_TRACE, "<<< authzNormalize: <%s> (%d)\n",
882 		normalized->bv_val, rc, 0 );
883 
884 	return rc;
885 }
886 
887 int
888 authzPretty(
889 	Syntax *syntax,
890 	struct berval *val,
891 	struct berval *out,
892 	void *ctx)
893 {
894 	int		rc;
895 
896 	Debug( LDAP_DEBUG_TRACE, ">>> authzPretty: <%s>\n",
897 		val->bv_val, 0, 0 );
898 
899 	rc = authzPrettyNormal( val, out, ctx, 0 );
900 
901 	Debug( LDAP_DEBUG_TRACE, "<<< authzPretty: <%s> (%d)\n",
902 		out->bv_val, rc, 0 );
903 
904 	return rc;
905 }
906 
907 
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 )
918 {
919 	struct berval	bv;
920 	int		rc;
921 	LDAPURLDesc	*ludp;
922 
923 	struct berval	idx;
924 
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;
931 
932 	Debug( LDAP_DEBUG_TRACE,
933 		"slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
934 
935 	rc = LDAP_PROTOCOL_ERROR;
936 
937 	idx = *uri;
938 	if ( idx.bv_val[ 0 ] == '{' ) {
939 		char	*ptr;
940 
941 		ptr = ber_bvchr( &idx, '}' ) + 1;
942 
943 		assert( ptr != (void *)1 );
944 
945 		idx.bv_len -= ptr - idx.bv_val;
946 		idx.bv_val = ptr;
947 		uri = &idx;
948 	}
949 
950 	/*
951 	 * dn[.<dnstyle>]:<dnpattern>
952 	 * <dnstyle> ::= {exact|regex|children|subtree|onelevel}
953 	 *
954 	 * <dnstyle> defaults to "exact"
955 	 * if <dnstyle> is not "regex", <dnpattern> must pass DN normalization
956 	 */
957 	if ( !strncasecmp( uri->bv_val, "dn", STRLENOF( "dn" ) ) ) {
958 		bv.bv_val = uri->bv_val + STRLENOF( "dn" );
959 
960 		if ( bv.bv_val[ 0 ] == '.' ) {
961 			bv.bv_val++;
962 
963 			if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
964 				bv.bv_val += STRLENOF( "exact:" );
965 				*scope = LDAP_X_SCOPE_EXACT;
966 
967 			} else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
968 				bv.bv_val += STRLENOF( "regex:" );
969 				*scope = LDAP_X_SCOPE_REGEX;
970 
971 			} else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
972 				bv.bv_val += STRLENOF( "children:" );
973 				*scope = LDAP_X_SCOPE_CHILDREN;
974 
975 			} else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
976 				bv.bv_val += STRLENOF( "subtree:" );
977 				*scope = LDAP_X_SCOPE_SUBTREE;
978 
979 			} else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
980 				bv.bv_val += STRLENOF( "onelevel:" );
981 				*scope = LDAP_X_SCOPE_ONELEVEL;
982 
983 			} else {
984 				return LDAP_PROTOCOL_ERROR;
985 			}
986 
987 		} else {
988 			if ( bv.bv_val[ 0 ] != ':' ) {
989 				return LDAP_PROTOCOL_ERROR;
990 			}
991 			*scope = LDAP_X_SCOPE_EXACT;
992 			bv.bv_val++;
993 		}
994 
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);
999 
1000 		/* a single '*' means any DN without using regexes */
1001 		if ( ber_bvccmp( &bv, '*' ) ) {
1002 			*scope = LDAP_X_SCOPE_USERS;
1003 		}
1004 
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;
1014 				}
1015 			} else {
1016 				ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1017 				rc = LDAP_SUCCESS;
1018 			}
1019 			break;
1020 
1021 		case LDAP_X_SCOPE_REGEX:
1022 			ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1023 
1024 		case LDAP_X_SCOPE_USERS:
1025 			rc = LDAP_SUCCESS;
1026 			break;
1027 
1028 		default:
1029 			*scope = -1;
1030 			break;
1031 		}
1032 
1033 		return rc;
1034 
1035 	/*
1036 	 * u:<uid>
1037 	 */
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 ] == '.' ) )
1042 	{
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;
1049 
1050 		if ( sizeof( buf ) <= uri->bv_len ) {
1051 			return LDAP_INVALID_SYNTAX;
1052 		}
1053 
1054 		id.bv_len = uri->bv_len;
1055 		id.bv_val = buf;
1056 		strncpy( buf, uri->bv_val, sizeof( buf ) );
1057 
1058 		rc = slap_parse_user( &id, &user, &realm, &mech );
1059 		if ( rc != LDAP_SUCCESS ) {
1060 			return rc;
1061 		}
1062 
1063 		if ( !BER_BVISNULL( &mech ) ) {
1064 			c.c_sasl_bind_mech = mech;
1065 		} else {
1066 			BER_BVSTR( &c.c_sasl_bind_mech, "AUTHZ" );
1067 		}
1068 
1069 		rc = slap_sasl_getdn( &c, op, &user,
1070 				realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
1071 
1072 		if ( rc == LDAP_SUCCESS ) {
1073 			*scope = LDAP_X_SCOPE_EXACT;
1074 		}
1075 
1076 		return rc;
1077 
1078 	/*
1079 	 * group[/<groupoc>[/<groupat>]]:<groupdn>
1080 	 *
1081 	 * groupoc defaults to "groupOfNames"
1082 	 * groupat defaults to "member"
1083 	 *
1084 	 * <groupdn> must pass DN normalization
1085 	 */
1086 	} else if ( strncasecmp( uri->bv_val, "group", STRLENOF( "group" ) ) == 0 )
1087 	{
1088 		struct berval	group_dn = BER_BVNULL,
1089 				group_oc = BER_BVNULL,
1090 				member_at = BER_BVNULL;
1091 		char		*tmp;
1092 
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;
1101 		}
1102 
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;
1106 
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;
1112 
1113 			} else {
1114 				BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
1115 			}
1116 
1117 		} else {
1118 			BER_BVSTR( &group_oc, SLAPD_GROUP_CLASS );
1119 			BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
1120 		}
1121 		group_dn.bv_val++;
1122 		group_dn.bv_len = uri->bv_len - ( group_dn.bv_val - uri->bv_val );
1123 
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;
1129 			}
1130 		} else {
1131 			ber_dupbv_x( nbase, &group_dn, op->o_tmpmemctx );
1132 			rc = LDAP_SUCCESS;
1133 		}
1134 		*scope = LDAP_X_SCOPE_GROUP;
1135 
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 );
1141 
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( "=" ) );
1149 
1150 		return rc;
1151 	}
1152 
1153 	/*
1154 	 * ldap:///<base>??<scope>?<filter>
1155 	 * <scope> ::= {base|one|subtree}
1156 	 *
1157 	 * <scope> defaults to "base"
1158 	 * <base> must pass DN normalization
1159 	 * <filter> must pass str2filter()
1160 	 */
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.
1170 		 */
1171 		if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
1172 			/*
1173 			 * must be ldap:///
1174 			 */
1175 			rc = LDAP_PROTOCOL_ERROR;
1176 			goto done;
1177 		}
1178 		break;
1179 
1180 	case LDAP_URL_ERR_BADSCHEME:
1181 		/*
1182 		 * last chance: assume it's a(n exact) DN ...
1183 		 *
1184 		 * NOTE: must pass DN normalization
1185 		 */
1186 		ldap_free_urldesc( ludp );
1187 		bv.bv_val = uri->bv_val;
1188 		*scope = LDAP_X_SCOPE_EXACT;
1189 		goto is_dn;
1190 
1191 	default:
1192 		rc = LDAP_PROTOCOL_ERROR;
1193 		goto done;
1194 	}
1195 
1196 	if ( ( ludp->lud_host && *ludp->lud_host )
1197 		|| ludp->lud_attrs || ludp->lud_exts )
1198 	{
1199 		/* host part must be empty */
1200 		/* attrs and extensions parts must be empty */
1201 		rc = LDAP_PROTOCOL_ERROR;
1202 		goto done;
1203 	}
1204 
1205 	/* Grab the scope */
1206 	*scope = ludp->lud_scope;
1207 
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;
1214 		}
1215 		ber_str2bv( ludp->lud_filter, 0, 0, fstr );
1216 	}
1217 
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;
1225 	}
1226 
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;
1236 	}
1237 
1238 	ldap_free_urldesc( ludp );
1239 	return( rc );
1240 }
1241 
1242 #ifndef SLAP_AUTH_REWRITE
1243 static int slap_sasl_rx_off(char *rep, int *off)
1244 {
1245 	const char *c;
1246 	int n;
1247 
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;
1255 		}
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 );
1262 
1263 				return( LDAP_OTHER );
1264 			}
1265 			off[n] = c - rep;
1266 			n++;
1267 		}
1268 	}
1269 
1270 	/* Final placeholder, after the last $n */
1271 	off[n] = c - rep;
1272 	n++;
1273 	off[n] = -1;
1274 	return( LDAP_SUCCESS );
1275 }
1276 #endif /* ! SLAP_AUTH_REWRITE */
1277 
1278 #ifdef SLAP_AUTH_REWRITE
1279 int slap_sasl_rewrite_config(
1280 		const char	*fname,
1281 		int		lineno,
1282 		int		argc,
1283 		char		**argv
1284 )
1285 {
1286 	int	rc;
1287 	char	*savearg0;
1288 
1289 	/* init at first call */
1290 	if ( sasl_rwinfo == NULL ) {
1291  		sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1292 	}
1293 
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;
1299 
1300 	return rc;
1301 }
1302 
1303 static int
1304 slap_sasl_rewrite_destroy( void )
1305 {
1306 	if ( sasl_rwinfo ) {
1307 		rewrite_info_delete( &sasl_rwinfo );
1308 		sasl_rwinfo = NULL;
1309 	}
1310 
1311 	return 0;
1312 }
1313 
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 )
1320 {
1321 	int	rc;
1322 	char	*argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL };
1323 
1324 	/* init at first call */
1325 	if ( sasl_rwinfo == NULL ) {
1326 		char *argvEngine[] = { "rewriteEngine", "on", NULL };
1327 		char *argvContext[] = { "rewriteContext", NULL, NULL };
1328 
1329 		/* initialize rewrite engine */
1330  		sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1331 
1332 		/* switch on rewrite engine */
1333  		rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine );
1334  		if (rc != LDAP_SUCCESS) {
1335 			return rc;
1336 		}
1337 
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;
1343 		}
1344 	}
1345 
1346 	argvRule[1] = (char *)match;
1347 	argvRule[2] = (char *)replace;
1348  	rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule );
1349 
1350 	return rc;
1351 }
1352 #endif /* SLAP_AUTH_REWRITE */
1353 
1354 int slap_sasl_regexp_config( const char *match, const char *replace )
1355 {
1356 	int rc;
1357 	SaslRegexp_t *reg;
1358 
1359 	SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
1360 	  (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
1361 
1362 	reg = &SaslRegexp[nSaslRegexp];
1363 
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 */
1368 
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 );
1375 
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 );
1380 		}
1381 #endif
1382 		return( LDAP_OTHER );
1383 	}
1384 
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 );
1390 
1391 		nSaslRegexp++;
1392 	}
1393 
1394 	return rc;
1395 }
1396 
1397 void
1398 slap_sasl_regexp_destroy( void )
1399 {
1400 	if ( SaslRegexp ) {
1401 		int	n;
1402 
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 */
1409 		}
1410 
1411 		ch_free( SaslRegexp );
1412 	}
1413 
1414 #ifdef SLAP_AUTH_REWRITE
1415 	slap_sasl_rewrite_destroy();
1416 #endif /* SLAP_AUTH_REWRITE */
1417 }
1418 
1419 void slap_sasl_regexp_unparse( BerVarray *out )
1420 {
1421 	int i;
1422 	BerVarray bva = NULL;
1423 	char ibuf[32], *ptr;
1424 	struct berval idx;
1425 
1426 	if ( !nSaslRegexp ) return;
1427 
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';
1443 	}
1444 	*out = bva;
1445 }
1446 
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 )
1456 {
1457 	int i, n, len, insert;
1458 
1459 	/* Get the total length of the final URI */
1460 
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;
1468 
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++;
1473 	}
1474 	out->bv_val = slap_sl_malloc( len + 1, ctx );
1475 	out->bv_len = len;
1476 
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;
1487 
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;
1493 
1494 		n++;
1495 	}
1496 
1497 	out->bv_val[insert] = '\0';
1498 }
1499 #endif /* ! SLAP_AUTH_REWRITE */
1500 
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) */
1504 
1505 static int slap_authz_regexp( struct berval *in, struct berval *out,
1506 		int flags, void *ctx )
1507 {
1508 #ifdef SLAP_AUTH_REWRITE
1509 	const char	*context = AUTHID_CONTEXT;
1510 
1511 	if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
1512 		return 0;
1513 	}
1514 
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 ) )
1519 	{
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 );
1526 			}
1527 		} else {
1528 			ber_dupbv_x( out, in, ctx );
1529 		}
1530 		Debug( LDAP_DEBUG_ARGS,
1531 			"[rw] %s: \"%s\" -> \"%s\"\n",
1532 			context, in->bv_val, out->bv_val );
1533 		return 1;
1534 
1535  	case REWRITE_REGEXEC_UNWILLING:
1536 	case REWRITE_REGEXEC_ERR:
1537 	default:
1538 		return 0;
1539 	}
1540 
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;
1546 
1547 	memset( out, 0, sizeof( *out ) );
1548 
1549 	Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
1550 	   saslname, 0, 0 );
1551 
1552 	if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
1553 		return( 0 );
1554 	}
1555 
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;
1561 	}
1562 
1563 	if( i >= nSaslRegexp ) return( 0 );
1564 
1565 	/*
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.*)
1569 	 */
1570 	slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
1571 		sr_strings, saslname, out, ctx );
1572 
1573 	Debug( LDAP_DEBUG_TRACE,
1574 		"slap_authz_regexp: converted SASL name to %s\n",
1575 		BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
1576 
1577 	return( 1 );
1578 #endif /* ! SLAP_AUTH_REWRITE */
1579 }
1580 
1581 /* This callback actually does some work...*/
1582 static int sasl_sc_sasl2dn( Operation *op, SlapReply *rs )
1583 {
1584 	struct berval *ndn = op->o_callback->sc_private;
1585 
1586 	if ( rs->sr_type != REP_SEARCH ) return LDAP_SUCCESS;
1587 
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 );
1592 
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 */
1597 	}
1598 
1599 	ber_dupbv_x( ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
1600 	return LDAP_SUCCESS;
1601 }
1602 
1603 
1604 typedef struct smatch_info {
1605 	struct berval *dn;
1606 	int match;
1607 } smatch_info;
1608 
1609 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
1610 {
1611 	smatch_info *sm = o->o_callback->sc_private;
1612 
1613 	if (rs->sr_type != REP_SEARCH) return 0;
1614 
1615 	if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
1616 		sm->match = 1;
1617 		return LDAP_UNAVAILABLE;	/* short-circuit the search */
1618 	}
1619 
1620 	return 0;
1621 }
1622 
1623 int
1624 slap_sasl_matches( Operation *op, BerVarray rules,
1625 		struct berval *assertDN, struct berval *authc )
1626 {
1627 	int	rc = LDAP_INAPPROPRIATE_AUTH;
1628 
1629 	if ( rules != NULL ) {
1630 		int	i;
1631 
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;
1635 		}
1636 	}
1637 
1638 	return rc;
1639 }
1640 
1641 /*
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.
1646  *
1647  * The assertDN should not have the dn: prefix
1648  */
1649 
1650 static int
1651 slap_sasl_match( Operation *opx, struct berval *rule,
1652 	struct berval *assertDN, struct berval *authc )
1653 {
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;
1661 
1662 	sm.dn = assertDN;
1663 	sm.match = 0;
1664 	cb.sc_private = &sm;
1665 
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 );
1669 
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 );
1673 
1674 	if( rc != LDAP_SUCCESS ) goto CONCLUDED;
1675 
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;
1683 		}
1684 		goto CONCLUDED;
1685 
1686 	case LDAP_X_SCOPE_CHILDREN:
1687 	case LDAP_X_SCOPE_SUBTREE:
1688 	case LDAP_X_SCOPE_ONELEVEL:
1689 	{
1690 		int	d = assertDN->bv_len - op.o_req_ndn.bv_len;
1691 
1692 		rc = LDAP_INAPPROPRIATE_AUTH;
1693 
1694 		if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
1695 			goto exact_match;
1696 
1697 		} else if ( d > 0 ) {
1698 			struct berval bv;
1699 
1700 			/* leave room for at least one char of attributeType,
1701 			 * one for '=' and one for ',' */
1702 			if ( d < STRLENOF( "x=,") ) {
1703 				goto CONCLUDED;
1704 			}
1705 
1706 			bv.bv_len = op.o_req_ndn.bv_len;
1707 			bv.bv_val = assertDN->bv_val + d;
1708 
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;
1715 
1716 				case LDAP_X_SCOPE_ONELEVEL:
1717 				{
1718 					struct berval	pdn;
1719 
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;
1727 					}
1728 					break;
1729 				}
1730 				default:
1731 					/* at present, impossible */
1732 					assert( 0 );
1733 				}
1734 			}
1735 		}
1736 		goto CONCLUDED;
1737 	}
1738 
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 );
1745 		}
1746 		if ( rc == 0 ) {
1747 			rc = LDAP_SUCCESS;
1748 		} else {
1749 			rc = LDAP_INAPPROPRIATE_AUTH;
1750 		}
1751 		goto CONCLUDED;
1752 
1753 	case LDAP_X_SCOPE_GROUP: {
1754 		char	*tmp;
1755 
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;
1765 		}
1766 		op.ors_filterstr.bv_val = tmp;
1767 
1768 		tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
1769 		tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
1770 
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;
1776 		}
1777 		op.ors_scope = LDAP_SCOPE_BASE;
1778 
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;
1782 
1783 		/* do the search */
1784 		break;
1785 		}
1786 
1787 	case LDAP_X_SCOPE_USERS:
1788 		if ( !BER_BVISEMPTY( assertDN ) ) {
1789 			rc = LDAP_SUCCESS;
1790 		} else {
1791 			rc = LDAP_INAPPROPRIATE_AUTH;
1792 		}
1793 		goto CONCLUDED;
1794 
1795 	default:
1796 		break;
1797 	}
1798 
1799 	/* Must run an internal search. */
1800 	if ( op.ors_filter == NULL ) {
1801 		rc = LDAP_FILTER_ERROR;
1802 		goto CONCLUDED;
1803 	}
1804 
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 );
1808 
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;
1813 	}
1814 
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 );
1827 	}
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;
1834 
1835 	op.o_bd->be_search( &op, &rs );
1836 
1837 	if (sm.match) {
1838 		rc = LDAP_SUCCESS;
1839 	} else {
1840 		rc = LDAP_INAPPROPRIATE_AUTH;
1841 	}
1842 
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 );
1848 
1849 	Debug( LDAP_DEBUG_TRACE,
1850 	   "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
1851 
1852 	return( rc );
1853 }
1854 
1855 
1856 /*
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.
1861  *
1862  * The DNs should not have the dn: prefix
1863  */
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 )
1870 {
1871 	int		rc,
1872 			do_not_cache = op->o_do_not_cache;
1873 	BerVarray	vals = NULL;
1874 
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);
1878 
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;
1884 
1885 	/* Check if the *assertDN matches any *vals */
1886 	rc = slap_sasl_matches( op, vals, assertDN, authc );
1887 
1888 COMPLETE:
1889 	if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1890 
1891 	Debug( LDAP_DEBUG_TRACE,
1892 	   "<==slap_sasl_check_authz: %s check returning %d\n",
1893 		ad->ad_cname.bv_val, rc, 0);
1894 
1895 	return( rc );
1896 }
1897 
1898 /*
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.
1905  */
1906 void
1907 slap_sasl2dn(
1908 	Operation	*opx,
1909 	struct berval	*saslname,
1910 	struct berval	*sasldn,
1911 	int		flags )
1912 {
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;
1919 
1920 	Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1921 		"converting SASL name %s to a DN\n",
1922 		saslname->bv_val, 0,0 );
1923 
1924 	BER_BVZERO( sasldn );
1925 	cb.sc_private = sasldn;
1926 
1927 	/* Convert the SASL name into a minimal URI */
1928 	if( !slap_authz_regexp( saslname, &regout, flags, opx->o_tmpmemctx ) ) {
1929 		goto FINISHED;
1930 	}
1931 
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;
1939 	}
1940 
1941 	/* Must do an internal search */
1942 	op.o_bd = select_backend( &op.o_req_ndn, 1 );
1943 
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 */
1949 
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;
1958 
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;
1965 
1966 	default:
1967 		/* catch unhandled cases (there shouldn't be) */
1968 		assert( 0 );
1969 	}
1970 
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 );
1974 
1975 	if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
1976 		goto FINISHED;
1977 	}
1978 
1979 	/* Must run an internal search. */
1980 	if ( op.ors_filter == NULL ) {
1981 		rc = LDAP_FILTER_ERROR;
1982 		goto FINISHED;
1983 	}
1984 
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 );
2002 	}
2003 	ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
2004 
2005 	op.o_bd->be_search( &op, &rs );
2006 
2007 FINISHED:
2008 	if( !BER_BVISEMPTY( sasldn ) ) {
2009 		opx->o_conn->c_authz_backend = op.o_bd;
2010 	}
2011 	if( !BER_BVISNULL( &op.o_req_dn ) ) {
2012 		slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
2013 	}
2014 	if( !BER_BVISNULL( &op.o_req_ndn ) ) {
2015 		slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
2016 	}
2017 	if( op.ors_filter ) {
2018 		filter_free_x( opx, op.ors_filter );
2019 	}
2020 	if( !BER_BVISNULL( &op.ors_filterstr ) ) {
2021 		ch_free( op.ors_filterstr.bv_val );
2022 	}
2023 
2024 	Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
2025 		!BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
2026 
2027 	return;
2028 }
2029 
2030 
2031 /* Check if a bind can SASL authorize to another identity.
2032  * The DNs should not have the dn: prefix
2033  */
2034 
2035 int slap_sasl_authorized( Operation *op,
2036 	struct berval *authcDN, struct berval *authzDN )
2037 {
2038 	int rc = LDAP_INAPPROPRIATE_AUTH;
2039 
2040 	/* User binding as anonymous */
2041 	if ( !authzDN || !authzDN->bv_len || !authzDN->bv_val ) {
2042 		rc = LDAP_SUCCESS;
2043 		goto DONE;
2044 	}
2045 
2046 	/* User is anonymous */
2047 	if ( !authcDN || !authcDN->bv_len || !authcDN->bv_val ) {
2048 		goto DONE;
2049 	}
2050 
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 );
2055 
2056 	/* If person is authorizing to self, succeed */
2057 	if ( dn_match( authcDN, authzDN ) ) {
2058 		rc = LDAP_SUCCESS;
2059 		goto DONE;
2060 	}
2061 
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 ))
2065 	{
2066 		rc = LDAP_SUCCESS;
2067 		goto DONE;
2068 	}
2069 
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;
2076 		}
2077 	}
2078 
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;
2085 		}
2086 	}
2087 
2088 	rc = LDAP_INAPPROPRIATE_AUTH;
2089 
2090 DONE:
2091 
2092 	Debug( LDAP_DEBUG_TRACE,
2093 		"<== slap_sasl_authorized: return %d\n", rc, 0, 0 );
2094 
2095 	return( rc );
2096 }
2097