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 
60 static int		sc_chainingBehavior;
61 #endif /*  LDAP_CONTROL_X_CHAINING_BEHAVIOR */
62 
63 typedef enum {
64 	LDAP_CH_NONE = 0,
65 	LDAP_CH_RES,
66 	LDAP_CH_ERR
67 } ldap_chain_status_t;
68 
69 static BackendInfo	*lback;
70 
71 typedef 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 
114 static int ldap_chain_db_init_common( BackendDB	*be );
115 static int ldap_chain_db_init_one( BackendDB *be );
116 static 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 
120 typedef 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 
127 static int
128 ldap_chain_op(
129 	Operation	*op,
130 	SlapReply	*rs,
131 	BI_op_func	*op_f,
132 	BerVarray	ref,
133 	int		depth );
134 
135 static int
136 ldap_chain_search(
137 	Operation	*op,
138 	SlapReply	*rs,
139 	BerVarray	ref,
140 	int		depth );
141 
142 static slap_overinst ldapchain;
143 
144 #ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
145 static int
146 chaining_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 
190 static int
191 chaining_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 
216 static int
217 ldap_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 
234 static int
235 ldap_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  */
259 static int
260 ldap_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  */
336 static int
337 ldap_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 ) {
347 retry:;
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 
393 static int
394 ldap_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 
432 Document: RFC 4511
433 
434 4.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 
544 cleanup:;
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 
555 further_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 
585 static int
586 ldap_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 
730 cleanup:;
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 
741 further_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 
784 static int
785 ldap_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:
970 cannot_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 
1002 dont_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
1016 static int
1017 ldap_chain_parse_ctrl(
1018 	Operation	*op,
1019 	SlapReply	*rs,
1020 	LDAPControl	*ctrl );
1021 
1022 static int
1023 str2chain( 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 
1046 enum {
1047 	CH_CHAINING = 1,
1048 	CH_CACHE_URI,
1049 	CH_MAX_DEPTH,
1050 	CH_RETURN_ERR,
1051 
1052 	CH_LAST
1053 };
1054 
1055 static ConfigDriver chain_cf_gen;
1056 static ConfigCfAdd chain_cfadd;
1057 static ConfigLDAPadd chain_ldadd;
1058 
1059 static 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 
1087 static 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 
1109 static int
1110 chain_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 
1198 done:;
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 
1208 typedef 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 
1216 static int
1217 ldap_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 
1238 static int
1239 chain_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
1267 static 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 
1276 static int
1277 chain_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 
1512 static int
1513 ldap_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 
1541 static int
1542 ldap_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 ) {
1634 private_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 
1672 enum db_which {
1673 	db_open = 0,
1674 	db_close,
1675 	db_destroy,
1676 
1677 	db_last
1678 };
1679 
1680 typedef struct ldap_chain_db_apply_t {
1681 	BackendDB	*be;
1682 	BI_db_func	*func;
1683 } ldap_chain_db_apply_t;
1684 
1685 static int
1686 ldap_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 
1696 static int
1697 ldap_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 
1738 static int
1739 ldap_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 
1771 static int
1772 ldap_chain_db_close(
1773 	BackendDB	*be,
1774 	ConfigReply	*cr )
1775 {
1776 	return ldap_chain_db_func( be, db_close );
1777 }
1778 
1779 static int
1780 ldap_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  */
1804 static int
1805 ldap_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  */
1835 static int
1836 ldap_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 
1869 static int
1870 ldap_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 
1896 typedef struct ldap_chain_conn_apply_t {
1897 	BackendDB	*be;
1898 	Connection	*conn;
1899 } ldap_chain_conn_apply_t;
1900 
1901 static int
1902 ldap_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 
1912 static int
1913 ldap_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
1937 static int
1938 ldap_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 
2064 int
2065 chain_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