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