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