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