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