1/* chain.c - chain LDAP operations */
2/* $OpenLDAP$ */
3/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4 *
5 * Copyright 2003-2011 The OpenLDAP Foundation.
6 * Portions Copyright 2003 Howard Chu.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted only as authorized by the OpenLDAP
11 * Public License.
12 *
13 * A copy of this license is available in the file LICENSE in the
14 * top-level directory of the distribution or, alternatively, at
15 * <http://www.OpenLDAP.org/license.html>.
16 */
17/* ACKNOWLEDGEMENTS:
18 * This work was initially developed by the Howard Chu for inclusion
19 * in OpenLDAP Software.
20 * This work was subsequently modified by Pierangelo Masarati.
21 */
22
23#include "portable.h"
24
25#include <stdio.h>
26
27#include <ac/string.h>
28#include <ac/socket.h>
29
30#include "lutil.h"
31#include "slap.h"
32#include "back-ldap.h"
33#include "config.h"
34
35#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
36#define SLAP_CHAINING_DEFAULT				LDAP_CHAINING_PREFERRED
37#define SLAP_CH_RESOLVE_SHIFT				SLAP_CONTROL_SHIFT
38#define SLAP_CH_RESOLVE_MASK				(0x3 << SLAP_CH_RESOLVE_SHIFT)
39#define SLAP_CH_RESOLVE_CHAINING_PREFERRED		(LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
40#define SLAP_CH_RESOLVE_CHAINING_REQUIRED		(LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
41#define SLAP_CH_RESOLVE_REFERRALS_PREFERRED		(LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
42#define SLAP_CH_RESOLVE_REFERRALS_REQUIRED		(LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
43#define SLAP_CH_RESOLVE_DEFAULT				(SLAP_CHAINING_DEFAULT << SLAP_CH_RESOLVE_SHIFT)
44#define	SLAP_CH_CONTINUATION_SHIFT			(SLAP_CH_RESOLVE_SHIFT + 2)
45#define SLAP_CH_CONTINUATION_MASK			(0x3 << SLAP_CH_CONTINUATION_SHIFT)
46#define SLAP_CH_CONTINUATION_CHAINING_PREFERRED		(LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
47#define SLAP_CH_CONTINUATION_CHAINING_REQUIRED		(LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
48#define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED	(LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
49#define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED		(LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
50#define SLAP_CH_CONTINUATION_DEFAULT			(SLAP_CHAINING_DEFAULT << SLAP_CH_CONTINUATION_SHIFT)
51
52#define o_chaining			o_ctrlflag[sc_chainingBehavior]
53#define get_chaining(op)		((op)->o_chaining & SLAP_CONTROL_MASK)
54#define get_chainingBehavior(op)	((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))
55#define get_resolveBehavior(op)		((op)->o_chaining & SLAP_CH_RESOLVE_MASK)
56#define get_continuationBehavior(op)	((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)
57
58static int		sc_chainingBehavior;
59#endif /*  LDAP_CONTROL_X_CHAINING_BEHAVIOR */
60
61typedef enum {
62	LDAP_CH_NONE = 0,
63	LDAP_CH_RES,
64	LDAP_CH_ERR
65} ldap_chain_status_t;
66
67static BackendInfo	*lback;
68
69typedef struct ldap_chain_t {
70	/*
71	 * A "template" ldapinfo_t gets all common configuration items;
72	 * then, for each configured URI, an entry is created in the tree;
73	 * all the specific configuration items get in the current URI
74	 * structure.
75	 *
76 	 * Then, for each referral, extract the URI and lookup the
77	 * related structure.  If configured to do so, allow URIs
78	 * not found in the structure to create a temporary one
79	 * that chains anonymously; maybe it can also be added to
80	 * the tree?  Should be all configurable.
81	 */
82
83	/* "common" configuration info (anything occurring before an "uri") */
84	ldapinfo_t		*lc_common_li;
85
86	/* current configuration info */
87	ldapinfo_t		*lc_cfg_li;
88
89	/* tree of configured[/generated?] "uri" info */
90	ldap_avl_info_t		lc_lai;
91
92	/* max depth in nested referrals chaining */
93	int			lc_max_depth;
94
95	unsigned		lc_flags;
96#define LDAP_CHAIN_F_NONE		(0x00U)
97#define	LDAP_CHAIN_F_CHAINING		(0x01U)
98#define	LDAP_CHAIN_F_CACHE_URI		(0x02U)
99#define	LDAP_CHAIN_F_RETURN_ERR		(0x04U)
100
101#define LDAP_CHAIN_ISSET(lc, f)		( ( (lc)->lc_flags & (f) ) == (f) )
102#define	LDAP_CHAIN_CHAINING( lc )	LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CHAINING )
103#define	LDAP_CHAIN_CACHE_URI( lc )	LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CACHE_URI )
104#define	LDAP_CHAIN_RETURN_ERR( lc )	LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_RETURN_ERR )
105
106#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
107	LDAPControl		lc_chaining_ctrl;
108	char			lc_chaining_ctrlflag;
109#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
110} ldap_chain_t;
111
112static int ldap_chain_db_init_common( BackendDB	*be );
113static int ldap_chain_db_init_one( BackendDB *be );
114static int ldap_chain_db_open_one( BackendDB *be );
115#define	ldap_chain_db_close_one(be)	(0)
116#define	ldap_chain_db_destroy_one(be, rs)	(lback)->bi_db_destroy( (be), (rs) )
117
118typedef struct ldap_chain_cb_t {
119	ldap_chain_status_t	lb_status;
120	ldap_chain_t		*lb_lc;
121	BI_op_func		*lb_op_f;
122	int			lb_depth;
123} ldap_chain_cb_t;
124
125static int
126ldap_chain_op(
127	Operation	*op,
128	SlapReply	*rs,
129	BI_op_func	*op_f,
130	BerVarray	ref,
131	int		depth );
132
133static int
134ldap_chain_search(
135	Operation	*op,
136	SlapReply	*rs,
137	BerVarray	ref,
138	int		depth );
139
140static slap_overinst ldapchain;
141
142#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
143static int
144chaining_control_add(
145		ldap_chain_t	*lc,
146		Operation 	*op,
147		LDAPControl	***oldctrlsp )
148{
149	LDAPControl	**ctrls = NULL;
150	int		c = 0;
151
152	*oldctrlsp = op->o_ctrls;
153
154	/* default chaining control not defined */
155	if ( !LDAP_CHAIN_CHAINING( lc ) ) {
156		return 0;
157	}
158
159	/* already present */
160	if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
161		return 0;
162	}
163
164	/* FIXME: check other incompatibilities */
165
166	/* add to other controls */
167	if ( op->o_ctrls ) {
168		for ( c = 0; op->o_ctrls[ c ]; c++ )
169			/* count them */ ;
170	}
171
172	ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 );
173	ctrls[ 0 ] = &lc->lc_chaining_ctrl;
174	if ( op->o_ctrls ) {
175		for ( c = 0; op->o_ctrls[ c ]; c++ ) {
176			ctrls[ c + 1 ] = op->o_ctrls[ c ];
177		}
178	}
179	ctrls[ c + 1 ] = NULL;
180
181	op->o_ctrls = ctrls;
182
183	op->o_chaining = lc->lc_chaining_ctrlflag;
184
185	return 0;
186}
187
188static int
189chaining_control_remove(
190		Operation 	*op,
191		LDAPControl	***oldctrlsp )
192{
193	LDAPControl	**oldctrls = *oldctrlsp;
194
195	/* we assume that the first control is the chaining control
196	 * added by the chain overlay, so it's the only one we explicitly
197	 * free */
198	if ( op->o_ctrls != oldctrls ) {
199		assert( op->o_ctrls != NULL );
200		assert( op->o_ctrls[ 0 ] != NULL );
201
202		free( op->o_ctrls );
203
204		op->o_chaining = 0;
205		op->o_ctrls = oldctrls;
206	}
207
208	*oldctrlsp = NULL;
209
210	return 0;
211}
212#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
213
214static int
215ldap_chain_uri_cmp( const void *c1, const void *c2 )
216{
217	const ldapinfo_t	*li1 = (const ldapinfo_t *)c1;
218	const ldapinfo_t	*li2 = (const ldapinfo_t *)c2;
219
220	assert( li1->li_bvuri != NULL );
221	assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
222	assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
223
224	assert( li2->li_bvuri != NULL );
225	assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
226	assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
227
228	return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
229}
230
231static int
232ldap_chain_uri_dup( void *c1, void *c2 )
233{
234	ldapinfo_t	*li1 = (ldapinfo_t *)c1;
235	ldapinfo_t	*li2 = (ldapinfo_t *)c2;
236
237	assert( li1->li_bvuri != NULL );
238	assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
239	assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );
240
241	assert( li2->li_bvuri != NULL );
242	assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
243	assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );
244
245	if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
246		return -1;
247	}
248
249	return 0;
250}
251
252/*
253 * Search specific response that strips entryDN from entries
254 */
255static int
256ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
257{
258	ldap_chain_cb_t	*lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
259
260	assert( op->o_tag == LDAP_REQ_SEARCH );
261
262	/* if in error, don't proceed any further */
263	if ( lb->lb_status == LDAP_CH_ERR ) {
264		return 0;
265	}
266
267	if ( rs->sr_type == REP_SEARCH ) {
268		Attribute	**ap = &rs->sr_entry->e_attrs;
269
270		for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
271			/* will be generated later by frontend
272			 * (a cleaner solution would be that
273			 * the frontend checks if it already exists */
274			if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
275			{
276				Attribute *a = *ap;
277
278				*ap = (*ap)->a_next;
279				attr_free( a );
280
281				/* there SHOULD be one only! */
282				break;
283			}
284		}
285
286		/* tell the frontend not to add generated
287		 * operational attributes */
288		rs->sr_flags |= REP_NO_OPERATIONALS;
289
290		return SLAP_CB_CONTINUE;
291
292	} else if ( rs->sr_type == REP_SEARCHREF ) {
293		/* if we get it here, it means the library was unable
294		 * to chase the referral... */
295		if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
296			rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth );
297		}
298
299#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
300		if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
301			switch ( get_continuationBehavior( op ) ) {
302			case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
303				lb->lb_status = LDAP_CH_ERR;
304				return rs->sr_err = LDAP_X_CANNOT_CHAIN;
305
306			default:
307				break;
308			}
309		}
310#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
311		return SLAP_CB_CONTINUE;
312
313	} else if ( rs->sr_type == REP_RESULT ) {
314		if ( rs->sr_err == LDAP_REFERRAL
315			&& lb->lb_depth < lb->lb_lc->lc_max_depth
316			&& rs->sr_ref != NULL )
317		{
318			rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
319		}
320
321		/* back-ldap tried to send result */
322		lb->lb_status = LDAP_CH_RES;
323	}
324
325	return 0;
326}
327
328/*
329 * Dummy response that simply traces if back-ldap tried to send
330 * anything to the client
331 */
332static int
333ldap_chain_cb_response( Operation *op, SlapReply *rs )
334{
335	ldap_chain_cb_t	*lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
336
337	/* if in error, don't proceed any further */
338	if ( lb->lb_status == LDAP_CH_ERR ) {
339		return 0;
340	}
341
342	if ( rs->sr_type == REP_RESULT ) {
343retry:;
344		switch ( rs->sr_err ) {
345		case LDAP_COMPARE_TRUE:
346		case LDAP_COMPARE_FALSE:
347			if ( op->o_tag != LDAP_REQ_COMPARE ) {
348				return rs->sr_err;
349			}
350			/* fallthru */
351
352		case LDAP_SUCCESS:
353			lb->lb_status = LDAP_CH_RES;
354			break;
355
356		case LDAP_REFERRAL:
357			if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
358				rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_f, rs->sr_ref, lb->lb_depth );
359				goto retry;
360			}
361
362#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
363			if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
364				switch ( get_continuationBehavior( op ) ) {
365				case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
366					lb->lb_status = LDAP_CH_ERR;
367					return rs->sr_err = LDAP_X_CANNOT_CHAIN;
368
369				default:
370					break;
371				}
372			}
373#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
374			break;
375
376		default:
377			return rs->sr_err;
378		}
379
380	} else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
381	{
382		/* strip the entryDN attribute, but keep returning results */
383		(void)ldap_chain_cb_search_response( op, rs );
384	}
385
386	return SLAP_CB_CONTINUE;
387}
388
389static int
390ldap_chain_op(
391	Operation	*op,
392	SlapReply	*rs,
393	BI_op_func	*op_f,
394	BerVarray	ref,
395	int		depth )
396{
397	slap_overinst	*on = (slap_overinst *) op->o_bd->bd_info;
398	ldap_chain_cb_t	*lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
399	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
400	struct berval	odn = op->o_req_dn,
401			ondn = op->o_req_ndn;
402	ldapinfo_t	li = { 0 }, *lip = NULL;
403	struct berval	bvuri[ 2 ] = { { 0 } };
404
405	/* NOTE: returned if ref is empty... */
406	int		rc = LDAP_OTHER,
407			first_rc;
408
409#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
410	LDAPControl	**ctrls = NULL;
411
412	(void)chaining_control_add( lc, op, &ctrls );
413#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
414
415	li.li_bvuri = bvuri;
416	first_rc = -1;
417	for ( ; !BER_BVISNULL( ref ); ref++ ) {
418		SlapReply	rs2 = { 0 };
419		LDAPURLDesc	*srv = NULL;
420		req_search_s	save_oq_search = op->oq_search,
421				tmp_oq_search = { 0 };
422		struct berval	dn = BER_BVNULL,
423				pdn = odn,
424				ndn = ondn;
425		char		*filter = NULL;
426		int		temporary = 0;
427		int		free_dn = 0;
428
429		/* We're setting the URI of the first referral;
430		 * what if there are more?
431
432Document: RFC 4511
433
4344.1.10. Referral
435   ...
436   If the client wishes to progress the operation, it MUST follow the
437   referral by contacting one of the supported services. If multiple
438   URIs are present, the client assumes that any supported URI may be
439   used to progress the operation.
440
441		 * so we actually need to follow exactly one,
442		 * and we can assume any is fine.
443		 */
444
445		/* parse reference and use
446		 * proto://[host][:port]/ only */
447		rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
448		if ( rc != LDAP_URL_SUCCESS ) {
449			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: unable to parse ref=\"%s\"\n",
450				op->o_log_prefix, ref->bv_val, 0 );
451
452			/* try next */
453			rc = LDAP_OTHER;
454			continue;
455		}
456
457		if ( op->o_tag == LDAP_REQ_SEARCH ) {
458			if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
459				/* RFC 4511: if scope is present, use it */
460				tmp_oq_search.rs_scope = srv->lud_scope;
461
462			} else {
463				/* RFC 4511: if scope is absent, use original */
464				tmp_oq_search.rs_scope = op->ors_scope;
465			}
466		}
467
468		rc = LDAP_SUCCESS;
469		srv->lud_scope = LDAP_SCOPE_DEFAULT;
470		dn.bv_val = srv->lud_dn;
471		filter = srv->lud_filter;
472
473		/* normalize DN */
474		if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
475			if ( srv->lud_dn == NULL ) {
476				srv->lud_dn = "";
477			}
478
479		} else {
480			ber_str2bv( srv->lud_dn, 0, 0, &dn );
481			rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
482			if ( rc == LDAP_SUCCESS ) {
483				/* remove DN essentially because later on
484				 * ldap_initialize() will parse the URL
485				 * as a comma-separated URL list */
486				srv->lud_dn = "";
487				free_dn = 1;
488			}
489		}
490
491		/* prepare filter */
492		if ( rc == LDAP_SUCCESS && op->o_tag == LDAP_REQ_SEARCH ) {
493			/* filter */
494			if ( srv->lud_filter != NULL
495				&& srv->lud_filter[0] != '\0'
496				&& strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
497			{
498				/* RFC 4511: if filter is present, use it;
499				 * otherwise, use original */
500				tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
501				if ( tmp_oq_search.rs_filter != NULL ) {
502					filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
503
504				} else {
505					Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": unable to parse filter=\"%s\"\n",
506						op->o_log_prefix, ref->bv_val, srv->lud_filter );
507					rc = LDAP_OTHER;
508				}
509			}
510		}
511		srv->lud_filter = NULL;
512
513		if ( rc == LDAP_SUCCESS ) {
514			li.li_uri = ldap_url_desc2str( srv );
515		}
516
517		srv->lud_dn = dn.bv_val;
518		srv->lud_filter = filter;
519		ldap_free_urldesc( srv );
520
521		if ( rc != LDAP_SUCCESS ) {
522			/* try next */
523			rc = LDAP_OTHER;
524			continue;
525		}
526
527		if ( li.li_uri == NULL ) {
528			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to reconstruct URI\n",
529				op->o_log_prefix, ref->bv_val, 0 );
530
531			/* try next */
532			rc = LDAP_OTHER;
533			goto further_cleanup;
534		}
535
536		Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" -> \"%s\"\n",
537			op->o_log_prefix, ref->bv_val, li.li_uri );
538
539		op->o_req_dn = pdn;
540		op->o_req_ndn = ndn;
541
542		if ( op->o_tag == LDAP_REQ_SEARCH ) {
543			op->ors_scope = tmp_oq_search.rs_scope;
544			if ( tmp_oq_search.rs_filter != NULL ) {
545				op->ors_filter = tmp_oq_search.rs_filter;
546				op->ors_filterstr = tmp_oq_search.rs_filterstr;
547			}
548		}
549
550		ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
551
552		/* Searches for a ldapinfo in the avl tree */
553		ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
554		lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
555			(caddr_t)&li, ldap_chain_uri_cmp );
556		ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
557
558		if ( lip != NULL ) {
559			op->o_bd->be_private = (void *)lip;
560
561			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": URI=\"%s\" found in cache\n",
562				op->o_log_prefix, ref->bv_val, li.li_uri );
563
564		} else {
565			rc = ldap_chain_db_init_one( op->o_bd );
566			if ( rc != 0 ) {
567				Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
568					op->o_log_prefix, ref->bv_val, li.li_uri );
569				goto cleanup;
570			}
571			lip = (ldapinfo_t *)op->o_bd->be_private;
572			lip->li_uri = li.li_uri;
573			lip->li_bvuri = bvuri;
574			rc = ldap_chain_db_open_one( op->o_bd );
575			if ( rc != 0 ) {
576				Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
577					op->o_log_prefix, ref->bv_val, li.li_uri );
578				lip->li_uri = NULL;
579				lip->li_bvuri = NULL;
580				(void)ldap_chain_db_destroy_one( op->o_bd, NULL);
581				goto cleanup;
582			}
583
584			if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
585				ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
586				if ( avl_insert( &lc->lc_lai.lai_tree,
587					(caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
588				{
589					/* someone just inserted another;
590					 * don't bother, use this and then
591					 * just free it */
592					temporary = 1;
593				}
594				ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
595
596			} else {
597				temporary = 1;
598			}
599
600			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" %s\n",
601				op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
602		}
603
604		lb->lb_op_f = op_f;
605		lb->lb_depth = depth + 1;
606
607		rc = op_f( op, &rs2 );
608
609		/* note the first error */
610		if ( first_rc == -1 ) {
611			first_rc = rc;
612		}
613
614cleanup:;
615		ldap_memfree( li.li_uri );
616		li.li_uri = NULL;
617
618		if ( temporary ) {
619			lip->li_uri = NULL;
620			lip->li_bvuri = NULL;
621			(void)ldap_chain_db_close_one( op->o_bd );
622			(void)ldap_chain_db_destroy_one( op->o_bd, NULL );
623		}
624
625further_cleanup:;
626		if ( free_dn ) {
627			op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
628			op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
629		}
630
631		if ( op->o_tag == LDAP_REQ_SEARCH ) {
632			if ( tmp_oq_search.rs_filter != NULL ) {
633				filter_free_x( op, tmp_oq_search.rs_filter, 1 );
634			}
635
636			if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
637				slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
638			}
639
640			op->oq_search = save_oq_search;
641		}
642
643		if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
644			*rs = rs2;
645			break;
646		}
647
648		rc = rs2.sr_err;
649	}
650
651	op->o_req_dn = odn;
652	op->o_req_ndn = ondn;
653
654#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
655	(void)chaining_control_remove( op, &ctrls );
656#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
657
658	if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
659		rc = first_rc;
660	}
661
662	return rc;
663}
664
665static int
666ldap_chain_search(
667	Operation	*op,
668	SlapReply	*rs,
669	BerVarray	ref,
670	int		depth )
671
672{
673	slap_overinst	*on = (slap_overinst *) op->o_bd->bd_info;
674	ldap_chain_cb_t	*lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
675	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
676	ldapinfo_t	li = { 0 }, *lip = NULL;
677	struct berval	bvuri[ 2 ] = { { 0 } };
678
679	struct berval	odn = op->o_req_dn,
680			ondn = op->o_req_ndn;
681	Entry		*save_entry = rs->sr_entry;
682	slap_mask_t	save_flags = rs->sr_flags;
683
684	int		rc = LDAP_OTHER,
685			first_rc = -1;
686
687#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
688	LDAPControl	**ctrls = NULL;
689
690	(void)chaining_control_add( lc, op, &ctrls );
691#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
692
693	assert( rs->sr_type == REP_SEARCHREF );
694
695	rs->sr_type = REP_SEARCH;
696
697	/* if we parse the URI then by no means
698	 * we can cache stuff or reuse connections,
699	 * because in back-ldap there's no caching
700	 * based on the URI value, which is supposed
701	 * to be set once for all (correct?) */
702	li.li_bvuri = bvuri;
703	for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
704		SlapReply	rs2 = { REP_RESULT };
705		LDAPURLDesc	*srv;
706		req_search_s	save_oq_search = op->oq_search,
707				tmp_oq_search = { 0 };
708		struct berval	dn,
709				pdn = op->o_req_dn,
710				ndn = op->o_req_ndn;
711		char		*filter = NULL;
712		int		temporary = 0;
713		int		free_dn = 0;
714
715		/* parse reference and use
716		 * proto://[host][:port]/ only */
717		rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
718		if ( rc != LDAP_URL_SUCCESS ) {
719			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: unable to parse ref=\"%s\"\n",
720				op->o_log_prefix, ref->bv_val, 0 );
721
722			/* try next */
723			rs->sr_err = LDAP_OTHER;
724			continue;
725		}
726
727		if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
728			/* RFC 4511: if scope is present, use it */
729			tmp_oq_search.rs_scope = srv->lud_scope;
730
731		} else {
732			/* RFC 4511: if scope is absent, use original */
733			/* Section 4.5.3: if scope is onelevel, use base */
734			if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
735				tmp_oq_search.rs_scope = LDAP_SCOPE_BASE;
736			else
737				tmp_oq_search.rs_scope = op->ors_scope;
738		}
739
740		rc = LDAP_SUCCESS;
741		srv->lud_scope = LDAP_SCOPE_DEFAULT;
742		dn.bv_val = srv->lud_dn;
743		filter = srv->lud_filter;
744
745		/* normalize DN */
746		if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
747			if ( srv->lud_dn == NULL ) {
748				srv->lud_dn = "";
749			}
750
751			if ( save_entry != NULL ) {
752				/* use the "right" DN, if available */
753				pdn = save_entry->e_name;
754				ndn = save_entry->e_nname;
755			} /* else leave the original req DN in place, if any RFC 4511 */
756
757		} else {
758			/* RFC 4511: if DN is present, use it */
759			ber_str2bv( srv->lud_dn, 0, 0, &dn );
760			rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
761			if ( rc == LDAP_SUCCESS ) {
762				/* remove DN essentially because later on
763				 * ldap_initialize() will parse the URL
764				 * as a comma-separated URL list */
765				srv->lud_dn = "";
766				free_dn = 1;
767			}
768		}
769
770		/* prepare filter */
771		if ( rc == LDAP_SUCCESS ) {
772			/* filter */
773			if ( srv->lud_filter != NULL
774				&& srv->lud_filter[0] != '\0'
775				&& strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
776			{
777				/* RFC 4511: if filter is present, use it;
778				 * otherwise, use original */
779				tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
780				if ( tmp_oq_search.rs_filter != NULL ) {
781					filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );
782
783				} else {
784					Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": unable to parse filter=\"%s\"\n",
785						op->o_log_prefix, ref->bv_val, srv->lud_filter );
786					rc = LDAP_OTHER;
787				}
788			}
789		}
790		srv->lud_filter = NULL;
791
792		if ( rc == LDAP_SUCCESS ) {
793			li.li_uri = ldap_url_desc2str( srv );
794		}
795
796		srv->lud_dn = dn.bv_val;
797		srv->lud_filter = filter;
798		ldap_free_urldesc( srv );
799
800		if ( rc != LDAP_SUCCESS || li.li_uri == NULL ) {
801			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to reconstruct URI\n",
802				op->o_log_prefix, ref->bv_val, 0 );
803
804			/* try next */
805			rc = LDAP_OTHER;
806			goto further_cleanup;
807		}
808
809		Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" -> \"%s\"\n",
810			op->o_log_prefix, ref->bv_val, li.li_uri );
811
812		op->o_req_dn = pdn;
813		op->o_req_ndn = ndn;
814		op->ors_scope = tmp_oq_search.rs_scope;
815		if ( tmp_oq_search.rs_filter != NULL ) {
816			op->ors_filter = tmp_oq_search.rs_filter;
817			op->ors_filterstr = tmp_oq_search.rs_filterstr;
818		}
819
820		ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );
821
822		/* Searches for a ldapinfo in the avl tree */
823		ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
824		lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree,
825			(caddr_t)&li, ldap_chain_uri_cmp );
826		ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
827
828		if ( lip != NULL ) {
829			op->o_bd->be_private = (void *)lip;
830
831			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": URI=\"%s\" found in cache\n",
832				op->o_log_prefix, ref->bv_val, li.li_uri );
833
834		} else {
835			/* if none is found, create a temporary... */
836			rc = ldap_chain_db_init_one( op->o_bd );
837			if ( rc != 0 ) {
838				Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
839					op->o_log_prefix, ref->bv_val, li.li_uri );
840				goto cleanup;
841			}
842			lip = (ldapinfo_t *)op->o_bd->be_private;
843			lip->li_uri = li.li_uri;
844			lip->li_bvuri = bvuri;
845			rc = ldap_chain_db_open_one( op->o_bd );
846			if ( rc != 0 ) {
847				Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
848					op->o_log_prefix, ref->bv_val, li.li_uri );
849				lip->li_uri = NULL;
850				lip->li_bvuri = NULL;
851				(void)ldap_chain_db_destroy_one( op->o_bd, NULL );
852				goto cleanup;
853			}
854
855			if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
856				ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
857				if ( avl_insert( &lc->lc_lai.lai_tree,
858					(caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
859				{
860					/* someone just inserted another;
861					 * don't bother, use this and then
862					 * just free it */
863					temporary = 1;
864				}
865				ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
866
867			} else {
868				temporary = 1;
869			}
870
871			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" %s\n",
872				op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
873		}
874
875		lb->lb_op_f = lback->bi_op_search;
876		lb->lb_depth = depth + 1;
877
878		/* FIXME: should we also copy filter and scope?
879		 * according to RFC3296, no */
880		rc = lback->bi_op_search( op, &rs2 );
881		if ( first_rc == -1 ) {
882			first_rc = rc;
883		}
884
885cleanup:;
886		ldap_memfree( li.li_uri );
887		li.li_uri = NULL;
888
889		if ( temporary ) {
890			lip->li_uri = NULL;
891			lip->li_bvuri = NULL;
892			(void)ldap_chain_db_close_one( op->o_bd );
893			(void)ldap_chain_db_destroy_one( op->o_bd, NULL );
894		}
895
896further_cleanup:;
897		if ( free_dn ) {
898			op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
899			op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
900		}
901
902		op->o_req_dn = odn;
903		op->o_req_ndn = ondn;
904
905		if ( tmp_oq_search.rs_filter != NULL ) {
906			filter_free_x( op, tmp_oq_search.rs_filter, 1 );
907		}
908
909		if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
910			slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
911		}
912
913		op->oq_search = save_oq_search;
914
915		if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
916			*rs = rs2;
917			break;
918		}
919
920		rc = rs2.sr_err;
921	}
922
923#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
924	(void)chaining_control_remove( op, &ctrls );
925#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
926
927	op->o_req_dn = odn;
928	op->o_req_ndn = ondn;
929	rs->sr_type = REP_SEARCHREF;
930	rs->sr_entry = save_entry;
931	rs->sr_flags = save_flags;
932
933	if ( rc != LDAP_SUCCESS ) {
934		/* couldn't chase any of the referrals */
935		if ( first_rc != -1 ) {
936			rc = first_rc;
937
938		} else {
939			rc = SLAP_CB_CONTINUE;
940		}
941	}
942
943	return rc;
944}
945
946static int
947ldap_chain_response( Operation *op, SlapReply *rs )
948{
949	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
950	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
951	BackendDB	db, *bd = op->o_bd;
952	ldap_chain_cb_t	lb = { 0 };
953	slap_callback	*sc = op->o_callback,
954			sc2 = { 0 };
955	int		rc = 0;
956	const char	*text = NULL;
957	const char	*matched;
958	BerVarray	ref;
959	struct berval	ndn = op->o_ndn;
960
961	int		sr_err = rs->sr_err;
962	slap_reply_t	sr_type = rs->sr_type;
963#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
964	slap_mask_t	chain_mask = 0;
965	ber_len_t	chain_shift = 0;
966#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
967
968	if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
969		return SLAP_CB_CONTINUE;
970	}
971
972#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
973	if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
974		switch ( get_resolveBehavior( op ) ) {
975		case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
976		case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
977			return SLAP_CB_CONTINUE;
978
979		default:
980			chain_mask = SLAP_CH_RESOLVE_MASK;
981			chain_shift = SLAP_CH_RESOLVE_SHIFT;
982			break;
983		}
984
985	} else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
986		switch ( get_continuationBehavior( op ) ) {
987		case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
988		case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
989			return SLAP_CB_CONTINUE;
990
991		default:
992			chain_mask = SLAP_CH_CONTINUATION_MASK;
993			chain_shift = SLAP_CH_CONTINUATION_SHIFT;
994			break;
995		}
996	}
997#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
998
999	/*
1000	 * TODO: add checks on who/when chain operations; e.g.:
1001	 *   a) what identities are authorized
1002	 *   b) what request DN (e.g. only chain requests rooted at <DN>)
1003	 *   c) what referral URIs
1004	 *   d) what protocol scheme (e.g. only ldaps://)
1005	 *   e) what ssf
1006	 */
1007
1008	db = *op->o_bd;
1009	SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
1010	op->o_bd = &db;
1011
1012	text = rs->sr_text;
1013	rs->sr_text = NULL;
1014	matched = rs->sr_matched;
1015	rs->sr_matched = NULL;
1016	ref = rs->sr_ref;
1017	rs->sr_ref = NULL;
1018
1019	/* we need this to know if back-ldap returned any result */
1020	lb.lb_lc = lc;
1021	sc2.sc_next = sc->sc_next;
1022	sc2.sc_private = &lb;
1023	sc2.sc_response = ldap_chain_cb_response;
1024	op->o_callback = &sc2;
1025
1026	/* Chaining can be performed by a privileged user on behalf
1027	 * of normal users, using the ProxyAuthz control, by exploiting
1028	 * the identity assertion feature of back-ldap; see idassert-*
1029	 * directives in slapd-ldap(5).
1030	 *
1031	 * FIXME: the idassert-authcDN is one, will it be fine regardless
1032	 * of the URI we obtain from the referral?
1033	 */
1034
1035	switch ( op->o_tag ) {
1036	case LDAP_REQ_BIND: {
1037		struct berval	rndn = op->o_req_ndn;
1038		Connection	*conn = op->o_conn;
1039
1040		/* FIXME: can we really get a referral for binds? */
1041		op->o_req_ndn = slap_empty_bv;
1042		op->o_conn = NULL;
1043		rc = ldap_chain_op( op, rs, lback->bi_op_bind, ref, 0 );
1044		op->o_req_ndn = rndn;
1045		op->o_conn = conn;
1046		}
1047		break;
1048
1049	case LDAP_REQ_ADD:
1050		rc = ldap_chain_op( op, rs, lback->bi_op_add, ref, 0 );
1051		break;
1052
1053	case LDAP_REQ_DELETE:
1054		rc = ldap_chain_op( op, rs, lback->bi_op_delete, ref, 0 );
1055		break;
1056
1057	case LDAP_REQ_MODRDN:
1058		rc = ldap_chain_op( op, rs, lback->bi_op_modrdn, ref, 0 );
1059	    	break;
1060
1061	case LDAP_REQ_MODIFY:
1062		rc = ldap_chain_op( op, rs, lback->bi_op_modify, ref, 0 );
1063		break;
1064
1065	case LDAP_REQ_COMPARE:
1066		rc = ldap_chain_op( op, rs, lback->bi_op_compare, ref, 0 );
1067		if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
1068			rc = LDAP_SUCCESS;
1069		}
1070		break;
1071
1072	case LDAP_REQ_SEARCH:
1073		if ( rs->sr_type == REP_SEARCHREF ) {
1074			sc2.sc_response = ldap_chain_cb_search_response;
1075			rc = ldap_chain_search( op, rs, ref, 0 );
1076
1077		} else {
1078			/* we might get here before any database actually
1079			 * performed a search; in those cases, we need
1080			 * to check limits, to make sure safe defaults
1081			 * are in place */
1082			if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
1083				rc = ldap_chain_op( op, rs, lback->bi_op_search, ref, 0 );
1084
1085			} else {
1086				rc = SLAP_CB_CONTINUE;
1087			}
1088		}
1089	    	break;
1090
1091	case LDAP_REQ_EXTENDED:
1092		rc = ldap_chain_op( op, rs, lback->bi_extended, ref, 0 );
1093		/* FIXME: ldap_back_extended() by design
1094		 * doesn't send result; frontend is expected
1095		 * to send it... */
1096		/* FIXME: what about chaining? */
1097		if ( rc != SLAPD_ABANDON ) {
1098			rs->sr_err = rc;
1099			send_ldap_extended( op, rs );
1100			rc = LDAP_SUCCESS;
1101		}
1102		lb.lb_status = LDAP_CH_RES;
1103		break;
1104
1105	default:
1106		rc = SLAP_CB_CONTINUE;
1107		break;
1108	}
1109
1110	switch ( rc ) {
1111	case SLAPD_ABANDON:
1112		goto dont_chain;
1113
1114	case LDAP_SUCCESS:
1115	case LDAP_REFERRAL:
1116		sr_err = rs->sr_err;
1117		/* slapd-ldap sent response */
1118		if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
1119			/* FIXME: should we send response? */
1120			Debug( LDAP_DEBUG_ANY,
1121				"%s: ldap_chain_response: "
1122				"overlay should have sent result.\n",
1123				op->o_log_prefix, 0, 0 );
1124		}
1125		break;
1126
1127	default:
1128#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1129		if ( lb.lb_status == LDAP_CH_ERR && rs->sr_err == LDAP_X_CANNOT_CHAIN ) {
1130			goto cannot_chain;
1131		}
1132
1133		switch ( ( get_chainingBehavior( op ) & chain_mask ) >> chain_shift ) {
1134		case LDAP_CHAINING_REQUIRED:
1135cannot_chain:;
1136			op->o_callback = NULL;
1137			send_ldap_error( op, rs, LDAP_X_CANNOT_CHAIN,
1138				"operation cannot be completed without chaining" );
1139			goto dont_chain;
1140
1141		default:
1142#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1143			if ( LDAP_CHAIN_RETURN_ERR( lc ) ) {
1144				sr_err = rs->sr_err = rc;
1145				rs->sr_type = sr_type;
1146
1147			} else {
1148				rc = SLAP_CB_CONTINUE;
1149				rs->sr_err = sr_err;
1150				rs->sr_type = sr_type;
1151				rs->sr_text = text;
1152				rs->sr_matched = matched;
1153				rs->sr_ref = ref;
1154			}
1155#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1156			break;
1157		}
1158#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1159	}
1160
1161	if ( lb.lb_status == LDAP_CH_NONE && rc != SLAPD_ABANDON ) {
1162		/* give the remaining callbacks a chance */
1163		op->o_callback = sc->sc_next;
1164		rc = rs->sr_err = slap_map_api2result( rs );
1165		send_ldap_result( op, rs );
1166	}
1167
1168dont_chain:;
1169	rs->sr_err = sr_err;
1170	rs->sr_type = sr_type;
1171	rs->sr_text = text;
1172	rs->sr_matched = matched;
1173	rs->sr_ref = ref;
1174	op->o_bd = bd;
1175	op->o_callback = sc;
1176	op->o_ndn = ndn;
1177
1178	return rc;
1179}
1180
1181#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1182static int
1183ldap_chain_parse_ctrl(
1184	Operation	*op,
1185	SlapReply	*rs,
1186	LDAPControl	*ctrl );
1187
1188static int
1189str2chain( const char *s )
1190{
1191	if ( strcasecmp( s, "chainingPreferred" ) == 0 ) {
1192		return LDAP_CHAINING_PREFERRED;
1193
1194	} else if ( strcasecmp( s, "chainingRequired" ) == 0 ) {
1195		return LDAP_CHAINING_REQUIRED;
1196
1197	} else if ( strcasecmp( s, "referralsPreferred" ) == 0 ) {
1198		return LDAP_REFERRALS_PREFERRED;
1199
1200	} else if ( strcasecmp( s, "referralsRequired" ) == 0 ) {
1201		return LDAP_REFERRALS_REQUIRED;
1202	}
1203
1204	return -1;
1205}
1206#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1207
1208/*
1209 * configuration...
1210 */
1211
1212enum {
1213	CH_CHAINING = 1,
1214	CH_CACHE_URI,
1215	CH_MAX_DEPTH,
1216	CH_RETURN_ERR,
1217
1218	CH_LAST
1219};
1220
1221static ConfigDriver chain_cf_gen;
1222static ConfigCfAdd chain_cfadd;
1223static ConfigLDAPadd chain_ldadd;
1224#ifdef SLAP_CONFIG_DELETE
1225static ConfigLDAPdel chain_lddel;
1226#endif
1227
1228static ConfigTable chaincfg[] = {
1229#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1230	{ "chain-chaining", "args",
1231		2, 4, 0, ARG_MAGIC|ARG_BERVAL|CH_CHAINING, chain_cf_gen,
1232		"( OLcfgOvAt:3.1 NAME 'olcChainingBehavior' "
1233			"DESC 'Chaining behavior control parameters (draft-sermersheim-ldap-chaining)' "
1234			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
1235#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1236	{ "chain-cache-uri", "TRUE/FALSE",
1237		2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_CACHE_URI, chain_cf_gen,
1238		"( OLcfgOvAt:3.2 NAME 'olcChainCacheURI' "
1239			"DESC 'Enables caching of URIs not present in configuration' "
1240			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1241	{ "chain-max-depth", "args",
1242		2, 2, 0, ARG_MAGIC|ARG_INT|CH_MAX_DEPTH, chain_cf_gen,
1243		"( OLcfgOvAt:3.3 NAME 'olcChainMaxReferralDepth' "
1244			"DESC 'max referral depth' "
1245			"SYNTAX OMsInteger "
1246			"EQUALITY integerMatch "
1247			"SINGLE-VALUE )", NULL, NULL },
1248	{ "chain-return-error", "TRUE/FALSE",
1249		2, 2, 0, ARG_MAGIC|ARG_ON_OFF|CH_RETURN_ERR, chain_cf_gen,
1250		"( OLcfgOvAt:3.4 NAME 'olcChainReturnError' "
1251			"DESC 'Errors are returned instead of the original referral' "
1252			"SYNTAX OMsBoolean SINGLE-VALUE )", NULL, NULL },
1253	{ NULL, NULL, 0, 0, 0, ARG_IGNORED }
1254};
1255
1256static ConfigOCs chainocs[] = {
1257	{ "( OLcfgOvOc:3.1 "
1258		"NAME 'olcChainConfig' "
1259		"DESC 'Chain configuration' "
1260		"SUP olcOverlayConfig "
1261		"MAY ( "
1262#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1263			"olcChainingBehavior $ "
1264#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1265			"olcChainCacheURI $ "
1266			"olcChainMaxReferralDepth $ "
1267			"olcChainReturnError "
1268			") )",
1269		Cft_Overlay, chaincfg, NULL, chain_cfadd },
1270	{ "( OLcfgOvOc:3.2 "
1271		"NAME 'olcChainDatabase' "
1272		"DESC 'Chain remote server configuration' "
1273		"AUXILIARY )",
1274		Cft_Misc, olcDatabaseDummy, chain_ldadd
1275#ifdef SLAP_CONFIG_DELETE
1276		, NULL, chain_lddel
1277#endif
1278	},
1279	{ NULL, 0, NULL }
1280};
1281
1282static int
1283chain_ldadd( CfEntryInfo *p, Entry *e, ConfigArgs *ca )
1284{
1285	slap_overinst		*on;
1286	ldap_chain_t		*lc;
1287
1288	ldapinfo_t		*li;
1289
1290	AttributeDescription	*ad = NULL;
1291	Attribute		*at;
1292	const char		*text;
1293
1294	int			rc;
1295
1296	if ( p->ce_type != Cft_Overlay
1297		|| !p->ce_bi
1298		|| p->ce_bi->bi_cf_ocs != chainocs )
1299	{
1300		return LDAP_CONSTRAINT_VIOLATION;
1301	}
1302
1303	on = (slap_overinst *)p->ce_bi;
1304	lc = (ldap_chain_t *)on->on_bi.bi_private;
1305
1306	assert( ca->be == NULL );
1307	ca->be = (BackendDB *)ch_calloc( 1, sizeof( BackendDB ) );
1308
1309	ca->be->bd_info = (BackendInfo *)on;
1310
1311	rc = slap_str2ad( "olcDbURI", &ad, &text );
1312	assert( rc == LDAP_SUCCESS );
1313
1314	at = attr_find( e->e_attrs, ad );
1315#if 0
1316	if ( lc->lc_common_li == NULL && at != NULL ) {
1317		/* FIXME: we should generate an empty default entry
1318		 * if none is supplied */
1319		Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1320			"first underlying database \"%s\" "
1321			"cannot contain attribute \"%s\".\n",
1322			e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1323		rc = LDAP_CONSTRAINT_VIOLATION;
1324		goto done;
1325
1326	} else
1327#endif
1328	if ( lc->lc_common_li != NULL && at == NULL ) {
1329		/* FIXME: we should generate an empty default entry
1330		 * if none is supplied */
1331		Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1332			"subsequent underlying database \"%s\" "
1333			"must contain attribute \"%s\".\n",
1334			e->e_name.bv_val, ad->ad_cname.bv_val, 0 );
1335		rc = LDAP_CONSTRAINT_VIOLATION;
1336		goto done;
1337	}
1338
1339	if ( lc->lc_common_li == NULL ) {
1340		rc = ldap_chain_db_init_common( ca->be );
1341
1342	} else {
1343		rc = ldap_chain_db_init_one( ca->be );
1344	}
1345
1346	if ( rc != 0 ) {
1347		Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1348			"unable to init %sunderlying database \"%s\".\n",
1349			lc->lc_common_li == NULL ? "common " : "", e->e_name.bv_val, 0 );
1350		return LDAP_CONSTRAINT_VIOLATION;
1351	}
1352
1353	li = ca->be->be_private;
1354
1355	if ( lc->lc_common_li == NULL ) {
1356		lc->lc_common_li = li;
1357
1358	} else {
1359		li->li_uri = ch_strdup( at->a_vals[ 0 ].bv_val );
1360		value_add_one( &li->li_bvuri, &at->a_vals[ 0 ] );
1361		if ( avl_insert( &lc->lc_lai.lai_tree, (caddr_t)li,
1362			ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1363		{
1364			Debug( LDAP_DEBUG_ANY, "slapd-chain: "
1365				"database \"%s\" insert failed.\n",
1366				e->e_name.bv_val, 0, 0 );
1367			rc = LDAP_CONSTRAINT_VIOLATION;
1368			goto done;
1369		}
1370	}
1371
1372	ca->ca_private = on;
1373
1374done:;
1375	if ( rc != LDAP_SUCCESS ) {
1376		(void)ldap_chain_db_destroy_one( ca->be, NULL );
1377		ch_free( ca->be );
1378		ca->be = NULL;
1379	}
1380
1381	return rc;
1382}
1383
1384typedef struct ldap_chain_cfadd_apply_t {
1385	Operation	*op;
1386	SlapReply	*rs;
1387	Entry		*p;
1388	ConfigArgs	*ca;
1389	int		count;
1390} ldap_chain_cfadd_apply_t;
1391
1392static int
1393ldap_chain_cfadd_apply( void *datum, void *arg )
1394{
1395	ldapinfo_t			*li = (ldapinfo_t *)datum;
1396	ldap_chain_cfadd_apply_t	*lca = (ldap_chain_cfadd_apply_t *)arg;
1397
1398	struct berval			bv;
1399
1400	/* FIXME: should not hardcode "olcDatabase" here */
1401	bv.bv_len = snprintf( lca->ca->cr_msg, sizeof( lca->ca->cr_msg ),
1402		"olcDatabase={%d}%s", lca->count, lback->bi_type );
1403	bv.bv_val = lca->ca->cr_msg;
1404
1405	lca->ca->be->be_private = (void *)li;
1406	config_build_entry( lca->op, lca->rs, lca->p->e_private, lca->ca,
1407		&bv, lback->bi_cf_ocs, &chainocs[1] );
1408
1409	lca->count++;
1410
1411	return 0;
1412}
1413
1414static int
1415chain_cfadd( Operation *op, SlapReply *rs, Entry *p, ConfigArgs *ca )
1416{
1417	CfEntryInfo	*pe = p->e_private;
1418	slap_overinst	*on = (slap_overinst *)pe->ce_bi;
1419	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
1420	void		*priv = (void *)ca->be->be_private;
1421
1422	if ( lback->bi_cf_ocs ) {
1423		ldap_chain_cfadd_apply_t	lca = { 0 };
1424
1425		lca.op = op;
1426		lca.rs = rs;
1427		lca.p = p;
1428		lca.ca = ca;
1429		lca.count = 0;
1430
1431		(void)ldap_chain_cfadd_apply( (void *)lc->lc_common_li, (void *)&lca );
1432
1433		(void)avl_apply( lc->lc_lai.lai_tree, ldap_chain_cfadd_apply,
1434			&lca, 1, AVL_INORDER );
1435
1436		ca->be->be_private = priv;
1437	}
1438
1439	return 0;
1440}
1441
1442#ifdef SLAP_CONFIG_DELETE
1443static int
1444chain_lddel( CfEntryInfo *ce, Operation *op )
1445{
1446	CfEntryInfo	*pe = ce->ce_parent;
1447	slap_overinst	*on = (slap_overinst *)pe->ce_bi;
1448	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
1449	ldapinfo_t	*li = (ldapinfo_t *) ce->ce_be->be_private;
1450
1451	if ( li != lc->lc_common_li ) {
1452		if (! avl_delete( &lc->lc_lai.lai_tree, li, ldap_chain_uri_cmp ) ) {
1453			Debug( LDAP_DEBUG_ANY, "slapd-chain: avl_delete failed. "
1454				"\"%s\" not found.\n", li->li_uri, 0, 0 );
1455			return -1;
1456		}
1457	} else if ( lc->lc_lai.lai_tree ) {
1458		Debug( LDAP_DEBUG_ANY, "slapd-chain: cannot delete first underlying "
1459			"LDAP database when other databases are still present.\n", 0, 0, 0 );
1460		return -1;
1461	} else {
1462		lc->lc_common_li = NULL;
1463	}
1464
1465	ce->ce_be->bd_info = lback;
1466
1467	if ( ce->ce_be->bd_info->bi_db_close ) {
1468		ce->ce_be->bd_info->bi_db_close( ce->ce_be, NULL );
1469	}
1470	if ( ce->ce_be->bd_info->bi_db_destroy ) {
1471		ce->ce_be->bd_info->bi_db_destroy( ce->ce_be, NULL );
1472	}
1473
1474	ch_free(ce->ce_be);
1475	ce->ce_be = NULL;
1476
1477	return LDAP_SUCCESS;
1478}
1479#endif /* SLAP_CONFIG_DELETE */
1480
1481#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1482static slap_verbmasks chaining_mode[] = {
1483	{ BER_BVC("referralsRequired"),		LDAP_REFERRALS_REQUIRED },
1484	{ BER_BVC("referralsPreferred"),	LDAP_REFERRALS_PREFERRED },
1485	{ BER_BVC("chainingRequired"),		LDAP_CHAINING_REQUIRED },
1486	{ BER_BVC("chainingPreferred"),		LDAP_CHAINING_PREFERRED },
1487	{ BER_BVNULL,				0 }
1488};
1489#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1490
1491static int
1492chain_cf_gen( ConfigArgs *c )
1493{
1494	slap_overinst	*on = (slap_overinst *)c->bi;
1495	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
1496
1497	int		rc = 0;
1498
1499	if ( c->op == SLAP_CONFIG_EMIT ) {
1500		switch( c->type ) {
1501#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1502		case CH_CHAINING: {
1503			struct berval	resolve = BER_BVNULL,
1504					continuation = BER_BVNULL;
1505
1506			if ( !LDAP_CHAIN_CHAINING( lc ) ) {
1507				return 1;
1508			}
1509
1510			enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_RESOLVE_MASK ) >> SLAP_CH_RESOLVE_SHIFT ), &resolve );
1511			enum_to_verb( chaining_mode, ( ( lc->lc_chaining_ctrlflag & SLAP_CH_CONTINUATION_MASK ) >> SLAP_CH_CONTINUATION_SHIFT ), &continuation );
1512
1513			c->value_bv.bv_len = STRLENOF( "resolve=" ) + resolve.bv_len
1514				+ STRLENOF( " " )
1515				+ STRLENOF( "continuation=" ) + continuation.bv_len;
1516			c->value_bv.bv_val = ch_malloc( c->value_bv.bv_len + 1 );
1517			snprintf( c->value_bv.bv_val, c->value_bv.bv_len + 1,
1518				"resolve=%s continuation=%s",
1519				resolve.bv_val, continuation.bv_val );
1520
1521			if ( lc->lc_chaining_ctrl.ldctl_iscritical ) {
1522				c->value_bv.bv_val = ch_realloc( c->value_bv.bv_val,
1523					c->value_bv.bv_len + STRLENOF( " critical" ) + 1 );
1524				AC_MEMCPY( &c->value_bv.bv_val[ c->value_bv.bv_len ],
1525					" critical", STRLENOF( " critical" ) + 1 );
1526				c->value_bv.bv_len += STRLENOF( " critical" );
1527			}
1528
1529			break;
1530		}
1531#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1532
1533		case CH_CACHE_URI:
1534			c->value_int = LDAP_CHAIN_CACHE_URI( lc );
1535			break;
1536
1537		case CH_MAX_DEPTH:
1538			c->value_int = lc->lc_max_depth;
1539			break;
1540
1541		case CH_RETURN_ERR:
1542			c->value_int = LDAP_CHAIN_RETURN_ERR( lc );
1543			break;
1544
1545		default:
1546			assert( 0 );
1547			rc = 1;
1548		}
1549		return rc;
1550
1551	} else if ( c->op == LDAP_MOD_DELETE ) {
1552		switch( c->type ) {
1553		case CH_CHAINING:
1554			return 1;
1555
1556		case CH_CACHE_URI:
1557			lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1558			break;
1559
1560		case CH_MAX_DEPTH:
1561			c->value_int = 0;
1562			break;
1563
1564		case CH_RETURN_ERR:
1565			lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1566			break;
1567
1568		default:
1569			return 1;
1570		}
1571		return rc;
1572	}
1573
1574	switch( c->type ) {
1575	case CH_CHAINING: {
1576#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1577		char			**argv = c->argv;
1578		int			argc = c->argc;
1579		BerElementBuffer	berbuf;
1580		BerElement		*ber = (BerElement *)&berbuf;
1581		int			resolve = -1,
1582					continuation = -1,
1583					iscritical = 0;
1584		Operation		op = { 0 };
1585		SlapReply		rs = { 0 };
1586
1587		lc->lc_chaining_ctrlflag = 0;
1588
1589		for ( argc--, argv++; argc > 0; argc--, argv++ ) {
1590			if ( strncasecmp( argv[ 0 ], "resolve=", STRLENOF( "resolve=" ) ) == 0 ) {
1591				resolve = str2chain( argv[ 0 ] + STRLENOF( "resolve=" ) );
1592				if ( resolve == -1 ) {
1593					Debug( LDAP_DEBUG_ANY, "%s: "
1594						"illegal <resolve> value %s "
1595						"in \"chain-chaining>\".\n",
1596						c->log, argv[ 0 ], 0 );
1597					return 1;
1598				}
1599
1600			} else if ( strncasecmp( argv[ 0 ], "continuation=", STRLENOF( "continuation=" ) ) == 0 ) {
1601				continuation = str2chain( argv[ 0 ] + STRLENOF( "continuation=" ) );
1602				if ( continuation == -1 ) {
1603					Debug( LDAP_DEBUG_ANY, "%s: "
1604						"illegal <continuation> value %s "
1605						"in \"chain-chaining\".\n",
1606						c->log, argv[ 0 ], 0 );
1607					return 1;
1608				}
1609
1610			} else if ( strcasecmp( argv[ 0 ], "critical" ) == 0 ) {
1611				iscritical = 1;
1612
1613			} else {
1614				Debug( LDAP_DEBUG_ANY, "%s: "
1615					"unknown option in \"chain-chaining\".\n",
1616					c->log, 0, 0 );
1617				return 1;
1618			}
1619		}
1620
1621		if ( resolve != -1 || continuation != -1 ) {
1622			int	err;
1623
1624			if ( resolve == -1 ) {
1625				/* default */
1626				resolve = SLAP_CHAINING_DEFAULT;
1627			}
1628
1629			ber_init2( ber, NULL, LBER_USE_DER );
1630
1631			err = ber_printf( ber, "{e" /* } */, resolve );
1632	    		if ( err == -1 ) {
1633				ber_free( ber, 1 );
1634				Debug( LDAP_DEBUG_ANY, "%s: "
1635					"chaining behavior control encoding error!\n",
1636					c->log, 0, 0 );
1637				return 1;
1638			}
1639
1640			if ( continuation > -1 ) {
1641				err = ber_printf( ber, "e", continuation );
1642	    			if ( err == -1 ) {
1643					ber_free( ber, 1 );
1644					Debug( LDAP_DEBUG_ANY, "%s: "
1645						"chaining behavior control encoding error!\n",
1646						c->log, 0, 0 );
1647					return 1;
1648				}
1649			}
1650
1651			err = ber_printf( ber, /* { */ "N}" );
1652	    		if ( err == -1 ) {
1653				ber_free( ber, 1 );
1654				Debug( LDAP_DEBUG_ANY, "%s: "
1655					"chaining behavior control encoding error!\n",
1656					c->log, 0, 0 );
1657				return 1;
1658			}
1659
1660			if ( ber_flatten2( ber, &lc->lc_chaining_ctrl.ldctl_value, 0 ) == -1 ) {
1661				exit( EXIT_FAILURE );
1662			}
1663
1664		} else {
1665			BER_BVZERO( &lc->lc_chaining_ctrl.ldctl_value );
1666		}
1667
1668		lc->lc_chaining_ctrl.ldctl_oid = LDAP_CONTROL_X_CHAINING_BEHAVIOR;
1669		lc->lc_chaining_ctrl.ldctl_iscritical = iscritical;
1670
1671		if ( ldap_chain_parse_ctrl( &op, &rs, &lc->lc_chaining_ctrl ) != LDAP_SUCCESS )
1672		{
1673			Debug( LDAP_DEBUG_ANY, "%s: "
1674				"unable to parse chaining control%s%s.\n",
1675				c->log, rs.sr_text ? ": " : "",
1676				rs.sr_text ? rs.sr_text : "" );
1677			return 1;
1678		}
1679
1680		lc->lc_chaining_ctrlflag = op.o_chaining;
1681
1682		lc->lc_flags |= LDAP_CHAIN_F_CHAINING;
1683
1684		rc = 0;
1685#else /* ! LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1686		Debug( LDAP_DEBUG_ANY, "%s: "
1687			"\"chaining\" control unsupported (ignored).\n",
1688			c->log, 0, 0 );
1689#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1690		} break;
1691
1692	case CH_CACHE_URI:
1693		if ( c->value_int ) {
1694			lc->lc_flags |= LDAP_CHAIN_F_CACHE_URI;
1695		} else {
1696			lc->lc_flags &= ~LDAP_CHAIN_F_CACHE_URI;
1697		}
1698		break;
1699
1700	case CH_MAX_DEPTH:
1701		if ( c->value_int < 0 ) {
1702			snprintf( c->cr_msg, sizeof( c->cr_msg ),
1703				"<%s> invalid max referral depth %d",
1704				c->argv[0], c->value_int );
1705			Debug( LDAP_DEBUG_ANY, "%s: %s.\n",
1706				c->log, c->cr_msg, 0 );
1707			rc = 1;
1708			break;
1709		}
1710		lc->lc_max_depth = c->value_int;
1711
1712	case CH_RETURN_ERR:
1713		if ( c->value_int ) {
1714			lc->lc_flags |= LDAP_CHAIN_F_RETURN_ERR;
1715		} else {
1716			lc->lc_flags &= ~LDAP_CHAIN_F_RETURN_ERR;
1717		}
1718		break;
1719
1720	default:
1721		assert( 0 );
1722		return 1;
1723	}
1724	return rc;
1725}
1726
1727static int
1728ldap_chain_db_init(
1729	BackendDB *be,
1730	ConfigReply *cr )
1731{
1732	slap_overinst	*on = (slap_overinst *)be->bd_info;
1733	ldap_chain_t	*lc = NULL;
1734
1735	if ( lback == NULL ) {
1736		lback = backend_info( "ldap" );
1737
1738		if ( lback == NULL ) {
1739			return 1;
1740		}
1741	}
1742
1743	lc = ch_malloc( sizeof( ldap_chain_t ) );
1744	if ( lc == NULL ) {
1745		return 1;
1746	}
1747	memset( lc, 0, sizeof( ldap_chain_t ) );
1748	lc->lc_max_depth = 1;
1749	ldap_pvt_thread_mutex_init( &lc->lc_lai.lai_mutex );
1750
1751	on->on_bi.bi_private = (void *)lc;
1752
1753	return 0;
1754}
1755
1756static int
1757ldap_chain_db_config(
1758	BackendDB	*be,
1759	const char	*fname,
1760	int		lineno,
1761	int		argc,
1762	char		**argv )
1763{
1764	slap_overinst	*on = (slap_overinst *)be->bd_info;
1765	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
1766
1767	int		rc = SLAP_CONF_UNKNOWN;
1768
1769	if ( lc->lc_common_li == NULL ) {
1770		BackendDB db = *be;
1771		ldap_chain_db_init_common( &db );
1772		lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)db.be_private;
1773	}
1774
1775	/* Something for the chain database? */
1776	if ( strncasecmp( argv[ 0 ], "chain-", STRLENOF( "chain-" ) ) == 0 ) {
1777		char		*save_argv0 = argv[ 0 ];
1778		BackendDB	db = *be;
1779		static char	*allowed_argv[] = {
1780			/* special: put URI here, so in the meanwhile
1781			 * it detects whether a new URI is being provided */
1782			"uri",
1783			"nretries",
1784			"timeout",
1785			/* flags */
1786			"tls",
1787			/* FIXME: maybe rebind-as-user should be allowed
1788			 * only within known URIs... */
1789			"rebind-as-user",
1790			"chase-referrals",
1791			"t-f-support",
1792			"proxy-whoami",
1793			NULL
1794		};
1795		int		which_argv = -1;
1796
1797		argv[ 0 ] += STRLENOF( "chain-" );
1798
1799		for ( which_argv = 0; allowed_argv[ which_argv ]; which_argv++ ) {
1800			if ( strcasecmp( argv[ 0 ], allowed_argv[ which_argv ] ) == 0 ) {
1801				break;
1802			}
1803		}
1804
1805		if ( allowed_argv[ which_argv ] == NULL ) {
1806			which_argv = -1;
1807
1808			if ( lc->lc_cfg_li == lc->lc_common_li ) {
1809				Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1810					"\"%s\" only allowed within a URI directive.\n.",
1811					fname, lineno, argv[ 0 ] );
1812				return 1;
1813			}
1814		}
1815
1816		if ( which_argv == 0 ) {
1817			rc = ldap_chain_db_init_one( &db );
1818			if ( rc != 0 ) {
1819				Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1820					"underlying slapd-ldap initialization failed.\n.",
1821					fname, lineno, 0 );
1822				return 1;
1823			}
1824			lc->lc_cfg_li = db.be_private;
1825		}
1826
1827		/* TODO: add checks on what other slapd-ldap(5) args
1828		 * should be put in the template; this is not quite
1829		 * harmful, because attributes that shouldn't don't
1830		 * get actually used, but the user should at least
1831		 * be warned.
1832		 */
1833
1834		db.bd_info = lback;
1835		db.be_private = (void *)lc->lc_cfg_li;
1836		db.be_cf_ocs = lback->bi_cf_ocs;
1837
1838		rc = config_generic_wrapper( &db, fname, lineno, argc, argv );
1839
1840		argv[ 0 ] = save_argv0;
1841
1842		if ( which_argv == 0 ) {
1843private_destroy:;
1844			if ( rc != 0 ) {
1845				db.bd_info = lback;
1846				db.be_private = (void *)lc->lc_cfg_li;
1847				ldap_chain_db_destroy_one( &db, NULL );
1848				lc->lc_cfg_li = NULL;
1849			} else {
1850				if ( lc->lc_cfg_li->li_bvuri == NULL
1851					|| BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 0 ] )
1852					|| !BER_BVISNULL( &lc->lc_cfg_li->li_bvuri[ 1 ] ) )
1853				{
1854					Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1855						"no URI list allowed in slapo-chain.\n",
1856						fname, lineno, 0 );
1857					rc = 1;
1858					goto private_destroy;
1859				}
1860
1861				if ( avl_insert( &lc->lc_lai.lai_tree,
1862					(caddr_t)lc->lc_cfg_li,
1863					ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
1864				{
1865					Debug( LDAP_DEBUG_ANY, "%s: line %d: "
1866						"duplicate URI in slapo-chain.\n",
1867						fname, lineno, 0 );
1868					rc = 1;
1869					goto private_destroy;
1870				}
1871			}
1872		}
1873	}
1874
1875	return rc;
1876}
1877
1878enum db_which {
1879	db_open = 0,
1880	db_close,
1881	db_destroy,
1882
1883	db_last
1884};
1885
1886typedef struct ldap_chain_db_apply_t {
1887	BackendDB	*be;
1888	BI_db_func	*func;
1889} ldap_chain_db_apply_t;
1890
1891static int
1892ldap_chain_db_apply( void *datum, void *arg )
1893{
1894	ldapinfo_t		*li = (ldapinfo_t *)datum;
1895	ldap_chain_db_apply_t	*lca = (ldap_chain_db_apply_t *)arg;
1896
1897	lca->be->be_private = (void *)li;
1898
1899	return lca->func( lca->be, NULL );
1900}
1901
1902static int
1903ldap_chain_db_func(
1904	BackendDB *be,
1905	enum db_which which
1906)
1907{
1908	slap_overinst	*on = (slap_overinst *)be->bd_info;
1909	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
1910
1911	int		rc = 0;
1912
1913	if ( lc ) {
1914		BI_db_func	*func = (&lback->bi_db_open)[ which ];
1915
1916		if ( func != NULL && lc->lc_common_li != NULL ) {
1917			BackendDB		db = *be;
1918
1919			db.bd_info = lback;
1920			db.be_private = lc->lc_common_li;
1921
1922			rc = func( &db, NULL );
1923
1924			if ( rc != 0 ) {
1925				return rc;
1926			}
1927
1928			if ( lc->lc_lai.lai_tree != NULL ) {
1929				ldap_chain_db_apply_t	lca;
1930
1931				lca.be = &db;
1932				lca.func = func;
1933
1934				rc = avl_apply( lc->lc_lai.lai_tree,
1935					ldap_chain_db_apply, (void *)&lca,
1936					1, AVL_INORDER ) != AVL_NOMORE;
1937			}
1938		}
1939	}
1940
1941	return rc;
1942}
1943
1944static int
1945ldap_chain_db_open(
1946	BackendDB	*be,
1947	ConfigReply	*cr )
1948{
1949	slap_overinst	*on = (slap_overinst *) be->bd_info;
1950	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
1951	slap_mask_t	monitoring;
1952	int		rc = 0;
1953
1954#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1955	rc = overlay_register_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1956	if ( rc != 0 ) {
1957		return rc;
1958	}
1959#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1960
1961	if ( lc->lc_common_li == NULL ) {
1962		void	*be_private = be->be_private;
1963		ldap_chain_db_init_common( be );
1964		lc->lc_common_li = lc->lc_cfg_li = (ldapinfo_t *)be->be_private;
1965		be->be_private = be_private;
1966	}
1967
1968	/* filter out and restore monitoring */
1969	monitoring = ( SLAP_DBFLAGS( be ) & SLAP_DBFLAG_MONITORING );
1970	SLAP_DBFLAGS( be ) &= ~SLAP_DBFLAG_MONITORING;
1971	rc = ldap_chain_db_func( be, db_open );
1972	SLAP_DBFLAGS( be ) |= monitoring;
1973
1974	return rc;
1975}
1976
1977static int
1978ldap_chain_db_close(
1979	BackendDB	*be,
1980	ConfigReply	*cr )
1981{
1982#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
1983#ifdef SLAP_CONFIG_DELETE
1984	overlay_unregister_control( be, LDAP_CONTROL_X_CHAINING_BEHAVIOR );
1985#endif /* SLAP_CONFIG_DELETE */
1986#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
1987	return ldap_chain_db_func( be, db_close );
1988}
1989
1990static int
1991ldap_chain_db_destroy(
1992	BackendDB	*be,
1993	ConfigReply	*cr )
1994{
1995	slap_overinst	*on = (slap_overinst *) be->bd_info;
1996	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
1997
1998	int		rc;
1999
2000	rc = ldap_chain_db_func( be, db_destroy );
2001
2002	if ( lc ) {
2003		avl_free( lc->lc_lai.lai_tree, NULL );
2004		ldap_pvt_thread_mutex_destroy( &lc->lc_lai.lai_mutex );
2005		ch_free( lc );
2006	}
2007
2008	return rc;
2009}
2010
2011/*
2012 * inits one instance of the slapd-ldap backend, and stores
2013 * the private info in be_private of the arg
2014 */
2015static int
2016ldap_chain_db_init_common(
2017	BackendDB	*be )
2018{
2019	BackendInfo	*bi = be->bd_info;
2020	ldapinfo_t	*li;
2021	int		rc;
2022
2023	be->bd_info = lback;
2024	be->be_private = NULL;
2025	rc = lback->bi_db_init( be, NULL );
2026	if ( rc != 0 ) {
2027		return rc;
2028	}
2029	li = (ldapinfo_t *)be->be_private;
2030	li->li_urllist_f = NULL;
2031	li->li_urllist_p = NULL;
2032
2033	be->bd_info = bi;
2034
2035	return 0;
2036}
2037
2038/*
2039 * inits one instance of the slapd-ldap backend, stores
2040 * the private info in be_private of the arg and fills
2041 * selected fields with data from the template.
2042 *
2043 * NOTE: add checks about the other fields of the template,
2044 * which are ignored and SHOULD NOT be configured by the user.
2045 */
2046static int
2047ldap_chain_db_init_one(
2048	BackendDB	*be )
2049{
2050	slap_overinst	*on = (slap_overinst *)be->bd_info;
2051	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
2052
2053	BackendInfo	*bi = be->bd_info;
2054	ldapinfo_t	*li;
2055
2056	slap_op_t	t;
2057
2058	be->bd_info = lback;
2059	be->be_private = NULL;
2060	t = lback->bi_db_init( be, NULL );
2061	if ( t != 0 ) {
2062		return t;
2063	}
2064	li = (ldapinfo_t *)be->be_private;
2065	li->li_urllist_f = NULL;
2066	li->li_urllist_p = NULL;
2067
2068	/* copy common data */
2069	li->li_nretries = lc->lc_common_li->li_nretries;
2070	li->li_flags = lc->lc_common_li->li_flags;
2071	li->li_version = lc->lc_common_li->li_version;
2072	for ( t = 0; t < SLAP_OP_LAST; t++ ) {
2073		li->li_timeout[ t ] = lc->lc_common_li->li_timeout[ t ];
2074	}
2075	be->bd_info = bi;
2076
2077	return 0;
2078}
2079
2080static int
2081ldap_chain_db_open_one(
2082	BackendDB	*be )
2083{
2084	if ( SLAP_DBMONITORING( be ) ) {
2085		ldapinfo_t	*li = (ldapinfo_t *)be->be_private;
2086
2087		if ( li->li_uri == NULL ) {
2088			ber_str2bv( "cn=Common Connections", 0, 1,
2089				&li->li_monitor_info.lmi_rdn );
2090
2091		} else {
2092			char		*ptr;
2093
2094			li->li_monitor_info.lmi_rdn.bv_len
2095				= STRLENOF( "cn=" ) + strlen( li->li_uri );
2096			ptr = li->li_monitor_info.lmi_rdn.bv_val
2097				= ch_malloc( li->li_monitor_info.lmi_rdn.bv_len + 1 );
2098			ptr = lutil_strcopy( ptr, "cn=" );
2099			ptr = lutil_strcopy( ptr, li->li_uri );
2100			ptr[ 0 ] = '\0';
2101		}
2102	}
2103
2104	return lback->bi_db_open( be, NULL );
2105}
2106
2107typedef struct ldap_chain_conn_apply_t {
2108	BackendDB	*be;
2109	Connection	*conn;
2110} ldap_chain_conn_apply_t;
2111
2112static int
2113ldap_chain_conn_apply( void *datum, void *arg )
2114{
2115	ldapinfo_t		*li = (ldapinfo_t *)datum;
2116	ldap_chain_conn_apply_t	*lca = (ldap_chain_conn_apply_t *)arg;
2117
2118	lca->be->be_private = (void *)li;
2119
2120	return lback->bi_connection_destroy( lca->be, lca->conn );
2121}
2122
2123static int
2124ldap_chain_connection_destroy(
2125	BackendDB *be,
2126	Connection *conn
2127)
2128{
2129	slap_overinst		*on = (slap_overinst *) be->bd_info;
2130	ldap_chain_t		*lc = (ldap_chain_t *)on->on_bi.bi_private;
2131	void			*private = be->be_private;
2132	ldap_chain_conn_apply_t	lca;
2133	int			rc;
2134
2135	be->be_private = NULL;
2136	lca.be = be;
2137	lca.conn = conn;
2138	ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
2139	rc = avl_apply( lc->lc_lai.lai_tree, ldap_chain_conn_apply,
2140		(void *)&lca, 1, AVL_INORDER ) != AVL_NOMORE;
2141	ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );
2142	be->be_private = private;
2143
2144	return rc;
2145}
2146
2147#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2148static int
2149ldap_chain_parse_ctrl(
2150	Operation	*op,
2151	SlapReply	*rs,
2152	LDAPControl	*ctrl )
2153{
2154	ber_tag_t	tag;
2155	BerElement	*ber;
2156	ber_int_t	mode,
2157			behavior;
2158
2159	if ( get_chaining( op ) != SLAP_CONTROL_NONE ) {
2160		rs->sr_text = "Chaining behavior control specified multiple times";
2161		return LDAP_PROTOCOL_ERROR;
2162	}
2163
2164	if ( op->o_pagedresults != SLAP_CONTROL_NONE ) {
2165		rs->sr_text = "Chaining behavior control specified with pagedResults control";
2166		return LDAP_PROTOCOL_ERROR;
2167	}
2168
2169	if ( BER_BVISEMPTY( &ctrl->ldctl_value ) ) {
2170		mode = (SLAP_CH_RESOLVE_DEFAULT|SLAP_CH_CONTINUATION_DEFAULT);
2171
2172	} else {
2173		ber_len_t	len;
2174
2175		/* Parse the control value
2176		 *      ChainingBehavior ::= SEQUENCE {
2177		 *           resolveBehavior         Behavior OPTIONAL,
2178		 *           continuationBehavior    Behavior OPTIONAL }
2179		 *
2180		 *      Behavior :: = ENUMERATED {
2181		 *           chainingPreferred       (0),
2182		 *           chainingRequired        (1),
2183		 *           referralsPreferred      (2),
2184		 *           referralsRequired       (3) }
2185		 */
2186
2187		ber = ber_init( &ctrl->ldctl_value );
2188		if( ber == NULL ) {
2189			rs->sr_text = "internal error";
2190			return LDAP_OTHER;
2191		}
2192
2193		tag = ber_scanf( ber, "{e" /* } */, &behavior );
2194		/* FIXME: since the whole SEQUENCE is optional,
2195		 * should we accept no enumerations at all? */
2196		if ( tag != LBER_ENUMERATED ) {
2197			rs->sr_text = "Chaining behavior control: resolveBehavior decoding error";
2198			return LDAP_PROTOCOL_ERROR;
2199		}
2200
2201		switch ( behavior ) {
2202		case LDAP_CHAINING_PREFERRED:
2203			mode = SLAP_CH_RESOLVE_CHAINING_PREFERRED;
2204			break;
2205
2206		case LDAP_CHAINING_REQUIRED:
2207			mode = SLAP_CH_RESOLVE_CHAINING_REQUIRED;
2208			break;
2209
2210		case LDAP_REFERRALS_PREFERRED:
2211			mode = SLAP_CH_RESOLVE_REFERRALS_PREFERRED;
2212			break;
2213
2214		case LDAP_REFERRALS_REQUIRED:
2215			mode = SLAP_CH_RESOLVE_REFERRALS_REQUIRED;
2216			break;
2217
2218		default:
2219			rs->sr_text = "Chaining behavior control: unknown resolveBehavior";
2220			return LDAP_PROTOCOL_ERROR;
2221		}
2222
2223		tag = ber_peek_tag( ber, &len );
2224		if ( tag == LBER_ENUMERATED ) {
2225			tag = ber_scanf( ber, "e", &behavior );
2226			if ( tag == LBER_ERROR ) {
2227				rs->sr_text = "Chaining behavior control: continuationBehavior decoding error";
2228				return LDAP_PROTOCOL_ERROR;
2229			}
2230		}
2231
2232		if ( tag == LBER_DEFAULT ) {
2233			mode |= SLAP_CH_CONTINUATION_DEFAULT;
2234
2235		} else {
2236			switch ( behavior ) {
2237			case LDAP_CHAINING_PREFERRED:
2238				mode |= SLAP_CH_CONTINUATION_CHAINING_PREFERRED;
2239				break;
2240
2241			case LDAP_CHAINING_REQUIRED:
2242				mode |= SLAP_CH_CONTINUATION_CHAINING_REQUIRED;
2243				break;
2244
2245			case LDAP_REFERRALS_PREFERRED:
2246				mode |= SLAP_CH_CONTINUATION_REFERRALS_PREFERRED;
2247				break;
2248
2249			case LDAP_REFERRALS_REQUIRED:
2250				mode |= SLAP_CH_CONTINUATION_REFERRALS_REQUIRED;
2251				break;
2252
2253			default:
2254				rs->sr_text = "Chaining behavior control: unknown continuationBehavior";
2255				return LDAP_PROTOCOL_ERROR;
2256			}
2257		}
2258
2259		if ( ( ber_scanf( ber, /* { */ "}") ) == LBER_ERROR ) {
2260			rs->sr_text = "Chaining behavior control: decoding error";
2261			return LDAP_PROTOCOL_ERROR;
2262		}
2263
2264		(void) ber_free( ber, 1 );
2265	}
2266
2267	op->o_chaining = mode | ( ctrl->ldctl_iscritical
2268			? SLAP_CONTROL_CRITICAL
2269			: SLAP_CONTROL_NONCRITICAL );
2270
2271	return LDAP_SUCCESS;
2272}
2273#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2274
2275int
2276chain_initialize( void )
2277{
2278	int rc;
2279
2280	/* Make sure we don't exceed the bits reserved for userland */
2281	config_check_userland( CH_LAST );
2282
2283#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
2284	rc = register_supported_control( LDAP_CONTROL_X_CHAINING_BEHAVIOR,
2285			/* SLAP_CTRL_GLOBAL| */ SLAP_CTRL_ACCESS|SLAP_CTRL_HIDE, NULL,
2286			ldap_chain_parse_ctrl, &sc_chainingBehavior );
2287	if ( rc != LDAP_SUCCESS ) {
2288		Debug( LDAP_DEBUG_ANY, "slapd-chain: "
2289			"unable to register chaining behavior control: %d.\n",
2290			rc, 0, 0 );
2291		return rc;
2292	}
2293#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
2294
2295	ldapchain.on_bi.bi_type = "chain";
2296	ldapchain.on_bi.bi_db_init = ldap_chain_db_init;
2297	ldapchain.on_bi.bi_db_config = ldap_chain_db_config;
2298	ldapchain.on_bi.bi_db_open = ldap_chain_db_open;
2299	ldapchain.on_bi.bi_db_close = ldap_chain_db_close;
2300	ldapchain.on_bi.bi_db_destroy = ldap_chain_db_destroy;
2301
2302	ldapchain.on_bi.bi_connection_destroy = ldap_chain_connection_destroy;
2303
2304	ldapchain.on_response = ldap_chain_response;
2305
2306	ldapchain.on_bi.bi_cf_ocs = chainocs;
2307
2308	rc = config_register_schema( chaincfg, chainocs );
2309	if ( rc ) {
2310		return rc;
2311	}
2312
2313	return overlay_register( &ldapchain );
2314}
2315
2316