saslauthz.c revision 1.1.1.7
1/*	$NetBSD: saslauthz.c,v 1.1.1.7 2019/08/08 13:31:39 christos Exp $	*/
2
3/* $OpenLDAP$ */
4/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 1998-2019 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.7 2019/08/08 13:31:39 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
74typedef 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
81static int nSaslRegexp = 0;
82static SaslRegexp_t *SaslRegexp = NULL;
83
84#ifdef SLAP_AUTH_REWRITE
85#include "rewrite.h"
86struct 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
96static const char *policy_txt[] = {
97	"none", "from", "to", "any"
98};
99
100static int authz_policy = SASL_AUTHZ_NONE;
101
102static int
103slap_sasl_match( Operation *opx, struct berval *rule,
104	struct berval *assertDN, struct berval *authc );
105
106int 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
126const 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
134int 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
210int
211authzValidate(
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 */
282is_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
461done:
462	ldap_free_urldesc( ludp );
463	return( rc );
464}
465
466static int
467authzPrettyNormal(
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 */
543is_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
850done:
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
870int
871authzNormalize(
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
892int
893authzPretty(
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
913static int
914slap_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 */
1003is_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
1232done:
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
1251static 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
1287int 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
1311static int
1312slap_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
1322int 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
1362int 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
1405void
1406slap_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
1427void 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 */
1457static 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
1513static 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...*/
1590static 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
1612typedef struct smatch_info {
1613	struct berval *dn;
1614	int match;
1615} smatch_info;
1616
1617static 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
1631int
1632slap_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
1658static int
1659slap_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:
1686exact_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
1851CONCLUDED:
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 */
1872static int
1873slap_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
1896COMPLETE:
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 */
1914void
1915slap_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
2015FINISHED:
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
2043int 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 in its own DBs. */
2071	{
2072		Backend *zbe = select_backend( authzDN, 1 );
2073		if ( zbe && be_isroot_dn( zbe, authcDN )) {
2074			rc = LDAP_SUCCESS;
2075			goto DONE;
2076		}
2077	}
2078
2079	/* Check source rules */
2080	if( authz_policy & SASL_AUTHZ_TO ) {
2081		rc = slap_sasl_check_authz( op, authcDN, authzDN,
2082			slap_schema.si_ad_saslAuthzTo, authcDN );
2083		if(( rc == LDAP_SUCCESS ) ^ (( authz_policy & SASL_AUTHZ_AND) != 0)) {
2084			if( rc != LDAP_SUCCESS )
2085				rc = LDAP_INAPPROPRIATE_AUTH;
2086			goto DONE;
2087		}
2088	}
2089
2090	/* Check destination rules */
2091	if( authz_policy & SASL_AUTHZ_FROM ) {
2092		rc = slap_sasl_check_authz( op, authzDN, authcDN,
2093			slap_schema.si_ad_saslAuthzFrom, authcDN );
2094		if( rc == LDAP_SUCCESS ) {
2095			goto DONE;
2096		}
2097	}
2098
2099	rc = LDAP_INAPPROPRIATE_AUTH;
2100
2101DONE:
2102
2103	Debug( LDAP_DEBUG_TRACE,
2104		"<== slap_sasl_authorized: return %d\n", rc, 0, 0 );
2105
2106	return( rc );
2107}
2108