xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/saslauthz.c (revision b7b7574d3bf8eeb51a1fa3977b59142ec6434a55)
1 /*	$NetBSD: saslauthz.c,v 1.1.1.4 2014/05/28 09:58:47 tron Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 1998-2014 The OpenLDAP Foundation.
7  * Portions Copyright 2000 Mark Adamson, Carnegie Mellon.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 
19 #include "portable.h"
20 
21 #include <stdio.h>
22 #ifdef HAVE_LIMITS_H
23 #include <limits.h>
24 #endif
25 
26 #include <ac/stdlib.h>
27 #include <ac/string.h>
28 #include <ac/ctype.h>
29 
30 #include "slap.h"
31 
32 #include "lutil.h"
33 
34 #define SASLREGEX_REPLACE 10
35 
36 #define LDAP_X_SCOPE_EXACT	((ber_int_t) 0x0010)
37 #define LDAP_X_SCOPE_REGEX	((ber_int_t) 0x0020)
38 #define LDAP_X_SCOPE_CHILDREN	((ber_int_t) 0x0030)
39 #define LDAP_X_SCOPE_SUBTREE	((ber_int_t) 0x0040)
40 #define LDAP_X_SCOPE_ONELEVEL	((ber_int_t) 0x0050)
41 #define LDAP_X_SCOPE_GROUP	((ber_int_t) 0x0060)
42 #define LDAP_X_SCOPE_USERS	((ber_int_t) 0x0070)
43 
44 /*
45  * IDs in DNauthzid form can now have a type specifier, that
46  * influences how they are used in related operations.
47  *
48  * syntax: dn[.{exact|regex}]:<val>
49  *
50  * dn.exact:	the value must pass normalization and is used
51  *		in exact DN match.
52  * dn.regex:	the value is treated as a regular expression
53  *		in matching DN values in authz{To|From}
54  *		attributes.
55  * dn:		for backwards compatibility reasons, the value
56  *		is treated as a regular expression, and thus
57  *		it is not normalized nor validated; it is used
58  *		in exact or regex comparisons based on the
59  *		context.
60  *
61  * IDs in DNauthzid form can now have a type specifier, that
62  * influences how they are used in related operations.
63  *
64  * syntax: u[.mech[/realm]]:<val>
65  *
66  * where mech is a SIMPLE, AUTHZ, or a SASL mechanism name
67  * and realm is mechanism specific realm (separate to those
68  * which are representable as part of the principal).
69  */
70 
71 typedef struct sasl_regexp {
72   char *sr_match;						/* regexp match pattern */
73   char *sr_replace; 					/* regexp replace pattern */
74   regex_t sr_workspace;					/* workspace for regexp engine */
75   int sr_offset[SASLREGEX_REPLACE+2];	/* offsets of $1,$2... in *replace */
76 } SaslRegexp_t;
77 
78 static int nSaslRegexp = 0;
79 static SaslRegexp_t *SaslRegexp = NULL;
80 
81 #ifdef SLAP_AUTH_REWRITE
82 #include "rewrite.h"
83 struct rewrite_info	*sasl_rwinfo = NULL;
84 #define AUTHID_CONTEXT	"authid"
85 #endif /* SLAP_AUTH_REWRITE */
86 
87 /* What SASL proxy authorization policies are allowed? */
88 #define	SASL_AUTHZ_NONE	0x00
89 #define	SASL_AUTHZ_FROM	0x01
90 #define	SASL_AUTHZ_TO	0x02
91 #define SASL_AUTHZ_AND	0x10
92 
93 static const char *policy_txt[] = {
94 	"none", "from", "to", "any"
95 };
96 
97 static int authz_policy = SASL_AUTHZ_NONE;
98 
99 static int
100 slap_sasl_match( Operation *opx, struct berval *rule,
101 	struct berval *assertDN, struct berval *authc );
102 
103 int slap_sasl_setpolicy( const char *arg )
104 {
105 	int rc = LDAP_SUCCESS;
106 
107 	if ( strcasecmp( arg, "none" ) == 0 ) {
108 		authz_policy = SASL_AUTHZ_NONE;
109 	} else if ( strcasecmp( arg, "from" ) == 0 ) {
110 		authz_policy = SASL_AUTHZ_FROM;
111 	} else if ( strcasecmp( arg, "to" ) == 0 ) {
112 		authz_policy = SASL_AUTHZ_TO;
113 	} else if ( strcasecmp( arg, "both" ) == 0 || strcasecmp( arg, "any" ) == 0 ) {
114 		authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO;
115 	} else if ( strcasecmp( arg, "all" ) == 0 ) {
116 		authz_policy = SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND;
117 	} else {
118 		rc = LDAP_OTHER;
119 	}
120 	return rc;
121 }
122 
123 const char * slap_sasl_getpolicy()
124 {
125 	if ( authz_policy == (SASL_AUTHZ_FROM | SASL_AUTHZ_TO | SASL_AUTHZ_AND) )
126 		return "all";
127 	else
128 		return policy_txt[authz_policy];
129 }
130 
131 int slap_parse_user( struct berval *id, struct berval *user,
132 		struct berval *realm, struct berval *mech )
133 {
134 	char	u;
135 
136 	assert( id != NULL );
137 	assert( !BER_BVISNULL( id ) );
138 	assert( user != NULL );
139 	assert( realm != NULL );
140 	assert( mech != NULL );
141 
142 	u = id->bv_val[ 0 ];
143 
144 	if ( u != 'u' && u != 'U' ) {
145 		/* called with something other than u: */
146 		return LDAP_PROTOCOL_ERROR;
147 	}
148 
149 	/* uauthzid form:
150 	 *		u[.mech[/realm]]:user
151 	 */
152 
153 	user->bv_val = ber_bvchr( id, ':' );
154 	if ( BER_BVISNULL( user ) ) {
155 		return LDAP_PROTOCOL_ERROR;
156 	}
157 	user->bv_val[ 0 ] = '\0';
158 	user->bv_val++;
159 	user->bv_len = id->bv_len - ( user->bv_val - id->bv_val );
160 
161 	mech->bv_val = ber_bvchr( id, '.' );
162 	if ( !BER_BVISNULL( mech ) ) {
163 		mech->bv_val[ 0 ] = '\0';
164 		mech->bv_val++;
165 		mech->bv_len = user->bv_val - mech->bv_val - 1;
166 
167 		realm->bv_val = ber_bvchr( mech, '/' );
168 
169 		if ( !BER_BVISNULL( realm ) ) {
170 			realm->bv_val[ 0 ] = '\0';
171 			realm->bv_val++;
172 			mech->bv_len = realm->bv_val - mech->bv_val - 1;
173 			realm->bv_len = user->bv_val - realm->bv_val - 1;
174 		}
175 
176 	} else {
177 		BER_BVZERO( realm );
178 	}
179 
180 	if ( id->bv_val[ 1 ] != '\0' ) {
181 		return LDAP_PROTOCOL_ERROR;
182 	}
183 
184 	if ( !BER_BVISNULL( mech ) ) {
185 		assert( mech->bv_val == id->bv_val + 2 );
186 
187 		AC_MEMCPY( mech->bv_val - 2, mech->bv_val, mech->bv_len + 1 );
188 		mech->bv_val -= 2;
189 	}
190 
191 	if ( !BER_BVISNULL( realm ) ) {
192 		assert( realm->bv_val >= id->bv_val + 2 );
193 
194 		AC_MEMCPY( realm->bv_val - 2, realm->bv_val, realm->bv_len + 1 );
195 		realm->bv_val -= 2;
196 	}
197 
198 	/* leave "u:" before user */
199 	user->bv_val -= 2;
200 	user->bv_len += 2;
201 	user->bv_val[ 0 ] = u;
202 	user->bv_val[ 1 ] = ':';
203 
204 	return LDAP_SUCCESS;
205 }
206 
207 int
208 authzValidate(
209 	Syntax *syntax,
210 	struct berval *in )
211 {
212 	struct berval	bv;
213 	int		rc = LDAP_INVALID_SYNTAX;
214 	LDAPURLDesc	*ludp = NULL;
215 	int		scope = -1;
216 
217 	/*
218 	 * 1) <DN>
219 	 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
220 	 * 3) dn.regex:<pattern>
221 	 * 4) u[.mech[/realm]]:<ID>
222 	 * 5) group[/<groupClass>[/<memberAttr>]]:<DN>
223 	 * 6) <URL>
224 	 */
225 
226 	assert( in != NULL );
227 	assert( !BER_BVISNULL( in ) );
228 
229 	Debug( LDAP_DEBUG_TRACE,
230 		"authzValidate: parsing %s\n", in->bv_val, 0, 0 );
231 
232 	/*
233 	 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
234 	 * 3) dn.regex:<pattern>
235 	 *
236 	 * <DN> must pass DN normalization
237 	 */
238 	if ( !strncasecmp( in->bv_val, "dn", STRLENOF( "dn" ) ) ) {
239 		bv.bv_val = in->bv_val + STRLENOF( "dn" );
240 
241 		if ( bv.bv_val[ 0 ] == '.' ) {
242 			bv.bv_val++;
243 
244 			if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
245 				bv.bv_val += STRLENOF( "exact:" );
246 				scope = LDAP_X_SCOPE_EXACT;
247 
248 			} else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
249 				bv.bv_val += STRLENOF( "regex:" );
250 				scope = LDAP_X_SCOPE_REGEX;
251 
252 			} else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
253 				bv.bv_val += STRLENOF( "children:" );
254 				scope = LDAP_X_SCOPE_CHILDREN;
255 
256 			} else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
257 				bv.bv_val += STRLENOF( "subtree:" );
258 				scope = LDAP_X_SCOPE_SUBTREE;
259 
260 			} else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
261 				bv.bv_val += STRLENOF( "onelevel:" );
262 				scope = LDAP_X_SCOPE_ONELEVEL;
263 
264 			} else {
265 				return LDAP_INVALID_SYNTAX;
266 			}
267 
268 		} else {
269 			if ( bv.bv_val[ 0 ] != ':' ) {
270 				return LDAP_INVALID_SYNTAX;
271 			}
272 			scope = LDAP_X_SCOPE_EXACT;
273 			bv.bv_val++;
274 		}
275 
276 		bv.bv_val += strspn( bv.bv_val, " " );
277 		/* jump here in case no type specification was present
278 		 * and uri was not an URI... HEADS-UP: assuming EXACT */
279 is_dn:		bv.bv_len = in->bv_len - ( bv.bv_val - in->bv_val );
280 
281 		/* a single '*' means any DN without using regexes */
282 		if ( ber_bvccmp( &bv, '*' ) ) {
283 			/* LDAP_X_SCOPE_USERS */
284 			return LDAP_SUCCESS;
285 		}
286 
287 		switch ( scope ) {
288 		case LDAP_X_SCOPE_EXACT:
289 		case LDAP_X_SCOPE_CHILDREN:
290 		case LDAP_X_SCOPE_SUBTREE:
291 		case LDAP_X_SCOPE_ONELEVEL:
292 			return dnValidate( NULL, &bv );
293 
294 		case LDAP_X_SCOPE_REGEX:
295 			return LDAP_SUCCESS;
296 		}
297 
298 		return rc;
299 
300 	/*
301 	 * 4) u[.mech[/realm]]:<ID>
302 	 */
303 	} else if ( ( in->bv_val[ 0 ] == 'u' || in->bv_val[ 0 ] == 'U' )
304 			&& ( in->bv_val[ 1 ] == ':'
305 				|| in->bv_val[ 1 ] == '/'
306 				|| in->bv_val[ 1 ] == '.' ) )
307 	{
308 		char		buf[ SLAP_LDAPDN_MAXLEN ];
309 		struct berval	id,
310 				user = BER_BVNULL,
311 				realm = BER_BVNULL,
312 				mech = BER_BVNULL;
313 
314 		if ( sizeof( buf ) <= in->bv_len ) {
315 			return LDAP_INVALID_SYNTAX;
316 		}
317 
318 		id.bv_len = in->bv_len;
319 		id.bv_val = buf;
320 		strncpy( buf, in->bv_val, sizeof( buf ) );
321 
322 		rc = slap_parse_user( &id, &user, &realm, &mech );
323 		if ( rc != LDAP_SUCCESS ) {
324 			return LDAP_INVALID_SYNTAX;
325 		}
326 
327 		return rc;
328 
329 	/*
330 	 * 5) group[/groupClass[/memberAttr]]:<DN>
331 	 *
332 	 * <groupClass> defaults to "groupOfNames"
333 	 * <memberAttr> defaults to "member"
334 	 *
335 	 * <DN> must pass DN normalization
336 	 */
337 	} else if ( strncasecmp( in->bv_val, "group", STRLENOF( "group" ) ) == 0 )
338 	{
339 		struct berval	group_dn = BER_BVNULL,
340 				group_oc = BER_BVNULL,
341 				member_at = BER_BVNULL;
342 
343 		bv.bv_val = in->bv_val + STRLENOF( "group" );
344 		bv.bv_len = in->bv_len - STRLENOF( "group" );
345 		group_dn.bv_val = ber_bvchr( &bv, ':' );
346 		if ( group_dn.bv_val == NULL ) {
347 			/* last chance: assume it's a(n exact) DN ... */
348 			bv.bv_val = in->bv_val;
349 			scope = LDAP_X_SCOPE_EXACT;
350 			goto is_dn;
351 		}
352 
353 		/*
354 		 * FIXME: we assume that "member" and "groupOfNames"
355 		 * are present in schema...
356 		 */
357 		if ( bv.bv_val[ 0 ] == '/' ) {
358 			group_oc.bv_val = &bv.bv_val[ 1 ];
359 			group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
360 
361 			member_at.bv_val = ber_bvchr( &group_oc, '/' );
362 			if ( member_at.bv_val ) {
363 				AttributeDescription	*ad = NULL;
364 				const char		*text = NULL;
365 
366 				group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
367 				member_at.bv_val++;
368 				member_at.bv_len = group_dn.bv_val - member_at.bv_val;
369 				rc = slap_bv2ad( &member_at, &ad, &text );
370 				if ( rc != LDAP_SUCCESS ) {
371 					return rc;
372 				}
373 			}
374 
375 			if ( oc_bvfind( &group_oc ) == NULL ) {
376 				return LDAP_INVALID_SYNTAX;
377 			}
378 		}
379 
380 		group_dn.bv_val++;
381 		group_dn.bv_len = in->bv_len - ( group_dn.bv_val - in->bv_val );
382 
383 		rc = dnValidate( NULL, &group_dn );
384 		if ( rc != LDAP_SUCCESS ) {
385 			return rc;
386 		}
387 
388 		return rc;
389 	}
390 
391 	/*
392 	 * ldap:///<base>??<scope>?<filter>
393 	 * <scope> ::= {base|one|subtree}
394 	 *
395 	 * <scope> defaults to "base"
396 	 * <base> must pass DN normalization
397 	 * <filter> must pass str2filter()
398 	 */
399 	rc = ldap_url_parse( in->bv_val, &ludp );
400 	switch ( rc ) {
401 	case LDAP_URL_SUCCESS:
402 		/* FIXME: the check is pedantic, but I think it's necessary,
403 		 * because people tend to use things like ldaps:// which
404 		 * gives the idea SSL is being used.  Maybe we could
405 		 * accept ldapi:// as well, but the point is that we use
406 		 * an URL as an easy means to define bits of a search with
407 		 * little parsing.
408 		 */
409 		if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
410 			/*
411 			 * must be ldap:///
412 			 */
413 			rc = LDAP_INVALID_SYNTAX;
414 			goto done;
415 		}
416 		break;
417 
418 	case LDAP_URL_ERR_BADSCHEME:
419 		/*
420 		 * last chance: assume it's a(n exact) DN ...
421 		 *
422 		 * NOTE: must pass DN normalization
423 		 */
424 		ldap_free_urldesc( ludp );
425 		bv.bv_val = in->bv_val;
426 		scope = LDAP_X_SCOPE_EXACT;
427 		goto is_dn;
428 
429 	default:
430 		rc = LDAP_INVALID_SYNTAX;
431 		goto done;
432 	}
433 
434 	if ( ( ludp->lud_host && *ludp->lud_host )
435 		|| ludp->lud_attrs || ludp->lud_exts )
436 	{
437 		/* host part must be empty */
438 		/* attrs and extensions parts must be empty */
439 		rc = LDAP_INVALID_SYNTAX;
440 		goto done;
441 	}
442 
443 	/* Grab the filter */
444 	if ( ludp->lud_filter ) {
445 		Filter	*f = str2filter( ludp->lud_filter );
446 		if ( f == NULL ) {
447 			rc = LDAP_INVALID_SYNTAX;
448 			goto done;
449 		}
450 		filter_free( f );
451 	}
452 
453 	/* Grab the searchbase */
454 	assert( ludp->lud_dn != NULL );
455 	ber_str2bv( ludp->lud_dn, 0, 0, &bv );
456 	rc = dnValidate( NULL, &bv );
457 
458 done:
459 	ldap_free_urldesc( ludp );
460 	return( rc );
461 }
462 
463 static int
464 authzPrettyNormal(
465 	struct berval	*val,
466 	struct berval	*normalized,
467 	void		*ctx,
468 	int		normalize )
469 {
470 	struct berval	bv;
471 	int		rc = LDAP_INVALID_SYNTAX;
472 	LDAPURLDesc	*ludp = NULL;
473 	char		*lud_dn = NULL,
474 			*lud_filter = NULL;
475 	int		scope = -1;
476 
477 	/*
478 	 * 1) <DN>
479 	 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
480 	 * 3) dn.regex:<pattern>
481 	 * 4) u[.mech[/realm]]:<ID>
482 	 * 5) group[/<groupClass>[/<memberAttr>]]:<DN>
483 	 * 6) <URL>
484 	 */
485 
486 	assert( val != NULL );
487 	assert( !BER_BVISNULL( val ) );
488 
489 	/*
490 	 * 2) dn[.{exact|children|subtree|onelevel}]:{*|<DN>}
491 	 * 3) dn.regex:<pattern>
492 	 *
493 	 * <DN> must pass DN normalization
494 	 */
495 	if ( !strncasecmp( val->bv_val, "dn", STRLENOF( "dn" ) ) ) {
496 		struct berval	out = BER_BVNULL,
497 				prefix = BER_BVNULL;
498 		char		*ptr;
499 
500 		bv.bv_val = val->bv_val + STRLENOF( "dn" );
501 
502 		if ( bv.bv_val[ 0 ] == '.' ) {
503 			bv.bv_val++;
504 
505 			if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
506 				bv.bv_val += STRLENOF( "exact:" );
507 				scope = LDAP_X_SCOPE_EXACT;
508 
509 			} else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
510 				bv.bv_val += STRLENOF( "regex:" );
511 				scope = LDAP_X_SCOPE_REGEX;
512 
513 			} else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
514 				bv.bv_val += STRLENOF( "children:" );
515 				scope = LDAP_X_SCOPE_CHILDREN;
516 
517 			} else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
518 				bv.bv_val += STRLENOF( "subtree:" );
519 				scope = LDAP_X_SCOPE_SUBTREE;
520 
521 			} else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
522 				bv.bv_val += STRLENOF( "onelevel:" );
523 				scope = LDAP_X_SCOPE_ONELEVEL;
524 
525 			} else {
526 				return LDAP_INVALID_SYNTAX;
527 			}
528 
529 		} else {
530 			if ( bv.bv_val[ 0 ] != ':' ) {
531 				return LDAP_INVALID_SYNTAX;
532 			}
533 			scope = LDAP_X_SCOPE_EXACT;
534 			bv.bv_val++;
535 		}
536 
537 		bv.bv_val += strspn( bv.bv_val, " " );
538 		/* jump here in case no type specification was present
539 		 * and uri was not an URI... HEADS-UP: assuming EXACT */
540 is_dn:		bv.bv_len = val->bv_len - ( bv.bv_val - val->bv_val );
541 
542 		/* a single '*' means any DN without using regexes */
543 		if ( ber_bvccmp( &bv, '*' ) ) {
544 			ber_str2bv_x( "dn:*", STRLENOF( "dn:*" ), 1, normalized, ctx );
545 			return LDAP_SUCCESS;
546 		}
547 
548 		switch ( scope ) {
549 		case LDAP_X_SCOPE_EXACT:
550 		case LDAP_X_SCOPE_CHILDREN:
551 		case LDAP_X_SCOPE_SUBTREE:
552 		case LDAP_X_SCOPE_ONELEVEL:
553 			if ( normalize ) {
554 				rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
555 			} else {
556 				rc = dnPretty( NULL, &bv, &out, ctx );
557 			}
558 			if( rc != LDAP_SUCCESS ) {
559 				return LDAP_INVALID_SYNTAX;
560 			}
561 			break;
562 
563 		case LDAP_X_SCOPE_REGEX:
564 			normalized->bv_len = STRLENOF( "dn.regex:" ) + bv.bv_len;
565 			normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
566 			ptr = lutil_strcopy( normalized->bv_val, "dn.regex:" );
567 			ptr = lutil_strncopy( ptr, bv.bv_val, bv.bv_len );
568 			ptr[ 0 ] = '\0';
569 			return LDAP_SUCCESS;
570 
571 		default:
572 			return LDAP_INVALID_SYNTAX;
573 		}
574 
575 		/* prepare prefix */
576 		switch ( scope ) {
577 		case LDAP_X_SCOPE_EXACT:
578 			BER_BVSTR( &prefix, "dn:" );
579 			break;
580 
581 		case LDAP_X_SCOPE_CHILDREN:
582 			BER_BVSTR( &prefix, "dn.children:" );
583 			break;
584 
585 		case LDAP_X_SCOPE_SUBTREE:
586 			BER_BVSTR( &prefix, "dn.subtree:" );
587 			break;
588 
589 		case LDAP_X_SCOPE_ONELEVEL:
590 			BER_BVSTR( &prefix, "dn.onelevel:" );
591 			break;
592 
593 		default:
594 			assert( 0 );
595 			break;
596 		}
597 
598 		normalized->bv_len = prefix.bv_len + out.bv_len;
599 		normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
600 
601 		ptr = lutil_strcopy( normalized->bv_val, prefix.bv_val );
602 		ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
603 		ptr[ 0 ] = '\0';
604 		ber_memfree_x( out.bv_val, ctx );
605 
606 		return LDAP_SUCCESS;
607 
608 	/*
609 	 * 4) u[.mech[/realm]]:<ID>
610 	 */
611 	} else if ( ( val->bv_val[ 0 ] == 'u' || val->bv_val[ 0 ] == 'U' )
612 			&& ( val->bv_val[ 1 ] == ':'
613 				|| val->bv_val[ 1 ] == '/'
614 				|| val->bv_val[ 1 ] == '.' ) )
615 	{
616 		char		buf[ SLAP_LDAPDN_MAXLEN ];
617 		struct berval	id,
618 				user = BER_BVNULL,
619 				realm = BER_BVNULL,
620 				mech = BER_BVNULL;
621 
622 		if ( sizeof( buf ) <= val->bv_len ) {
623 			return LDAP_INVALID_SYNTAX;
624 		}
625 
626 		id.bv_len = val->bv_len;
627 		id.bv_val = buf;
628 		strncpy( buf, val->bv_val, sizeof( buf ) );
629 
630 		rc = slap_parse_user( &id, &user, &realm, &mech );
631 		if ( rc != LDAP_SUCCESS ) {
632 			return LDAP_INVALID_SYNTAX;
633 		}
634 
635 		ber_dupbv_x( normalized, val, ctx );
636 
637 		return rc;
638 
639 	/*
640 	 * 5) group[/groupClass[/memberAttr]]:<DN>
641 	 *
642 	 * <groupClass> defaults to "groupOfNames"
643 	 * <memberAttr> defaults to "member"
644 	 *
645 	 * <DN> must pass DN normalization
646 	 */
647 	} else if ( strncasecmp( val->bv_val, "group", STRLENOF( "group" ) ) == 0 )
648 	{
649 		struct berval	group_dn = BER_BVNULL,
650 				group_oc = BER_BVNULL,
651 				member_at = BER_BVNULL,
652 				out = BER_BVNULL;
653 		char		*ptr;
654 
655 		bv.bv_val = val->bv_val + STRLENOF( "group" );
656 		bv.bv_len = val->bv_len - STRLENOF( "group" );
657 		group_dn.bv_val = ber_bvchr( &bv, ':' );
658 		if ( group_dn.bv_val == NULL ) {
659 			/* last chance: assume it's a(n exact) DN ... */
660 			bv.bv_val = val->bv_val;
661 			scope = LDAP_X_SCOPE_EXACT;
662 			goto is_dn;
663 		}
664 
665 		/*
666 		 * FIXME: we assume that "member" and "groupOfNames"
667 		 * are present in schema...
668 		 */
669 		if ( bv.bv_val[ 0 ] == '/' ) {
670 			ObjectClass		*oc = NULL;
671 
672 			group_oc.bv_val = &bv.bv_val[ 1 ];
673 			group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
674 
675 			member_at.bv_val = ber_bvchr( &group_oc, '/' );
676 			if ( member_at.bv_val ) {
677 				AttributeDescription	*ad = NULL;
678 				const char		*text = NULL;
679 
680 				group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
681 				member_at.bv_val++;
682 				member_at.bv_len = group_dn.bv_val - member_at.bv_val;
683 				rc = slap_bv2ad( &member_at, &ad, &text );
684 				if ( rc != LDAP_SUCCESS ) {
685 					return rc;
686 				}
687 
688 				member_at = ad->ad_cname;
689 
690 			}
691 
692 			oc = oc_bvfind( &group_oc );
693 			if ( oc == NULL ) {
694 				return LDAP_INVALID_SYNTAX;
695 			}
696 
697 			group_oc = oc->soc_cname;
698 		}
699 
700 		group_dn.bv_val++;
701 		group_dn.bv_len = val->bv_len - ( group_dn.bv_val - val->bv_val );
702 
703 		if ( normalize ) {
704 			rc = dnNormalize( 0, NULL, NULL, &group_dn, &out, ctx );
705 		} else {
706 			rc = dnPretty( NULL, &group_dn, &out, ctx );
707 		}
708 		if ( rc != LDAP_SUCCESS ) {
709 			return rc;
710 		}
711 
712 		normalized->bv_len = STRLENOF( "group" ":" ) + out.bv_len;
713 		if ( !BER_BVISNULL( &group_oc ) ) {
714 			normalized->bv_len += STRLENOF( "/" ) + group_oc.bv_len;
715 			if ( !BER_BVISNULL( &member_at ) ) {
716 				normalized->bv_len += STRLENOF( "/" ) + member_at.bv_len;
717 			}
718 		}
719 
720 		normalized->bv_val = ber_memalloc_x( normalized->bv_len + 1, ctx );
721 		ptr = lutil_strcopy( normalized->bv_val, "group" );
722 		if ( !BER_BVISNULL( &group_oc ) ) {
723 			ptr[ 0 ] = '/';
724 			ptr++;
725 			ptr = lutil_strncopy( ptr, group_oc.bv_val, group_oc.bv_len );
726 			if ( !BER_BVISNULL( &member_at ) ) {
727 				ptr[ 0 ] = '/';
728 				ptr++;
729 				ptr = lutil_strncopy( ptr, member_at.bv_val, member_at.bv_len );
730 			}
731 		}
732 		ptr[ 0 ] = ':';
733 		ptr++;
734 		ptr = lutil_strncopy( ptr, out.bv_val, out.bv_len );
735 		ptr[ 0 ] = '\0';
736 		ber_memfree_x( out.bv_val, ctx );
737 
738 		return rc;
739 	}
740 
741 	/*
742 	 * ldap:///<base>??<scope>?<filter>
743 	 * <scope> ::= {base|one|subtree}
744 	 *
745 	 * <scope> defaults to "base"
746 	 * <base> must pass DN normalization
747 	 * <filter> must pass str2filter()
748 	 */
749 	rc = ldap_url_parse( val->bv_val, &ludp );
750 	switch ( rc ) {
751 	case LDAP_URL_SUCCESS:
752 		/* FIXME: the check is pedantic, but I think it's necessary,
753 		 * because people tend to use things like ldaps:// which
754 		 * gives the idea SSL is being used.  Maybe we could
755 		 * accept ldapi:// as well, but the point is that we use
756 		 * an URL as an easy means to define bits of a search with
757 		 * little parsing.
758 		 */
759 		if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
760 			/*
761 			 * must be ldap:///
762 			 */
763 			rc = LDAP_INVALID_SYNTAX;
764 			goto done;
765 		}
766 
767 		AC_MEMCPY( ludp->lud_scheme, "ldap", STRLENOF( "ldap" ) );
768 		break;
769 
770 	case LDAP_URL_ERR_BADSCHEME:
771 		/*
772 		 * last chance: assume it's a(n exact) DN ...
773 		 *
774 		 * NOTE: must pass DN normalization
775 		 */
776 		ldap_free_urldesc( ludp );
777 		bv.bv_val = val->bv_val;
778 		scope = LDAP_X_SCOPE_EXACT;
779 		goto is_dn;
780 
781 	default:
782 		rc = LDAP_INVALID_SYNTAX;
783 		goto done;
784 	}
785 
786 	if ( ( ludp->lud_host && *ludp->lud_host )
787 		|| ludp->lud_attrs || ludp->lud_exts )
788 	{
789 		/* host part must be empty */
790 		/* attrs and extensions parts must be empty */
791 		rc = LDAP_INVALID_SYNTAX;
792 		goto done;
793 	}
794 
795 	/* Grab the filter */
796 	if ( ludp->lud_filter ) {
797 		struct berval	filterstr;
798 		Filter		*f;
799 
800 		lud_filter = ludp->lud_filter;
801 
802 		f = str2filter( lud_filter );
803 		if ( f == NULL ) {
804 			rc = LDAP_INVALID_SYNTAX;
805 			goto done;
806 		}
807 		filter2bv( f, &filterstr );
808 		filter_free( f );
809 		if ( BER_BVISNULL( &filterstr ) ) {
810 			rc = LDAP_INVALID_SYNTAX;
811 			goto done;
812 		}
813 
814 		ludp->lud_filter = filterstr.bv_val;
815 	}
816 
817 	/* Grab the searchbase */
818 	assert( ludp->lud_dn != NULL );
819 	if ( ludp->lud_dn ) {
820 		struct berval	out = BER_BVNULL;
821 
822 		lud_dn = ludp->lud_dn;
823 
824 		ber_str2bv( lud_dn, 0, 0, &bv );
825 		if ( normalize ) {
826 			rc = dnNormalize( 0, NULL, NULL, &bv, &out, ctx );
827 		} else {
828 			rc = dnPretty( NULL, &bv, &out, ctx );
829 		}
830 
831 		if ( rc != LDAP_SUCCESS ) {
832 			goto done;
833 		}
834 
835 		ludp->lud_dn = out.bv_val;
836 	}
837 
838 	ludp->lud_port = 0;
839 	normalized->bv_val = ldap_url_desc2str( ludp );
840 	if ( normalized->bv_val ) {
841 		normalized->bv_len = strlen( normalized->bv_val );
842 
843 	} else {
844 		rc = LDAP_INVALID_SYNTAX;
845 	}
846 
847 done:
848 	if ( lud_filter ) {
849 		if ( ludp->lud_filter != lud_filter ) {
850 			ber_memfree( ludp->lud_filter );
851 		}
852 		ludp->lud_filter = lud_filter;
853 	}
854 
855 	if ( lud_dn ) {
856 		if ( ludp->lud_dn != lud_dn ) {
857 			ber_memfree( ludp->lud_dn );
858 		}
859 		ludp->lud_dn = lud_dn;
860 	}
861 
862 	ldap_free_urldesc( ludp );
863 
864 	return( rc );
865 }
866 
867 int
868 authzNormalize(
869 	slap_mask_t	usage,
870 	Syntax		*syntax,
871 	MatchingRule	*mr,
872 	struct berval	*val,
873 	struct berval	*normalized,
874 	void		*ctx )
875 {
876 	int		rc;
877 
878 	Debug( LDAP_DEBUG_TRACE, ">>> authzNormalize: <%s>\n",
879 		val->bv_val, 0, 0 );
880 
881 	rc = authzPrettyNormal( val, normalized, ctx, 1 );
882 
883 	Debug( LDAP_DEBUG_TRACE, "<<< authzNormalize: <%s> (%d)\n",
884 		normalized->bv_val, rc, 0 );
885 
886 	return rc;
887 }
888 
889 int
890 authzPretty(
891 	Syntax *syntax,
892 	struct berval *val,
893 	struct berval *out,
894 	void *ctx)
895 {
896 	int		rc;
897 
898 	Debug( LDAP_DEBUG_TRACE, ">>> authzPretty: <%s>\n",
899 		val->bv_val, 0, 0 );
900 
901 	rc = authzPrettyNormal( val, out, ctx, 0 );
902 
903 	Debug( LDAP_DEBUG_TRACE, "<<< authzPretty: <%s> (%d)\n",
904 		out->bv_val, rc, 0 );
905 
906 	return rc;
907 }
908 
909 
910 static int
911 slap_parseURI(
912 	Operation	*op,
913 	struct berval	*uri,
914 	struct berval	*base,
915 	struct berval	*nbase,
916 	int		*scope,
917 	Filter		**filter,
918 	struct berval	*fstr,
919 	int		normalize )
920 {
921 	struct berval	bv;
922 	int		rc;
923 	LDAPURLDesc	*ludp;
924 
925 	struct berval	idx;
926 
927 	assert( uri != NULL && !BER_BVISNULL( uri ) );
928 	BER_BVZERO( base );
929 	BER_BVZERO( nbase );
930 	BER_BVZERO( fstr );
931 	*scope = -1;
932 	*filter = NULL;
933 
934 	Debug( LDAP_DEBUG_TRACE,
935 		"slap_parseURI: parsing %s\n", uri->bv_val, 0, 0 );
936 
937 	rc = LDAP_PROTOCOL_ERROR;
938 
939 	idx = *uri;
940 	if ( idx.bv_val[ 0 ] == '{' ) {
941 		char	*ptr;
942 
943 		ptr = ber_bvchr( &idx, '}' ) + 1;
944 
945 		assert( ptr != (void *)1 );
946 
947 		idx.bv_len -= ptr - idx.bv_val;
948 		idx.bv_val = ptr;
949 		uri = &idx;
950 	}
951 
952 	/*
953 	 * dn[.<dnstyle>]:<dnpattern>
954 	 * <dnstyle> ::= {exact|regex|children|subtree|onelevel}
955 	 *
956 	 * <dnstyle> defaults to "exact"
957 	 * if <dnstyle> is not "regex", <dnpattern> must pass DN normalization
958 	 */
959 	if ( !strncasecmp( uri->bv_val, "dn", STRLENOF( "dn" ) ) ) {
960 		bv.bv_val = uri->bv_val + STRLENOF( "dn" );
961 
962 		if ( bv.bv_val[ 0 ] == '.' ) {
963 			bv.bv_val++;
964 
965 			if ( !strncasecmp( bv.bv_val, "exact:", STRLENOF( "exact:" ) ) ) {
966 				bv.bv_val += STRLENOF( "exact:" );
967 				*scope = LDAP_X_SCOPE_EXACT;
968 
969 			} else if ( !strncasecmp( bv.bv_val, "regex:", STRLENOF( "regex:" ) ) ) {
970 				bv.bv_val += STRLENOF( "regex:" );
971 				*scope = LDAP_X_SCOPE_REGEX;
972 
973 			} else if ( !strncasecmp( bv.bv_val, "children:", STRLENOF( "children:" ) ) ) {
974 				bv.bv_val += STRLENOF( "children:" );
975 				*scope = LDAP_X_SCOPE_CHILDREN;
976 
977 			} else if ( !strncasecmp( bv.bv_val, "subtree:", STRLENOF( "subtree:" ) ) ) {
978 				bv.bv_val += STRLENOF( "subtree:" );
979 				*scope = LDAP_X_SCOPE_SUBTREE;
980 
981 			} else if ( !strncasecmp( bv.bv_val, "onelevel:", STRLENOF( "onelevel:" ) ) ) {
982 				bv.bv_val += STRLENOF( "onelevel:" );
983 				*scope = LDAP_X_SCOPE_ONELEVEL;
984 
985 			} else {
986 				return LDAP_PROTOCOL_ERROR;
987 			}
988 
989 		} else {
990 			if ( bv.bv_val[ 0 ] != ':' ) {
991 				return LDAP_PROTOCOL_ERROR;
992 			}
993 			*scope = LDAP_X_SCOPE_EXACT;
994 			bv.bv_val++;
995 		}
996 
997 		bv.bv_val += strspn( bv.bv_val, " " );
998 		/* jump here in case no type specification was present
999 		 * and uri was not an URI... HEADS-UP: assuming EXACT */
1000 is_dn:		bv.bv_len = uri->bv_len - (bv.bv_val - uri->bv_val);
1001 
1002 		/* a single '*' means any DN without using regexes */
1003 		if ( ber_bvccmp( &bv, '*' ) ) {
1004 			*scope = LDAP_X_SCOPE_USERS;
1005 		}
1006 
1007 		switch ( *scope ) {
1008 		case LDAP_X_SCOPE_EXACT:
1009 		case LDAP_X_SCOPE_CHILDREN:
1010 		case LDAP_X_SCOPE_SUBTREE:
1011 		case LDAP_X_SCOPE_ONELEVEL:
1012 			if ( normalize ) {
1013 				rc = dnNormalize( 0, NULL, NULL, &bv, nbase, op->o_tmpmemctx );
1014 				if( rc != LDAP_SUCCESS ) {
1015 					*scope = -1;
1016 				}
1017 			} else {
1018 				ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1019 				rc = LDAP_SUCCESS;
1020 			}
1021 			break;
1022 
1023 		case LDAP_X_SCOPE_REGEX:
1024 			ber_dupbv_x( nbase, &bv, op->o_tmpmemctx );
1025 
1026 		case LDAP_X_SCOPE_USERS:
1027 			rc = LDAP_SUCCESS;
1028 			break;
1029 
1030 		default:
1031 			*scope = -1;
1032 			break;
1033 		}
1034 
1035 		return rc;
1036 
1037 	/*
1038 	 * u:<uid>
1039 	 */
1040 	} else if ( ( uri->bv_val[ 0 ] == 'u' || uri->bv_val[ 0 ] == 'U' )
1041 			&& ( uri->bv_val[ 1 ] == ':'
1042 				|| uri->bv_val[ 1 ] == '/'
1043 				|| uri->bv_val[ 1 ] == '.' ) )
1044 	{
1045 		Connection	c = *op->o_conn;
1046 		char		buf[ SLAP_LDAPDN_MAXLEN ];
1047 		struct berval	id,
1048 				user = BER_BVNULL,
1049 				realm = BER_BVNULL,
1050 				mech = BER_BVNULL;
1051 
1052 		if ( sizeof( buf ) <= uri->bv_len ) {
1053 			return LDAP_INVALID_SYNTAX;
1054 		}
1055 
1056 		id.bv_len = uri->bv_len;
1057 		id.bv_val = buf;
1058 		strncpy( buf, uri->bv_val, sizeof( buf ) );
1059 
1060 		rc = slap_parse_user( &id, &user, &realm, &mech );
1061 		if ( rc != LDAP_SUCCESS ) {
1062 			return rc;
1063 		}
1064 
1065 		if ( !BER_BVISNULL( &mech ) ) {
1066 			c.c_sasl_bind_mech = mech;
1067 		} else {
1068 			BER_BVSTR( &c.c_sasl_bind_mech, "AUTHZ" );
1069 		}
1070 
1071 		rc = slap_sasl_getdn( &c, op, &user,
1072 				realm.bv_val, nbase, SLAP_GETDN_AUTHZID );
1073 
1074 		if ( rc == LDAP_SUCCESS ) {
1075 			*scope = LDAP_X_SCOPE_EXACT;
1076 		}
1077 
1078 		return rc;
1079 
1080 	/*
1081 	 * group[/<groupoc>[/<groupat>]]:<groupdn>
1082 	 *
1083 	 * groupoc defaults to "groupOfNames"
1084 	 * groupat defaults to "member"
1085 	 *
1086 	 * <groupdn> must pass DN normalization
1087 	 */
1088 	} else if ( strncasecmp( uri->bv_val, "group", STRLENOF( "group" ) ) == 0 )
1089 	{
1090 		struct berval	group_dn = BER_BVNULL,
1091 				group_oc = BER_BVNULL,
1092 				member_at = BER_BVNULL;
1093 		char		*tmp;
1094 
1095 		bv.bv_val = uri->bv_val + STRLENOF( "group" );
1096 		bv.bv_len = uri->bv_len - STRLENOF( "group" );
1097 		group_dn.bv_val = ber_bvchr( &bv, ':' );
1098 		if ( group_dn.bv_val == NULL ) {
1099 			/* last chance: assume it's a(n exact) DN ... */
1100 			bv.bv_val = uri->bv_val;
1101 			*scope = LDAP_X_SCOPE_EXACT;
1102 			goto is_dn;
1103 		}
1104 
1105 		if ( bv.bv_val[ 0 ] == '/' ) {
1106 			group_oc.bv_val = &bv.bv_val[ 1 ];
1107 			group_oc.bv_len = group_dn.bv_val - group_oc.bv_val;
1108 
1109 			member_at.bv_val = ber_bvchr( &group_oc, '/' );
1110 			if ( member_at.bv_val ) {
1111 				group_oc.bv_len = member_at.bv_val - group_oc.bv_val;
1112 				member_at.bv_val++;
1113 				member_at.bv_len = group_dn.bv_val - member_at.bv_val;
1114 
1115 			} else {
1116 				BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
1117 			}
1118 
1119 		} else {
1120 			BER_BVSTR( &group_oc, SLAPD_GROUP_CLASS );
1121 			BER_BVSTR( &member_at, SLAPD_GROUP_ATTR );
1122 		}
1123 		group_dn.bv_val++;
1124 		group_dn.bv_len = uri->bv_len - ( group_dn.bv_val - uri->bv_val );
1125 
1126 		if ( normalize ) {
1127 			rc = dnNormalize( 0, NULL, NULL, &group_dn, nbase, op->o_tmpmemctx );
1128 			if ( rc != LDAP_SUCCESS ) {
1129 				*scope = -1;
1130 				return rc;
1131 			}
1132 		} else {
1133 			ber_dupbv_x( nbase, &group_dn, op->o_tmpmemctx );
1134 			rc = LDAP_SUCCESS;
1135 		}
1136 		*scope = LDAP_X_SCOPE_GROUP;
1137 
1138 		/* FIXME: caller needs to add value of member attribute
1139 		 * and close brackets twice */
1140 		fstr->bv_len = STRLENOF( "(&(objectClass=)(=" /* )) */ )
1141 			+ group_oc.bv_len + member_at.bv_len;
1142 		fstr->bv_val = ch_malloc( fstr->bv_len + 1 );
1143 
1144 		tmp = lutil_strncopy( fstr->bv_val, "(&(objectClass=" /* )) */ ,
1145 				STRLENOF( "(&(objectClass=" /* )) */ ) );
1146 		tmp = lutil_strncopy( tmp, group_oc.bv_val, group_oc.bv_len );
1147 		tmp = lutil_strncopy( tmp, /* ( */ ")(" /* ) */ ,
1148 				STRLENOF( /* ( */ ")(" /* ) */ ) );
1149 		tmp = lutil_strncopy( tmp, member_at.bv_val, member_at.bv_len );
1150 		tmp = lutil_strncopy( tmp, "=", STRLENOF( "=" ) );
1151 
1152 		return rc;
1153 	}
1154 
1155 	/*
1156 	 * ldap:///<base>??<scope>?<filter>
1157 	 * <scope> ::= {base|one|subtree}
1158 	 *
1159 	 * <scope> defaults to "base"
1160 	 * <base> must pass DN normalization
1161 	 * <filter> must pass str2filter()
1162 	 */
1163 	rc = ldap_url_parse( uri->bv_val, &ludp );
1164 	switch ( rc ) {
1165 	case LDAP_URL_SUCCESS:
1166 		/* FIXME: the check is pedantic, but I think it's necessary,
1167 		 * because people tend to use things like ldaps:// which
1168 		 * gives the idea SSL is being used.  Maybe we could
1169 		 * accept ldapi:// as well, but the point is that we use
1170 		 * an URL as an easy means to define bits of a search with
1171 		 * little parsing.
1172 		 */
1173 		if ( strcasecmp( ludp->lud_scheme, "ldap" ) != 0 ) {
1174 			/*
1175 			 * must be ldap:///
1176 			 */
1177 			rc = LDAP_PROTOCOL_ERROR;
1178 			goto done;
1179 		}
1180 		break;
1181 
1182 	case LDAP_URL_ERR_BADSCHEME:
1183 		/*
1184 		 * last chance: assume it's a(n exact) DN ...
1185 		 *
1186 		 * NOTE: must pass DN normalization
1187 		 */
1188 		ldap_free_urldesc( ludp );
1189 		bv.bv_val = uri->bv_val;
1190 		*scope = LDAP_X_SCOPE_EXACT;
1191 		goto is_dn;
1192 
1193 	default:
1194 		rc = LDAP_PROTOCOL_ERROR;
1195 		goto done;
1196 	}
1197 
1198 	if ( ( ludp->lud_host && *ludp->lud_host )
1199 		|| ludp->lud_attrs || ludp->lud_exts )
1200 	{
1201 		/* host part must be empty */
1202 		/* attrs and extensions parts must be empty */
1203 		rc = LDAP_PROTOCOL_ERROR;
1204 		goto done;
1205 	}
1206 
1207 	/* Grab the scope */
1208 	*scope = ludp->lud_scope;
1209 
1210 	/* Grab the filter */
1211 	if ( ludp->lud_filter ) {
1212 		*filter = str2filter_x( op, ludp->lud_filter );
1213 		if ( *filter == NULL ) {
1214 			rc = LDAP_PROTOCOL_ERROR;
1215 			goto done;
1216 		}
1217 		ber_str2bv( ludp->lud_filter, 0, 0, fstr );
1218 	}
1219 
1220 	/* Grab the searchbase */
1221 	ber_str2bv( ludp->lud_dn, 0, 0, base );
1222 	if ( normalize ) {
1223 		rc = dnNormalize( 0, NULL, NULL, base, nbase, op->o_tmpmemctx );
1224 	} else {
1225 		ber_dupbv_x( nbase, base, op->o_tmpmemctx );
1226 		rc = LDAP_SUCCESS;
1227 	}
1228 
1229 done:
1230 	if( rc != LDAP_SUCCESS ) {
1231 		if( *filter ) filter_free_x( op, *filter, 1 );
1232 		BER_BVZERO( base );
1233 		BER_BVZERO( fstr );
1234 	} else {
1235 		/* Don't free these, return them to caller */
1236 		ludp->lud_filter = NULL;
1237 		ludp->lud_dn = NULL;
1238 	}
1239 
1240 	ldap_free_urldesc( ludp );
1241 	return( rc );
1242 }
1243 
1244 #ifndef SLAP_AUTH_REWRITE
1245 static int slap_sasl_rx_off(char *rep, int *off)
1246 {
1247 	const char *c;
1248 	int n;
1249 
1250 	/* Precompile replace pattern. Find the $<n> placeholders */
1251 	off[0] = -2;
1252 	n = 1;
1253 	for ( c = rep;	 *c;  c++ ) {
1254 		if ( *c == '\\' && c[1] ) {
1255 			c++;
1256 			continue;
1257 		}
1258 		if ( *c == '$' ) {
1259 			if ( n == SASLREGEX_REPLACE ) {
1260 				Debug( LDAP_DEBUG_ANY,
1261 					"SASL replace pattern %s has too many $n "
1262 						"placeholders (max %d)\n",
1263 					rep, SASLREGEX_REPLACE, 0 );
1264 
1265 				return( LDAP_OTHER );
1266 			}
1267 			off[n] = c - rep;
1268 			n++;
1269 		}
1270 	}
1271 
1272 	/* Final placeholder, after the last $n */
1273 	off[n] = c - rep;
1274 	n++;
1275 	off[n] = -1;
1276 	return( LDAP_SUCCESS );
1277 }
1278 #endif /* ! SLAP_AUTH_REWRITE */
1279 
1280 #ifdef SLAP_AUTH_REWRITE
1281 int slap_sasl_rewrite_config(
1282 		const char	*fname,
1283 		int		lineno,
1284 		int		argc,
1285 		char		**argv
1286 )
1287 {
1288 	int	rc;
1289 	char	*savearg0;
1290 
1291 	/* init at first call */
1292 	if ( sasl_rwinfo == NULL ) {
1293  		sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1294 	}
1295 
1296 	/* strip "authid-" prefix for parsing */
1297 	savearg0 = argv[0];
1298 	argv[0] += STRLENOF( "authid-" );
1299  	rc = rewrite_parse( sasl_rwinfo, fname, lineno, argc, argv );
1300 	argv[0] = savearg0;
1301 
1302 	return rc;
1303 }
1304 
1305 static int
1306 slap_sasl_rewrite_destroy( void )
1307 {
1308 	if ( sasl_rwinfo ) {
1309 		rewrite_info_delete( &sasl_rwinfo );
1310 		sasl_rwinfo = NULL;
1311 	}
1312 
1313 	return 0;
1314 }
1315 
1316 int slap_sasl_regexp_rewrite_config(
1317 		const char	*fname,
1318 		int		lineno,
1319 		const char	*match,
1320 		const char	*replace,
1321 		const char	*context )
1322 {
1323 	int	rc;
1324 	char	*argvRule[] = { "rewriteRule", NULL, NULL, ":@", NULL };
1325 
1326 	/* init at first call */
1327 	if ( sasl_rwinfo == NULL ) {
1328 		char *argvEngine[] = { "rewriteEngine", "on", NULL };
1329 		char *argvContext[] = { "rewriteContext", NULL, NULL };
1330 
1331 		/* initialize rewrite engine */
1332  		sasl_rwinfo = rewrite_info_init( REWRITE_MODE_USE_DEFAULT );
1333 
1334 		/* switch on rewrite engine */
1335  		rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvEngine );
1336  		if (rc != LDAP_SUCCESS) {
1337 			return rc;
1338 		}
1339 
1340 		/* create generic authid context */
1341 		argvContext[1] = AUTHID_CONTEXT;
1342  		rc = rewrite_parse( sasl_rwinfo, fname, lineno, 2, argvContext );
1343  		if (rc != LDAP_SUCCESS) {
1344 			return rc;
1345 		}
1346 	}
1347 
1348 	argvRule[1] = (char *)match;
1349 	argvRule[2] = (char *)replace;
1350  	rc = rewrite_parse( sasl_rwinfo, fname, lineno, 4, argvRule );
1351 
1352 	return rc;
1353 }
1354 #endif /* SLAP_AUTH_REWRITE */
1355 
1356 int slap_sasl_regexp_config( const char *match, const char *replace )
1357 {
1358 	int rc;
1359 	SaslRegexp_t *reg;
1360 
1361 	SaslRegexp = (SaslRegexp_t *) ch_realloc( (char *) SaslRegexp,
1362 	  (nSaslRegexp + 1) * sizeof(SaslRegexp_t) );
1363 
1364 	reg = &SaslRegexp[nSaslRegexp];
1365 
1366 #ifdef SLAP_AUTH_REWRITE
1367 	rc = slap_sasl_regexp_rewrite_config( "sasl-regexp", 0,
1368 			match, replace, AUTHID_CONTEXT );
1369 #else /* ! SLAP_AUTH_REWRITE */
1370 
1371 	/* Precompile matching pattern */
1372 	rc = regcomp( &reg->sr_workspace, match, REG_EXTENDED|REG_ICASE );
1373 	if ( rc ) {
1374 		Debug( LDAP_DEBUG_ANY,
1375 			"SASL match pattern %s could not be compiled by regexp engine\n",
1376 			match, 0, 0 );
1377 
1378 #ifdef ENABLE_REWRITE
1379 		/* Dummy block to force symbol references in librewrite */
1380 		if ( slapMode == ( SLAP_SERVER_MODE|SLAP_TOOL_MODE )) {
1381 			rewrite_info_init( 0 );
1382 		}
1383 #endif
1384 		return( LDAP_OTHER );
1385 	}
1386 
1387 	rc = slap_sasl_rx_off( replace, reg->sr_offset );
1388 #endif /* ! SLAP_AUTH_REWRITE */
1389 	if ( rc == LDAP_SUCCESS ) {
1390 		reg->sr_match = ch_strdup( match );
1391 		reg->sr_replace = ch_strdup( replace );
1392 
1393 		nSaslRegexp++;
1394 	}
1395 
1396 	return rc;
1397 }
1398 
1399 void
1400 slap_sasl_regexp_destroy( void )
1401 {
1402 	if ( SaslRegexp ) {
1403 		int	n;
1404 
1405 		for ( n = 0; n < nSaslRegexp; n++ ) {
1406 			ch_free( SaslRegexp[ n ].sr_match );
1407 			ch_free( SaslRegexp[ n ].sr_replace );
1408 #ifndef SLAP_AUTH_REWRITE
1409 			regfree( &SaslRegexp[ n ].sr_workspace );
1410 #endif /* SLAP_AUTH_REWRITE */
1411 		}
1412 
1413 		ch_free( SaslRegexp );
1414 	}
1415 
1416 #ifdef SLAP_AUTH_REWRITE
1417 	slap_sasl_rewrite_destroy();
1418 #endif /* SLAP_AUTH_REWRITE */
1419 }
1420 
1421 void slap_sasl_regexp_unparse( BerVarray *out )
1422 {
1423 	int i;
1424 	BerVarray bva = NULL;
1425 	char ibuf[32], *ptr;
1426 	struct berval idx;
1427 
1428 	if ( !nSaslRegexp ) return;
1429 
1430 	idx.bv_val = ibuf;
1431 	bva = ch_malloc( (nSaslRegexp+1) * sizeof(struct berval) );
1432 	BER_BVZERO(bva+nSaslRegexp);
1433 	for ( i=0; i<nSaslRegexp; i++ ) {
1434 		idx.bv_len = sprintf( idx.bv_val, "{%d}", i);
1435 		bva[i].bv_len = idx.bv_len + strlen( SaslRegexp[i].sr_match ) +
1436 			strlen( SaslRegexp[i].sr_replace ) + 5;
1437 		bva[i].bv_val = ch_malloc( bva[i].bv_len+1 );
1438 		ptr = lutil_strcopy( bva[i].bv_val, ibuf );
1439 		*ptr++ = '"';
1440 		ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_match );
1441 		ptr = lutil_strcopy( ptr, "\" \"" );
1442 		ptr = lutil_strcopy( ptr, SaslRegexp[i].sr_replace );
1443 		*ptr++ = '"';
1444 		*ptr = '\0';
1445 	}
1446 	*out = bva;
1447 }
1448 
1449 #ifndef SLAP_AUTH_REWRITE
1450 /* Perform replacement on regexp matches */
1451 static void slap_sasl_rx_exp(
1452 	const char *rep,
1453 	const int *off,
1454 	regmatch_t *str,
1455 	const char *saslname,
1456 	struct berval *out,
1457 	void *ctx )
1458 {
1459 	int i, n, len, insert;
1460 
1461 	/* Get the total length of the final URI */
1462 
1463 	n=1;
1464 	len = 0;
1465 	while( off[n] >= 0 ) {
1466 		/* Len of next section from replacement string (x,y,z above) */
1467 		len += off[n] - off[n-1] - 2;
1468 		if( off[n+1] < 0)
1469 			break;
1470 
1471 		/* Len of string from saslname that matched next $i  (b,d above) */
1472 		i = rep[ off[n] + 1 ]	- '0';
1473 		len += str[i].rm_eo - str[i].rm_so;
1474 		n++;
1475 	}
1476 	out->bv_val = slap_sl_malloc( len + 1, ctx );
1477 	out->bv_len = len;
1478 
1479 	/* Fill in URI with replace string, replacing $i as we go */
1480 	n=1;
1481 	insert = 0;
1482 	while( off[n] >= 0) {
1483 		/* Paste in next section from replacement string (x,y,z above) */
1484 		len = off[n] - off[n-1] - 2;
1485 		strncpy( out->bv_val+insert, rep + off[n-1] + 2, len);
1486 		insert += len;
1487 		if( off[n+1] < 0)
1488 			break;
1489 
1490 		/* Paste in string from saslname that matched next $i  (b,d above) */
1491 		i = rep[ off[n] + 1 ]	- '0';
1492 		len = str[i].rm_eo - str[i].rm_so;
1493 		strncpy( out->bv_val+insert, saslname + str[i].rm_so, len );
1494 		insert += len;
1495 
1496 		n++;
1497 	}
1498 
1499 	out->bv_val[insert] = '\0';
1500 }
1501 #endif /* ! SLAP_AUTH_REWRITE */
1502 
1503 /* Take the passed in SASL name and attempt to convert it into an
1504    LDAP URI to find the matching LDAP entry, using the pattern matching
1505    strings given in the saslregexp config file directive(s) */
1506 
1507 static int slap_authz_regexp( struct berval *in, struct berval *out,
1508 		int flags, void *ctx )
1509 {
1510 #ifdef SLAP_AUTH_REWRITE
1511 	const char	*context = AUTHID_CONTEXT;
1512 
1513 	if ( sasl_rwinfo == NULL || BER_BVISNULL( in ) ) {
1514 		return 0;
1515 	}
1516 
1517 	/* FIXME: if aware of authc/authz mapping,
1518 	 * we could use different contexts ... */
1519 	switch ( rewrite_session( sasl_rwinfo, context, in->bv_val, NULL,
1520 				&out->bv_val ) )
1521 	{
1522 	case REWRITE_REGEXEC_OK:
1523 		if ( !BER_BVISNULL( out ) ) {
1524 			char *val = out->bv_val;
1525 			ber_str2bv_x( val, 0, 1, out, ctx );
1526 			if ( val != in->bv_val ) {
1527 				free( val );
1528 			}
1529 		} else {
1530 			ber_dupbv_x( out, in, ctx );
1531 		}
1532 		Debug( LDAP_DEBUG_ARGS,
1533 			"[rw] %s: \"%s\" -> \"%s\"\n",
1534 			context, in->bv_val, out->bv_val );
1535 		return 1;
1536 
1537  	case REWRITE_REGEXEC_UNWILLING:
1538 	case REWRITE_REGEXEC_ERR:
1539 	default:
1540 		return 0;
1541 	}
1542 
1543 #else /* ! SLAP_AUTH_REWRITE */
1544 	char *saslname = in->bv_val;
1545 	SaslRegexp_t *reg;
1546   	regmatch_t sr_strings[SASLREGEX_REPLACE];	/* strings matching $1,$2 ... */
1547 	int i;
1548 
1549 	memset( out, 0, sizeof( *out ) );
1550 
1551 	Debug( LDAP_DEBUG_TRACE, "slap_authz_regexp: converting SASL name %s\n",
1552 	   saslname, 0, 0 );
1553 
1554 	if (( saslname == NULL ) || ( nSaslRegexp == 0 )) {
1555 		return( 0 );
1556 	}
1557 
1558 	/* Match the normalized SASL name to the saslregexp patterns */
1559 	for( reg = SaslRegexp,i=0;  i<nSaslRegexp;  i++,reg++ ) {
1560 		if ( regexec( &reg->sr_workspace, saslname, SASLREGEX_REPLACE,
1561 		  sr_strings, 0)  == 0 )
1562 			break;
1563 	}
1564 
1565 	if( i >= nSaslRegexp ) return( 0 );
1566 
1567 	/*
1568 	 * The match pattern may have been of the form "a(b.*)c(d.*)e" and the
1569 	 * replace pattern of the form "x$1y$2z". The returned string needs
1570 	 * to replace the $1,$2 with the strings that matched (b.*) and (d.*)
1571 	 */
1572 	slap_sasl_rx_exp( reg->sr_replace, reg->sr_offset,
1573 		sr_strings, saslname, out, ctx );
1574 
1575 	Debug( LDAP_DEBUG_TRACE,
1576 		"slap_authz_regexp: converted SASL name to %s\n",
1577 		BER_BVISEMPTY( out ) ? "" : out->bv_val, 0, 0 );
1578 
1579 	return( 1 );
1580 #endif /* ! SLAP_AUTH_REWRITE */
1581 }
1582 
1583 /* This callback actually does some work...*/
1584 static int sasl_sc_sasl2dn( Operation *op, SlapReply *rs )
1585 {
1586 	struct berval *ndn = op->o_callback->sc_private;
1587 
1588 	if ( rs->sr_type != REP_SEARCH ) return LDAP_SUCCESS;
1589 
1590 	/* We only want to be called once */
1591 	if ( !BER_BVISNULL( ndn ) ) {
1592 		op->o_tmpfree( ndn->bv_val, op->o_tmpmemctx );
1593 		BER_BVZERO( ndn );
1594 
1595 		Debug( LDAP_DEBUG_TRACE,
1596 			"%s: slap_sc_sasl2dn: search DN returned more than 1 entry\n",
1597 			op->o_log_prefix, 0, 0 );
1598 		return LDAP_UNAVAILABLE; /* short-circuit the search */
1599 	}
1600 
1601 	ber_dupbv_x( ndn, &rs->sr_entry->e_nname, op->o_tmpmemctx );
1602 	return LDAP_SUCCESS;
1603 }
1604 
1605 
1606 typedef struct smatch_info {
1607 	struct berval *dn;
1608 	int match;
1609 } smatch_info;
1610 
1611 static int sasl_sc_smatch( Operation *o, SlapReply *rs )
1612 {
1613 	smatch_info *sm = o->o_callback->sc_private;
1614 
1615 	if (rs->sr_type != REP_SEARCH) return 0;
1616 
1617 	if (dn_match(sm->dn, &rs->sr_entry->e_nname)) {
1618 		sm->match = 1;
1619 		return LDAP_UNAVAILABLE;	/* short-circuit the search */
1620 	}
1621 
1622 	return 0;
1623 }
1624 
1625 int
1626 slap_sasl_matches( Operation *op, BerVarray rules,
1627 		struct berval *assertDN, struct berval *authc )
1628 {
1629 	int	rc = LDAP_INAPPROPRIATE_AUTH;
1630 
1631 	if ( rules != NULL ) {
1632 		int	i;
1633 
1634 		for( i = 0; !BER_BVISNULL( &rules[i] ); i++ ) {
1635 			rc = slap_sasl_match( op, &rules[i], assertDN, authc );
1636 			if ( rc == LDAP_SUCCESS ) break;
1637 		}
1638 	}
1639 
1640 	return rc;
1641 }
1642 
1643 /*
1644  * Map a SASL regexp rule to a DN. If the rule is just a DN or a scope=base
1645  * URI, just strcmp the rule (or its searchbase) to the *assertDN. Otherwise,
1646  * the rule must be used as an internal search for entries. If that search
1647  * returns the *assertDN entry, the match is successful.
1648  *
1649  * The assertDN should not have the dn: prefix
1650  */
1651 
1652 static int
1653 slap_sasl_match( Operation *opx, struct berval *rule,
1654 	struct berval *assertDN, struct berval *authc )
1655 {
1656 	int rc;
1657 	regex_t reg;
1658 	smatch_info sm;
1659 	slap_callback cb = { NULL, sasl_sc_smatch, NULL, NULL };
1660 	Operation op = {0};
1661 	SlapReply rs = {REP_RESULT};
1662 	struct berval base = BER_BVNULL;
1663 
1664 	sm.dn = assertDN;
1665 	sm.match = 0;
1666 	cb.sc_private = &sm;
1667 
1668 	Debug( LDAP_DEBUG_TRACE,
1669 	   "===>slap_sasl_match: comparing DN %s to rule %s\n",
1670 		assertDN->bv_len ? assertDN->bv_val : "(null)", rule->bv_val, 0 );
1671 
1672 	/* NOTE: don't normalize rule if authz syntax is enabled */
1673 	rc = slap_parseURI( opx, rule, &base, &op.o_req_ndn,
1674 		&op.ors_scope, &op.ors_filter, &op.ors_filterstr, 0 );
1675 
1676 	if( rc != LDAP_SUCCESS ) goto CONCLUDED;
1677 
1678 	switch ( op.ors_scope ) {
1679 	case LDAP_X_SCOPE_EXACT:
1680 exact_match:
1681 		if ( dn_match( &op.o_req_ndn, assertDN ) ) {
1682 			rc = LDAP_SUCCESS;
1683 		} else {
1684 			rc = LDAP_INAPPROPRIATE_AUTH;
1685 		}
1686 		goto CONCLUDED;
1687 
1688 	case LDAP_X_SCOPE_CHILDREN:
1689 	case LDAP_X_SCOPE_SUBTREE:
1690 	case LDAP_X_SCOPE_ONELEVEL:
1691 	{
1692 		int	d = assertDN->bv_len - op.o_req_ndn.bv_len;
1693 
1694 		rc = LDAP_INAPPROPRIATE_AUTH;
1695 
1696 		if ( d == 0 && op.ors_scope == LDAP_X_SCOPE_SUBTREE ) {
1697 			goto exact_match;
1698 
1699 		} else if ( d > 0 ) {
1700 			struct berval bv;
1701 
1702 			/* leave room for at least one char of attributeType,
1703 			 * one for '=' and one for ',' */
1704 			if ( d < (int) STRLENOF( "x=,") ) {
1705 				goto CONCLUDED;
1706 			}
1707 
1708 			bv.bv_len = op.o_req_ndn.bv_len;
1709 			bv.bv_val = assertDN->bv_val + d;
1710 
1711 			if ( bv.bv_val[ -1 ] == ',' && dn_match( &op.o_req_ndn, &bv ) ) {
1712 				switch ( op.ors_scope ) {
1713 				case LDAP_X_SCOPE_SUBTREE:
1714 				case LDAP_X_SCOPE_CHILDREN:
1715 					rc = LDAP_SUCCESS;
1716 					break;
1717 
1718 				case LDAP_X_SCOPE_ONELEVEL:
1719 				{
1720 					struct berval	pdn;
1721 
1722 					dnParent( assertDN, &pdn );
1723 					/* the common portion of the DN
1724 					 * already matches, so only check
1725 					 * if parent DN of assertedDN
1726 					 * is all the pattern */
1727 					if ( pdn.bv_len == op.o_req_ndn.bv_len ) {
1728 						rc = LDAP_SUCCESS;
1729 					}
1730 					break;
1731 				}
1732 				default:
1733 					/* at present, impossible */
1734 					assert( 0 );
1735 				}
1736 			}
1737 		}
1738 		goto CONCLUDED;
1739 	}
1740 
1741 	case LDAP_X_SCOPE_REGEX:
1742 		rc = regcomp(&reg, op.o_req_ndn.bv_val,
1743 			REG_EXTENDED|REG_ICASE|REG_NOSUB);
1744 		if ( rc == 0 ) {
1745 			rc = regexec(&reg, assertDN->bv_val, 0, NULL, 0);
1746 			regfree( &reg );
1747 		}
1748 		if ( rc == 0 ) {
1749 			rc = LDAP_SUCCESS;
1750 		} else {
1751 			rc = LDAP_INAPPROPRIATE_AUTH;
1752 		}
1753 		goto CONCLUDED;
1754 
1755 	case LDAP_X_SCOPE_GROUP: {
1756 		char	*tmp;
1757 
1758 		/* Now filterstr looks like "(&(objectClass=<group_oc>)(<member_at>="
1759 		 * we need to append the <assertDN> so that the <group_dn> is searched
1760 		 * with scope "base", and the filter ensures that <assertDN> is
1761 		 * member of the group */
1762 		tmp = ch_realloc( op.ors_filterstr.bv_val, op.ors_filterstr.bv_len +
1763 			assertDN->bv_len + STRLENOF( /*"(("*/ "))" ) + 1 );
1764 		if ( tmp == NULL ) {
1765 			rc = LDAP_NO_MEMORY;
1766 			goto CONCLUDED;
1767 		}
1768 		op.ors_filterstr.bv_val = tmp;
1769 
1770 		tmp = lutil_strcopy( &tmp[op.ors_filterstr.bv_len], assertDN->bv_val );
1771 		tmp = lutil_strcopy( tmp, /*"(("*/ "))" );
1772 
1773 		/* pass opx because str2filter_x may (and does) use o_tmpmfuncs */
1774 		op.ors_filter = str2filter_x( opx, op.ors_filterstr.bv_val );
1775 		if ( op.ors_filter == NULL ) {
1776 			rc = LDAP_PROTOCOL_ERROR;
1777 			goto CONCLUDED;
1778 		}
1779 		op.ors_scope = LDAP_SCOPE_BASE;
1780 
1781 		/* hijack match DN: use that of the group instead of the assertDN;
1782 		 * assertDN is now in the filter */
1783 		sm.dn = &op.o_req_ndn;
1784 
1785 		/* do the search */
1786 		break;
1787 		}
1788 
1789 	case LDAP_X_SCOPE_USERS:
1790 		if ( !BER_BVISEMPTY( assertDN ) ) {
1791 			rc = LDAP_SUCCESS;
1792 		} else {
1793 			rc = LDAP_INAPPROPRIATE_AUTH;
1794 		}
1795 		goto CONCLUDED;
1796 
1797 	default:
1798 		break;
1799 	}
1800 
1801 	/* Must run an internal search. */
1802 	if ( op.ors_filter == NULL ) {
1803 		rc = LDAP_FILTER_ERROR;
1804 		goto CONCLUDED;
1805 	}
1806 
1807 	Debug( LDAP_DEBUG_TRACE,
1808 	   "slap_sasl_match: performing internal search (base=%s, scope=%d)\n",
1809 	   op.o_req_ndn.bv_val, op.ors_scope, 0 );
1810 
1811 	op.o_bd = select_backend( &op.o_req_ndn, 1 );
1812 	if(( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL)) {
1813 		rc = LDAP_INAPPROPRIATE_AUTH;
1814 		goto CONCLUDED;
1815 	}
1816 
1817 	op.o_hdr = opx->o_hdr;
1818 	op.o_tag = LDAP_REQ_SEARCH;
1819 	op.o_ndn = *authc;
1820 	op.o_callback = &cb;
1821 	slap_op_time( &op.o_time, &op.o_tincr );
1822 	op.o_do_not_cache = 1;
1823 	op.o_is_auth_check = 1;
1824 	/* use req_ndn as req_dn instead of non-pretty base of uri */
1825 	if( !BER_BVISNULL( &base ) ) {
1826 		ch_free( base.bv_val );
1827 		/* just in case... */
1828 		BER_BVZERO( &base );
1829 	}
1830 	ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
1831 	op.ors_deref = LDAP_DEREF_NEVER;
1832 	op.ors_slimit = 1;
1833 	op.ors_tlimit = SLAP_NO_LIMIT;
1834 	op.ors_attrs = slap_anlist_no_attrs;
1835 	op.ors_attrsonly = 1;
1836 
1837 	op.o_bd->be_search( &op, &rs );
1838 
1839 	if (sm.match) {
1840 		rc = LDAP_SUCCESS;
1841 	} else {
1842 		rc = LDAP_INAPPROPRIATE_AUTH;
1843 	}
1844 
1845 CONCLUDED:
1846 	if( !BER_BVISNULL( &op.o_req_dn ) ) slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
1847 	if( !BER_BVISNULL( &op.o_req_ndn ) ) slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
1848 	if( op.ors_filter ) filter_free_x( opx, op.ors_filter, 1 );
1849 	if( !BER_BVISNULL( &op.ors_filterstr ) ) ch_free( op.ors_filterstr.bv_val );
1850 
1851 	Debug( LDAP_DEBUG_TRACE,
1852 	   "<===slap_sasl_match: comparison returned %d\n", rc, 0, 0);
1853 
1854 	return( rc );
1855 }
1856 
1857 
1858 /*
1859  * This function answers the question, "Can this ID authorize to that ID?",
1860  * based on authorization rules. The rules are stored in the *searchDN, in the
1861  * attribute named by *attr. If any of those rules map to the *assertDN, the
1862  * authorization is approved.
1863  *
1864  * The DNs should not have the dn: prefix
1865  */
1866 static int
1867 slap_sasl_check_authz( Operation *op,
1868 	struct berval *searchDN,
1869 	struct berval *assertDN,
1870 	AttributeDescription *ad,
1871 	struct berval *authc )
1872 {
1873 	int		rc,
1874 			do_not_cache = op->o_do_not_cache;
1875 	BerVarray	vals = NULL;
1876 
1877 	Debug( LDAP_DEBUG_TRACE,
1878 	   "==>slap_sasl_check_authz: does %s match %s rule in %s?\n",
1879 	   assertDN->bv_val, ad->ad_cname.bv_val, searchDN->bv_val);
1880 
1881 	/* ITS#4760: don't cache group access */
1882 	op->o_do_not_cache = 1;
1883 	rc = backend_attribute( op, NULL, searchDN, ad, &vals, ACL_AUTH );
1884 	op->o_do_not_cache = do_not_cache;
1885 	if( rc != LDAP_SUCCESS ) goto COMPLETE;
1886 
1887 	/* Check if the *assertDN matches any *vals */
1888 	rc = slap_sasl_matches( op, vals, assertDN, authc );
1889 
1890 COMPLETE:
1891 	if( vals ) ber_bvarray_free_x( vals, op->o_tmpmemctx );
1892 
1893 	Debug( LDAP_DEBUG_TRACE,
1894 	   "<==slap_sasl_check_authz: %s check returning %d\n",
1895 		ad->ad_cname.bv_val, rc, 0);
1896 
1897 	return( rc );
1898 }
1899 
1900 /*
1901  * Given a SASL name (e.g. "UID=name,cn=REALM,cn=MECH,cn=AUTH")
1902  * return the LDAP DN to which it matches. The SASL regexp rules in the config
1903  * file turn the SASL name into an LDAP URI. If the URI is just a DN (or a
1904  * search with scope=base), just return the URI (or its searchbase). Otherwise
1905  * an internal search must be done, and if that search returns exactly one
1906  * entry, return the DN of that one entry.
1907  */
1908 void
1909 slap_sasl2dn(
1910 	Operation	*opx,
1911 	struct berval	*saslname,
1912 	struct berval	*sasldn,
1913 	int		flags )
1914 {
1915 	int rc;
1916 	slap_callback cb = { NULL, sasl_sc_sasl2dn, NULL, NULL };
1917 	Operation op = {0};
1918 	SlapReply rs = {REP_RESULT};
1919 	struct berval regout = BER_BVNULL;
1920 	struct berval base = BER_BVNULL;
1921 
1922 	Debug( LDAP_DEBUG_TRACE, "==>slap_sasl2dn: "
1923 		"converting SASL name %s to a DN\n",
1924 		saslname->bv_val, 0,0 );
1925 
1926 	BER_BVZERO( sasldn );
1927 	cb.sc_private = sasldn;
1928 
1929 	/* Convert the SASL name into a minimal URI */
1930 	if( !slap_authz_regexp( saslname, &regout, flags, opx->o_tmpmemctx ) ) {
1931 		goto FINISHED;
1932 	}
1933 
1934 	/* NOTE: always normalize regout because it results
1935 	 * from string submatch expansion */
1936 	rc = slap_parseURI( opx, &regout, &base, &op.o_req_ndn,
1937 		&op.ors_scope, &op.ors_filter, &op.ors_filterstr, 1 );
1938 	if ( !BER_BVISNULL( &regout ) ) slap_sl_free( regout.bv_val, opx->o_tmpmemctx );
1939 	if ( rc != LDAP_SUCCESS ) {
1940 		goto FINISHED;
1941 	}
1942 
1943 	/* Must do an internal search */
1944 	op.o_bd = select_backend( &op.o_req_ndn, 1 );
1945 
1946 	switch ( op.ors_scope ) {
1947 	case LDAP_X_SCOPE_EXACT:
1948 		*sasldn = op.o_req_ndn;
1949 		BER_BVZERO( &op.o_req_ndn );
1950 		/* intentionally continue to next case */
1951 
1952 	case LDAP_X_SCOPE_REGEX:
1953 	case LDAP_X_SCOPE_SUBTREE:
1954 	case LDAP_X_SCOPE_CHILDREN:
1955 	case LDAP_X_SCOPE_ONELEVEL:
1956 	case LDAP_X_SCOPE_GROUP:
1957 	case LDAP_X_SCOPE_USERS:
1958 		/* correctly parsed, but illegal */
1959 		goto FINISHED;
1960 
1961 	case LDAP_SCOPE_BASE:
1962 	case LDAP_SCOPE_ONELEVEL:
1963 	case LDAP_SCOPE_SUBTREE:
1964 	case LDAP_SCOPE_SUBORDINATE:
1965 		/* do a search */
1966 		break;
1967 
1968 	default:
1969 		/* catch unhandled cases (there shouldn't be) */
1970 		assert( 0 );
1971 	}
1972 
1973 	Debug( LDAP_DEBUG_TRACE,
1974 		"slap_sasl2dn: performing internal search (base=%s, scope=%d)\n",
1975 		op.o_req_ndn.bv_val, op.ors_scope, 0 );
1976 
1977 	if ( ( op.o_bd == NULL ) || ( op.o_bd->be_search == NULL) ) {
1978 		goto FINISHED;
1979 	}
1980 
1981 	/* Must run an internal search. */
1982 	if ( op.ors_filter == NULL ) {
1983 		rc = LDAP_FILTER_ERROR;
1984 		goto FINISHED;
1985 	}
1986 
1987 	op.o_hdr = opx->o_hdr;
1988 	op.o_tag = LDAP_REQ_SEARCH;
1989 	op.o_ndn = opx->o_conn->c_ndn;
1990 	op.o_callback = &cb;
1991 	slap_op_time( &op.o_time, &op.o_tincr );
1992 	op.o_do_not_cache = 1;
1993 	op.o_is_auth_check = 1;
1994 	op.ors_deref = LDAP_DEREF_NEVER;
1995 	op.ors_slimit = 1;
1996 	op.ors_tlimit = SLAP_NO_LIMIT;
1997 	op.ors_attrs = slap_anlist_no_attrs;
1998 	op.ors_attrsonly = 1;
1999 	/* use req_ndn as req_dn instead of non-pretty base of uri */
2000 	if( !BER_BVISNULL( &base ) ) {
2001 		ch_free( base.bv_val );
2002 		/* just in case... */
2003 		BER_BVZERO( &base );
2004 	}
2005 	ber_dupbv_x( &op.o_req_dn, &op.o_req_ndn, op.o_tmpmemctx );
2006 
2007 	op.o_bd->be_search( &op, &rs );
2008 
2009 FINISHED:
2010 	if( opx == opx->o_conn->c_sasl_bindop && !BER_BVISEMPTY( sasldn ) ) {
2011 		opx->o_conn->c_authz_backend = op.o_bd;
2012 	}
2013 	if( !BER_BVISNULL( &op.o_req_dn ) ) {
2014 		slap_sl_free( op.o_req_dn.bv_val, opx->o_tmpmemctx );
2015 	}
2016 	if( !BER_BVISNULL( &op.o_req_ndn ) ) {
2017 		slap_sl_free( op.o_req_ndn.bv_val, opx->o_tmpmemctx );
2018 	}
2019 	if( op.ors_filter ) {
2020 		filter_free_x( opx, op.ors_filter, 1 );
2021 	}
2022 	if( !BER_BVISNULL( &op.ors_filterstr ) ) {
2023 		ch_free( op.ors_filterstr.bv_val );
2024 	}
2025 
2026 	Debug( LDAP_DEBUG_TRACE, "<==slap_sasl2dn: Converted SASL name to %s\n",
2027 		!BER_BVISEMPTY( sasldn ) ? sasldn->bv_val : "<nothing>", 0, 0 );
2028 
2029 	return;
2030 }
2031 
2032 
2033 /* Check if a bind can SASL authorize to another identity.
2034  * The DNs should not have the dn: prefix
2035  */
2036 
2037 int slap_sasl_authorized( Operation *op,
2038 	struct berval *authcDN, struct berval *authzDN )
2039 {
2040 	int rc = LDAP_INAPPROPRIATE_AUTH;
2041 
2042 	/* User binding as anonymous */
2043 	if ( !authzDN || !authzDN->bv_len || !authzDN->bv_val ) {
2044 		rc = LDAP_SUCCESS;
2045 		goto DONE;
2046 	}
2047 
2048 	/* User is anonymous */
2049 	if ( !authcDN || !authcDN->bv_len || !authcDN->bv_val ) {
2050 		goto DONE;
2051 	}
2052 
2053 	Debug( LDAP_DEBUG_TRACE,
2054 	   "==>slap_sasl_authorized: can %s become %s?\n",
2055 		authcDN->bv_len ? authcDN->bv_val : "(null)",
2056 		authzDN->bv_len ? authzDN->bv_val : "(null)",  0 );
2057 
2058 	/* If person is authorizing to self, succeed */
2059 	if ( dn_match( authcDN, authzDN ) ) {
2060 		rc = LDAP_SUCCESS;
2061 		goto DONE;
2062 	}
2063 
2064 	/* Allow the manager to authorize as any DN. */
2065 	if( op->o_conn->c_authz_backend &&
2066 		be_isroot_dn( op->o_conn->c_authz_backend, authcDN ))
2067 	{
2068 		rc = LDAP_SUCCESS;
2069 		goto DONE;
2070 	}
2071 
2072 	/* Check source rules */
2073 	if( authz_policy & SASL_AUTHZ_TO ) {
2074 		rc = slap_sasl_check_authz( op, authcDN, authzDN,
2075 			slap_schema.si_ad_saslAuthzTo, authcDN );
2076 		if( rc == LDAP_SUCCESS && !(authz_policy & SASL_AUTHZ_AND) ) {
2077 			goto DONE;
2078 		}
2079 	}
2080 
2081 	/* Check destination rules */
2082 	if( authz_policy & SASL_AUTHZ_FROM ) {
2083 		rc = slap_sasl_check_authz( op, authzDN, authcDN,
2084 			slap_schema.si_ad_saslAuthzFrom, authcDN );
2085 		if( rc == LDAP_SUCCESS ) {
2086 			goto DONE;
2087 		}
2088 	}
2089 
2090 	rc = LDAP_INAPPROPRIATE_AUTH;
2091 
2092 DONE:
2093 
2094 	Debug( LDAP_DEBUG_TRACE,
2095 		"<== slap_sasl_authorized: return %d\n", rc, 0, 0 );
2096 
2097 	return( rc );
2098 }
2099