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