chain.c 59.4 KB
Newer Older
1
2
/* chain.c - chain LDAP operations */
/* $OpenLDAP$ */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
3
4
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
5
 * Copyright 2003-2020 The OpenLDAP Foundation.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
6
 * Portions Copyright 2003 Howard Chu.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
7
8
9
10
11
12
13
14
15
16
17
18
19
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* ACKNOWLEDGEMENTS:
 * This work was initially developed by the Howard Chu for inclusion
 * in OpenLDAP Software.
20
 * This work was subsequently modified by Pierangelo Masarati.
21
22
23
24
25
26
27
28
29
 */

#include "portable.h"

#include <stdio.h>

#include <ac/string.h>
#include <ac/socket.h>

30
#include "lutil.h"
31
#include "slap.h"
32
#include "back-ldap.h"
33
34
#include "config.h"

35
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
36
#define SLAP_CHAINING_DEFAULT				LDAP_CHAINING_PREFERRED
37
38
39
40
41
42
#define SLAP_CH_RESOLVE_SHIFT				SLAP_CONTROL_SHIFT
#define SLAP_CH_RESOLVE_MASK				(0x3 << SLAP_CH_RESOLVE_SHIFT)
#define SLAP_CH_RESOLVE_CHAINING_PREFERRED		(LDAP_CHAINING_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
#define SLAP_CH_RESOLVE_CHAINING_REQUIRED		(LDAP_CHAINING_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
#define SLAP_CH_RESOLVE_REFERRALS_PREFERRED		(LDAP_REFERRALS_PREFERRED << SLAP_CH_RESOLVE_SHIFT)
#define SLAP_CH_RESOLVE_REFERRALS_REQUIRED		(LDAP_REFERRALS_REQUIRED << SLAP_CH_RESOLVE_SHIFT)
43
#define SLAP_CH_RESOLVE_DEFAULT				(SLAP_CHAINING_DEFAULT << SLAP_CH_RESOLVE_SHIFT)
44
45
46
47
48
49
#define	SLAP_CH_CONTINUATION_SHIFT			(SLAP_CH_RESOLVE_SHIFT + 2)
#define SLAP_CH_CONTINUATION_MASK			(0x3 << SLAP_CH_CONTINUATION_SHIFT)
#define SLAP_CH_CONTINUATION_CHAINING_PREFERRED		(LDAP_CHAINING_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
#define SLAP_CH_CONTINUATION_CHAINING_REQUIRED		(LDAP_CHAINING_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
#define SLAP_CH_CONTINUATION_REFERRALS_PREFERRED	(LDAP_REFERRALS_PREFERRED << SLAP_CH_CONTINUATION_SHIFT)
#define SLAP_CH_CONTINUATION_REFERRALS_REQUIRED		(LDAP_REFERRALS_REQUIRED << SLAP_CH_CONTINUATION_SHIFT)
50
#define SLAP_CH_CONTINUATION_DEFAULT			(SLAP_CHAINING_DEFAULT << SLAP_CH_CONTINUATION_SHIFT)
51
52
53
54
55
56

#define o_chaining			o_ctrlflag[sc_chainingBehavior]
#define get_chaining(op)		((op)->o_chaining & SLAP_CONTROL_MASK)
#define get_chainingBehavior(op)	((op)->o_chaining & (SLAP_CH_RESOLVE_MASK|SLAP_CH_CONTINUATION_MASK))
#define get_resolveBehavior(op)		((op)->o_chaining & SLAP_CH_RESOLVE_MASK)
#define get_continuationBehavior(op)	((op)->o_chaining & SLAP_CH_CONTINUATION_MASK)
57
58

static int		sc_chainingBehavior;
59
60
#endif /*  LDAP_CONTROL_X_CHAINING_BEHAVIOR */

61
62
63
64
65
typedef enum {
	LDAP_CH_NONE = 0,
	LDAP_CH_RES,
	LDAP_CH_ERR
} ldap_chain_status_t;
66

67
static BackendInfo	*lback;
68

69
typedef struct ldap_chain_t {
70
	/*
71
72
73
74
	 * A "template" ldapinfo_t gets all common configuration items;
	 * then, for each configured URI, an entry is created in the tree;
	 * all the specific configuration items get in the current URI 
	 * structure.
75
76
77
78
79
80
81
	 *
 	 * Then, for each referral, extract the URI and lookup the
	 * related structure.  If configured to do so, allow URIs
	 * not found in the structure to create a temporary one
	 * that chains anonymously; maybe it can also be added to 
	 * the tree?  Should be all configurable.
	 */
82

83
	/* "common" configuration info (anything occurring before an "uri") */
84
85
	ldapinfo_t		*lc_common_li;

86
87
88
89
90
91
	/* current configuration info */
	ldapinfo_t		*lc_cfg_li;

	/* tree of configured[/generated?] "uri" info */
	ldap_avl_info_t		lc_lai;

92
93
94
	/* max depth in nested referrals chaining */
	int			lc_max_depth;

95
96
97
	unsigned		lc_flags;
#define LDAP_CHAIN_F_NONE		(0x00U)
#define	LDAP_CHAIN_F_CHAINING		(0x01U)
98
99
#define	LDAP_CHAIN_F_CACHE_URI		(0x02U)
#define	LDAP_CHAIN_F_RETURN_ERR		(0x04U)
100

101
102
103
104
#define LDAP_CHAIN_ISSET(lc, f)		( ( (lc)->lc_flags & (f) ) == (f) )
#define	LDAP_CHAIN_CHAINING( lc )	LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CHAINING )
#define	LDAP_CHAIN_CACHE_URI( lc )	LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_CACHE_URI )
#define	LDAP_CHAIN_RETURN_ERR( lc )	LDAP_CHAIN_ISSET( (lc), LDAP_CHAIN_F_RETURN_ERR )
105

106
107
108
109
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
	LDAPControl		lc_chaining_ctrl;
	char			lc_chaining_ctrlflag;
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
110
111
} ldap_chain_t;

112
static int ldap_chain_db_init_common( BackendDB	*be );
113
static int ldap_chain_db_init_one( BackendDB *be );
114
static int ldap_chain_db_open_one( BackendDB *be );
115
#define	ldap_chain_db_close_one(be)	(0)
116
#define	ldap_chain_db_destroy_one(be, rs)	(lback)->bi_db_destroy( (be), (rs) )
117

118
119
120
typedef struct ldap_chain_cb_t {
	ldap_chain_status_t	lb_status;
	ldap_chain_t		*lb_lc;
121
	slap_operation_t	lb_op_type;
122
123
124
125
126
127
128
	int			lb_depth;
} ldap_chain_cb_t;

static int
ldap_chain_op(
	Operation	*op,
	SlapReply	*rs,
129
	slap_operation_t op_type,
130
131
132
133
134
135
136
137
138
139
	BerVarray	ref,
	int		depth );

static int
ldap_chain_search(
	Operation	*op,
	SlapReply	*rs,
	BerVarray	ref,
	int		depth );

140
141
static slap_overinst ldapchain;

142
143
144
145
146
147
148
149
150
151
152
153
154
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
static int
chaining_control_add(
		ldap_chain_t	*lc,
		Operation 	*op, 
		LDAPControl	***oldctrlsp )
{
	LDAPControl	**ctrls = NULL;
	int		c = 0;

	*oldctrlsp = op->o_ctrls;

	/* default chaining control not defined */
155
	if ( !LDAP_CHAIN_CHAINING( lc ) ) {
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
		return 0;
	}

	/* already present */
	if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
		return 0;
	}

	/* FIXME: check other incompatibilities */

	/* add to other controls */
	if ( op->o_ctrls ) {
		for ( c = 0; op->o_ctrls[ c ]; c++ )
			/* count them */ ;
	}

	ctrls = ch_calloc( sizeof( LDAPControl *), c + 2 );
	ctrls[ 0 ] = &lc->lc_chaining_ctrl;
	if ( op->o_ctrls ) {
		for ( c = 0; op->o_ctrls[ c ]; c++ ) {
			ctrls[ c + 1 ] = op->o_ctrls[ c ];
		}
	}
	ctrls[ c + 1 ] = NULL;

	op->o_ctrls = ctrls;

	op->o_chaining = lc->lc_chaining_ctrlflag;

	return 0;
}

static int
chaining_control_remove(
		Operation 	*op, 
		LDAPControl	***oldctrlsp )
{
	LDAPControl	**oldctrls = *oldctrlsp;

	/* we assume that the first control is the chaining control
	 * added by the chain overlay, so it's the only one we explicitly 
	 * free */
	if ( op->o_ctrls != oldctrls ) {
199
200
		if ( op->o_ctrls != NULL ) {
			assert( op->o_ctrls[ 0 ] != NULL );
201

202
			free( op->o_ctrls );
203

204
205
			op->o_chaining = 0;
		}
206
207
208
209
210
211
212
213
214
		op->o_ctrls = oldctrls;
	} 

	*oldctrlsp = NULL;

	return 0;
}
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */

215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
static int
ldap_chain_uri_cmp( const void *c1, const void *c2 )
{
	const ldapinfo_t	*li1 = (const ldapinfo_t *)c1;
	const ldapinfo_t	*li2 = (const ldapinfo_t *)c2;

	assert( li1->li_bvuri != NULL );
	assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
	assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );

	assert( li2->li_bvuri != NULL );
	assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
	assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );

	return ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] );
}

static int
ldap_chain_uri_dup( void *c1, void *c2 )
{
	ldapinfo_t	*li1 = (ldapinfo_t *)c1;
	ldapinfo_t	*li2 = (ldapinfo_t *)c2;

	assert( li1->li_bvuri != NULL );
	assert( !BER_BVISNULL( &li1->li_bvuri[ 0 ] ) );
	assert( BER_BVISNULL( &li1->li_bvuri[ 1 ] ) );

	assert( li2->li_bvuri != NULL );
	assert( !BER_BVISNULL( &li2->li_bvuri[ 0 ] ) );
	assert( BER_BVISNULL( &li2->li_bvuri[ 1 ] ) );

	if ( ber_bvcmp( &li1->li_bvuri[ 0 ], &li2->li_bvuri[ 0 ] ) == 0 ) {
		return -1;
	}
249

250
251
252
	return 0;
}

253
254
255
/*
 * Search specific response that strips entryDN from entries
 */
256
static int
257
ldap_chain_cb_search_response( Operation *op, SlapReply *rs )
258
{
259
260
	ldap_chain_cb_t	*lb = (ldap_chain_cb_t *)op->o_callback->sc_private;

261
262
	assert( op->o_tag == LDAP_REQ_SEARCH );

263
	/* if in error, don't proceed any further */
264
	if ( lb->lb_status == LDAP_CH_ERR ) {
265
266
267
		return 0;
	}

268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
	if ( rs->sr_type == REP_SEARCH ) {
		Attribute	**ap = &rs->sr_entry->e_attrs;

		for ( ; *ap != NULL; ap = &(*ap)->a_next ) {
			/* will be generated later by frontend
			 * (a cleaner solution would be that
			 * the frontend checks if it already exists */
			if ( ad_cmp( (*ap)->a_desc, slap_schema.si_ad_entryDN ) == 0 )
			{
				Attribute *a = *ap;

				*ap = (*ap)->a_next;
				attr_free( a );

				/* there SHOULD be one only! */
				break;
			}
		}
286
287
288
289

		/* tell the frontend not to add generated
		 * operational attributes */
		rs->sr_flags |= REP_NO_OPERATIONALS;
290
291
		
		return SLAP_CB_CONTINUE;
292

293
294
295
	} else if ( rs->sr_type == REP_SEARCHREF ) {
		/* if we get it here, it means the library was unable
		 * to chase the referral... */
296
297
298
		if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
			rs->sr_err = ldap_chain_search( op, rs, rs->sr_ref, lb->lb_depth );
		}
299
300

#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
301
		if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
302
303
			switch ( get_continuationBehavior( op ) ) {
			case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
304
				lb->lb_status = LDAP_CH_ERR;
305
				return rs->sr_err = LDAP_X_CANNOT_CHAIN;
306
307
308
309
310
311
312
313

			default:
				break;
			}
		}
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
		return SLAP_CB_CONTINUE;

314
	} else if ( rs->sr_type == REP_RESULT ) {
315
316
317
318
		if ( rs->sr_err == LDAP_REFERRAL
			&& lb->lb_depth < lb->lb_lc->lc_max_depth
			&& rs->sr_ref != NULL )
		{
319
320
			rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_type,
				rs->sr_ref, lb->lb_depth );
321
322
		}

323
		/* back-ldap tried to send result */
324
		lb->lb_status = LDAP_CH_RES;
Howard Chu's avatar
Howard Chu committed
325
326
327
328
		/* don't let other callbacks run, this isn't
		 * the real result for this op.
		 */
		op->o_callback->sc_next = NULL;
329
330
331
332
333
	}

	return 0;
}

334
335
336
337
338
339
340
/*
 * Dummy response that simply traces if back-ldap tried to send 
 * anything to the client
 */
static int
ldap_chain_cb_response( Operation *op, SlapReply *rs )
{
341
342
	ldap_chain_cb_t	*lb = (ldap_chain_cb_t *)op->o_callback->sc_private;

343
	/* if in error, don't proceed any further */
344
	if ( lb->lb_status == LDAP_CH_ERR ) {
345
346
347
		return 0;
	}

348
	if ( rs->sr_type == REP_RESULT ) {
349
retry:;
350
351
352
353
354
355
356
357
358
		switch ( rs->sr_err ) {
		case LDAP_COMPARE_TRUE:
		case LDAP_COMPARE_FALSE:
			if ( op->o_tag != LDAP_REQ_COMPARE ) {
				return rs->sr_err;
			}
			/* fallthru */

		case LDAP_SUCCESS:
359
			lb->lb_status = LDAP_CH_RES;
360
361
362
			break;

		case LDAP_REFERRAL:
363
			if ( lb->lb_depth < lb->lb_lc->lc_max_depth && rs->sr_ref != NULL ) {
364
365
				rs->sr_err = ldap_chain_op( op, rs, lb->lb_op_type,
					rs->sr_ref, lb->lb_depth );
366
367
368
				goto retry;
			}

369
370
371
372
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
			if ( get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
				switch ( get_continuationBehavior( op ) ) {
				case SLAP_CH_RESOLVE_CHAINING_REQUIRED:
373
					lb->lb_status = LDAP_CH_ERR;
374
375
376
377
378
379
380
381
382
383
384
385
					return rs->sr_err = LDAP_X_CANNOT_CHAIN;

				default:
					break;
				}
			}
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */
			break;

		default:
			return rs->sr_err;
		}
386
387
388
389
390
391
392
393
394
395
396
397
398
399

	} else if ( op->o_tag == LDAP_REQ_SEARCH && rs->sr_type == REP_SEARCH )
	{
		/* strip the entryDN attribute, but keep returning results */
		(void)ldap_chain_cb_search_response( op, rs );
	}

	return SLAP_CB_CONTINUE;
}

static int
ldap_chain_op(
	Operation	*op,
	SlapReply	*rs,
400
	slap_operation_t op_type,
401
402
	BerVarray	ref,
	int		depth )
403
404
{
	slap_overinst	*on = (slap_overinst *) op->o_bd->bd_info;
405
	ldap_chain_cb_t	*lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
406
	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
407
408
	struct berval	odn = op->o_req_dn,
			ondn = op->o_req_ndn;
409
	ldapinfo_t	li = { 0 }, *lip = NULL;
410
	struct berval	bvuri[ 2 ] = { { 0 } };
Pierangelo Masarati's avatar
Pierangelo Masarati committed
411
412

	/* NOTE: returned if ref is empty... */
413
414
	int		rc = LDAP_OTHER,
			first_rc;
415

416
417
418
419
420
421
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
	LDAPControl	**ctrls = NULL;
	
	(void)chaining_control_add( lc, op, &ctrls );
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */

422
	li.li_bvuri = bvuri;
423
	first_rc = -1;
424
	for ( ; !BER_BVISNULL( ref ); ref++ ) {
425
		SlapReply	rs2 = { 0 };
426
		LDAPURLDesc	*srv = NULL;
427
428
		req_search_s	save_oq_search = op->oq_search,
				tmp_oq_search = { 0 };
429
430
431
		struct berval	dn = BER_BVNULL,
				pdn = odn,
				ndn = ondn;
432
		char		*filter = NULL;
433
		int		temporary = 0;
434
		int		free_dn = 0;
435
436
437
438
			
		/* We're setting the URI of the first referral;
		 * what if there are more?

Kurt Zeilenga's avatar
Kurt Zeilenga committed
439
Document: RFC 4511
440
441
442
443
444
445
446
447
448
449
450
451
452
453

4.1.10. Referral 
   ...
   If the client wishes to progress the operation, it MUST follow the 
   referral by contacting one of the supported services. If multiple 
   URIs are present, the client assumes that any supported URI may be 
   used to progress the operation. 

		 * so we actually need to follow exactly one,
		 * and we can assume any is fine.
		 */
	
		/* parse reference and use 
		 * proto://[host][:port]/ only */
454
		rc = ldap_url_parse_ext( ref->bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
455
		if ( rc != LDAP_URL_SUCCESS ) {
456
			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: unable to parse ref=\"%s\"\n",
457
				op->o_log_prefix, ref->bv_val );
458

459
460
461
462
463
			/* try next */
			rc = LDAP_OTHER;
			continue;
		}

464
465
466
467
468
469
470
471
472
473
474
		if ( op->o_tag == LDAP_REQ_SEARCH ) {
			if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
				/* RFC 4511: if scope is present, use it */
				tmp_oq_search.rs_scope = srv->lud_scope;

			} else {
				/* RFC 4511: if scope is absent, use original */
				tmp_oq_search.rs_scope = op->ors_scope;
			}
		}

475
476
		rc = LDAP_SUCCESS;
		srv->lud_scope = LDAP_SCOPE_DEFAULT;
477
478
479
480
481
482
483
484
485
486
		dn.bv_val = srv->lud_dn;
		filter = srv->lud_filter;

		/* normalize DN */
		if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
			if ( srv->lud_dn == NULL ) {
				srv->lud_dn = "";
			}

		} else {
487
488
489
490
491
492
493
			ber_str2bv( srv->lud_dn, 0, 0, &dn );
			rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
			if ( rc == LDAP_SUCCESS ) {
				/* remove DN essentially because later on 
				 * ldap_initialize() will parse the URL 
				 * as a comma-separated URL list */
				srv->lud_dn = "";
494
				free_dn = 1;
495
			}
496
		}
497

498
499
500
501
502
503
504
505
506
507
508
509
510
511
		/* prepare filter */
		if ( rc == LDAP_SUCCESS && op->o_tag == LDAP_REQ_SEARCH ) {
			/* filter */
			if ( srv->lud_filter != NULL
				&& srv->lud_filter[0] != '\0'
				&& strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
			{
				/* RFC 4511: if filter is present, use it;
				 * otherwise, use original */
				tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
				if ( tmp_oq_search.rs_filter != NULL ) {
					filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );

				} else {
512
513
					Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": unable to parse filter=\"%s\"\n",
						op->o_log_prefix, ref->bv_val, srv->lud_filter );
514
515
516
517
518
519
520
521
					rc = LDAP_OTHER;
				}
			}
		}
		srv->lud_filter = NULL;

		if ( rc == LDAP_SUCCESS ) {
			li.li_uri = ldap_url_desc2str( srv );
522
		}
523
524

		srv->lud_dn = dn.bv_val;
525
		srv->lud_filter = filter;
526
527
		ldap_free_urldesc( srv );

528
		if ( rc != LDAP_SUCCESS ) {
529
530
531
532
533
			/* try next */
			rc = LDAP_OTHER;
			continue;
		}

534
		if ( li.li_uri == NULL ) {
535
			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to reconstruct URI\n",
536
				op->o_log_prefix, ref->bv_val );
537

538
539
540
541
542
			/* try next */
			rc = LDAP_OTHER;
			goto further_cleanup;
		}

543
544
545
		Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" -> \"%s\"\n",
			op->o_log_prefix, ref->bv_val, li.li_uri );

546
547
548
		op->o_req_dn = pdn;
		op->o_req_ndn = ndn;

549
550
551
552
553
554
555
556
		if ( op->o_tag == LDAP_REQ_SEARCH ) {
			op->ors_scope = tmp_oq_search.rs_scope;
			if ( tmp_oq_search.rs_filter != NULL ) {
				op->ors_filter = tmp_oq_search.rs_filter;
				op->ors_filterstr = tmp_oq_search.rs_filterstr;
			}
		}

557
558
559
560
561
562
563
564
565
566
567
		ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );

		/* Searches for a ldapinfo in the avl tree */
		ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
		lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree, 
			(caddr_t)&li, ldap_chain_uri_cmp );
		ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );

		if ( lip != NULL ) {
			op->o_bd->be_private = (void *)lip;

568
569
570
			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\": URI=\"%s\" found in cache\n",
				op->o_log_prefix, ref->bv_val, li.li_uri );

571
572
573
		} else {
			rc = ldap_chain_db_init_one( op->o_bd );
			if ( rc != 0 ) {
574
575
				Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
					op->o_log_prefix, ref->bv_val, li.li_uri );
576
577
578
579
580
				goto cleanup;
			}
			lip = (ldapinfo_t *)op->o_bd->be_private;
			lip->li_uri = li.li_uri;
			lip->li_bvuri = bvuri;
581
			rc = ldap_chain_db_open_one( op->o_bd );
582
			if ( rc != 0 ) {
583
584
				Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
					op->o_log_prefix, ref->bv_val, li.li_uri );
585
586
				lip->li_uri = NULL;
				lip->li_bvuri = NULL;
587
				(void)ldap_chain_db_destroy_one( op->o_bd, NULL);
588
589
				goto cleanup;
			}
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605

			if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
				ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
				if ( avl_insert( &lc->lc_lai.lai_tree,
					(caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
				{
					/* someone just inserted another;
					 * don't bother, use this and then
					 * just free it */
					temporary = 1;
				}
				ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );

			} else {
				temporary = 1;
			}
606
607
608

			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_op: ref=\"%s\" %s\n",
				op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
609
610
		}

611
		lb->lb_op_type = op_type;
612
613
		lb->lb_depth = depth + 1;

614
		rc = (&lback->bi_op_bind)[ op_type ]( op, &rs2 );
615

616
617
618
619
620
621
622
623
624
625
626
627
628
		/* note the first error */
		if ( first_rc == -1 ) {
			first_rc = rc;
		}

cleanup:;
		ldap_memfree( li.li_uri );
		li.li_uri = NULL;

		if ( temporary ) {
			lip->li_uri = NULL;
			lip->li_bvuri = NULL;
			(void)ldap_chain_db_close_one( op->o_bd );
629
			(void)ldap_chain_db_destroy_one( op->o_bd, NULL );
630
		}
631
632

further_cleanup:;
Howard Chu's avatar
Howard Chu committed
633
634
635
636
637
		if ( op->o_req_dn.bv_val == pdn.bv_val ) {
			op->o_req_dn = odn;
			op->o_req_ndn = ondn;
		}

638
		if ( free_dn ) {
639
			op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
640
			op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
641
		}
642
643
644
645
646
	
		if ( op->o_tag == LDAP_REQ_SEARCH ) {	
			if ( tmp_oq_search.rs_filter != NULL ) {
				filter_free_x( op, tmp_oq_search.rs_filter, 1 );
			}
647

648
649
650
651
652
			if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
				slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
			}

			op->oq_search = save_oq_search;
653
		}
654

655
656
		if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
			*rs = rs2;
657
658
			break;
		}
659
660

		rc = rs2.sr_err;
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
	}

#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
	(void)chaining_control_remove( op, &ctrls );
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */

	if ( rc != LDAP_SUCCESS && first_rc > 0 ) {
		rc = first_rc;
	}

	return rc;
}

static int
ldap_chain_search(
	Operation	*op,
	SlapReply	*rs,
	BerVarray	ref,
	int		depth )

{
	slap_overinst	*on = (slap_overinst *) op->o_bd->bd_info;
	ldap_chain_cb_t	*lb = (ldap_chain_cb_t *)op->o_callback->sc_private;
	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
	ldapinfo_t	li = { 0 }, *lip = NULL;
	struct berval	bvuri[ 2 ] = { { 0 } };

	struct berval	odn = op->o_req_dn,
			ondn = op->o_req_ndn;
690
691
	Entry		*save_entry = rs->sr_entry;
	slap_mask_t	save_flags = rs->sr_flags;
692

693
694
	int		rc = LDAP_OTHER,
			first_rc = -1;
695
696
697
698
699
700
701

#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
	LDAPControl	**ctrls = NULL;
	
	(void)chaining_control_add( lc, op, &ctrls );
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */

702
703
	assert( rs->sr_type == REP_SEARCHREF );

704
705
706
707
708
709
710
711
712
	rs->sr_type = REP_SEARCH;

	/* if we parse the URI then by no means 
	 * we can cache stuff or reuse connections, 
	 * because in back-ldap there's no caching
	 * based on the URI value, which is supposed
	 * to be set once for all (correct?) */
	li.li_bvuri = bvuri;
	for ( ; !BER_BVISNULL( &ref[0] ); ref++ ) {
713
		SlapReply	rs2 = { REP_RESULT };
714
		LDAPURLDesc	*srv;
715
716
		req_search_s	save_oq_search = op->oq_search,
				tmp_oq_search = { 0 };
717
718
719
		struct berval	dn,
				pdn = op->o_req_dn,
				ndn = op->o_req_ndn;
720
		char		*filter = NULL;
721
		int		temporary = 0;
722
		int		free_dn = 0;
723
724
725
726
727

		/* parse reference and use
		 * proto://[host][:port]/ only */
		rc = ldap_url_parse_ext( ref[0].bv_val, &srv, LDAP_PVT_URL_PARSE_NONE );
		if ( rc != LDAP_URL_SUCCESS ) {
728
			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: unable to parse ref=\"%s\"\n",
729
				op->o_log_prefix, ref->bv_val );
730

731
732
733
734
735
			/* try next */
			rs->sr_err = LDAP_OTHER;
			continue;
		}

736
737
738
739
740
741
		if ( srv->lud_scope != LDAP_SCOPE_DEFAULT ) {
			/* RFC 4511: if scope is present, use it */
			tmp_oq_search.rs_scope = srv->lud_scope;

		} else {
			/* RFC 4511: if scope is absent, use original */
742
743
744
745
746
			/* Section 4.5.3: if scope is onelevel, use base */
			if ( op->ors_scope == LDAP_SCOPE_ONELEVEL )
				tmp_oq_search.rs_scope = LDAP_SCOPE_BASE;
			else
				tmp_oq_search.rs_scope = op->ors_scope;
747
748
749
750
751
752
753
		}

		rc = LDAP_SUCCESS;
		srv->lud_scope = LDAP_SCOPE_DEFAULT;
		dn.bv_val = srv->lud_dn;
		filter = srv->lud_filter;

754
		/* normalize DN */
755
756
757
758
759
		if ( srv->lud_dn == NULL || srv->lud_dn[0] == '\0' ) {
			if ( srv->lud_dn == NULL ) {
				srv->lud_dn = "";
			}

760
			if ( save_entry != NULL ) {
761
762
763
				/* use the "right" DN, if available */
				pdn = save_entry->e_name;
				ndn = save_entry->e_nname;
764
			} /* else leave the original req DN in place, if any RFC 4511 */
765
766
767
			
		} else {
			/* RFC 4511: if DN is present, use it */
768
769
770
771
772
773
774
			ber_str2bv( srv->lud_dn, 0, 0, &dn );
			rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn, op->o_tmpmemctx );
			if ( rc == LDAP_SUCCESS ) {
				/* remove DN essentially because later on 
				 * ldap_initialize() will parse the URL 
				 * as a comma-separated URL list */
				srv->lud_dn = "";
775
				free_dn = 1;
776
			}
777
778
		}

779
780
781
782
783
784
785
786
787
788
789
790
791
792
		/* prepare filter */
		if ( rc == LDAP_SUCCESS ) {
			/* filter */
			if ( srv->lud_filter != NULL
				&& srv->lud_filter[0] != '\0'
				&& strcasecmp( srv->lud_filter, "(objectClass=*)" ) != 0 )
			{
				/* RFC 4511: if filter is present, use it;
				 * otherwise, use original */
				tmp_oq_search.rs_filter = str2filter_x( op, srv->lud_filter );
				if ( tmp_oq_search.rs_filter != NULL ) {
					filter2bv_x( op, tmp_oq_search.rs_filter, &tmp_oq_search.rs_filterstr );

				} else {
793
794
					Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": unable to parse filter=\"%s\"\n",
						op->o_log_prefix, ref->bv_val, srv->lud_filter );
795
796
797
					rc = LDAP_OTHER;
				}
			}
798
		}
799
		srv->lud_filter = NULL;
800

801
802
803
804
805
806
807
808
809
		if ( rc == LDAP_SUCCESS ) {
			li.li_uri = ldap_url_desc2str( srv );
		}

		srv->lud_dn = dn.bv_val;
		srv->lud_filter = filter;
		ldap_free_urldesc( srv );

		if ( rc != LDAP_SUCCESS || li.li_uri == NULL ) {
810
			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to reconstruct URI\n",
811
				op->o_log_prefix, ref->bv_val );
812

813
814
815
816
817
			/* try next */
			rc = LDAP_OTHER;
			goto further_cleanup;
		}

818
819
820
		Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" -> \"%s\"\n",
			op->o_log_prefix, ref->bv_val, li.li_uri );

821
822
		op->o_req_dn = pdn;
		op->o_req_ndn = ndn;
823
824
825
826
827
		op->ors_scope = tmp_oq_search.rs_scope;
		if ( tmp_oq_search.rs_filter != NULL ) {
			op->ors_filter = tmp_oq_search.rs_filter;
			op->ors_filterstr = tmp_oq_search.rs_filterstr;
		}
828

829
830
831
832
833
834
835
836
837
838
839
		ber_str2bv( li.li_uri, 0, 0, &li.li_bvuri[ 0 ] );

		/* Searches for a ldapinfo in the avl tree */
		ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
		lip = (ldapinfo_t *)avl_find( lc->lc_lai.lai_tree, 
			(caddr_t)&li, ldap_chain_uri_cmp );
		ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );

		if ( lip != NULL ) {
			op->o_bd->be_private = (void *)lip;

840
841
842
			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\": URI=\"%s\" found in cache\n",
				op->o_log_prefix, ref->bv_val, li.li_uri );

843
844
845
846
		} else {
			/* if none is found, create a temporary... */
			rc = ldap_chain_db_init_one( op->o_bd );
			if ( rc != 0 ) {
847
848
				Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to init back-ldap for URI=\"%s\"\n",
					op->o_log_prefix, ref->bv_val, li.li_uri );
849
850
851
852
853
854
855
				goto cleanup;
			}
			lip = (ldapinfo_t *)op->o_bd->be_private;
			lip->li_uri = li.li_uri;
			lip->li_bvuri = bvuri;
			rc = ldap_chain_db_open_one( op->o_bd );
			if ( rc != 0 ) {
856
857
				Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" unable to open back-ldap for URI=\"%s\"\n",
					op->o_log_prefix, ref->bv_val, li.li_uri );
858
859
				lip->li_uri = NULL;
				lip->li_bvuri = NULL;
860
				(void)ldap_chain_db_destroy_one( op->o_bd, NULL );
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
				goto cleanup;
			}

			if ( LDAP_CHAIN_CACHE_URI( lc ) ) {
				ldap_pvt_thread_mutex_lock( &lc->lc_lai.lai_mutex );
				if ( avl_insert( &lc->lc_lai.lai_tree,
					(caddr_t)lip, ldap_chain_uri_cmp, ldap_chain_uri_dup ) )
				{
					/* someone just inserted another;
					 * don't bother, use this and then
					 * just free it */
					temporary = 1;
				}
				ldap_pvt_thread_mutex_unlock( &lc->lc_lai.lai_mutex );

			} else {
				temporary = 1;
			}
879
880
881

			Debug( LDAP_DEBUG_TRACE, "%s ldap_chain_search: ref=\"%s\" %s\n",
				op->o_log_prefix, ref->bv_val, temporary ? "temporary" : "caching" );
882
883
		}

884
		lb->lb_op_type = op_search;
885
886
887
888
		lb->lb_depth = depth + 1;

		/* FIXME: should we also copy filter and scope?
		 * according to RFC3296, no */
889
		rc = lback->bi_op_search( op, &rs2 );
890
891
892
		if ( first_rc == -1 ) {
			first_rc = rc;
		}
893

894
895
896
cleanup:;
		ldap_memfree( li.li_uri );
		li.li_uri = NULL;
897

898
899
900
		if ( temporary ) {
			lip->li_uri = NULL;
			lip->li_bvuri = NULL;
901
			(void)ldap_chain_db_close_one( op->o_bd );
902
			(void)ldap_chain_db_destroy_one( op->o_bd, NULL );
903
		}
904
		
905
further_cleanup:;
Howard Chu's avatar
Howard Chu committed
906
907
908
909
910
		if ( op->o_req_dn.bv_val == pdn.bv_val ) {
			op->o_req_dn = odn;
			op->o_req_ndn = ondn;
		}

911
		if ( free_dn ) {
912
			op->o_tmpfree( pdn.bv_val, op->o_tmpmemctx );
913
			op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
914
		}
915
916
917

		if ( tmp_oq_search.rs_filter != NULL ) {
			filter_free_x( op, tmp_oq_search.rs_filter, 1 );
918
		}
919
920
921
922
923
924

		if ( !BER_BVISNULL( &tmp_oq_search.rs_filterstr ) ) {
			slap_sl_free( tmp_oq_search.rs_filterstr.bv_val, op->o_tmpmemctx );
		}

		op->oq_search = save_oq_search;
925
		
926
927
		if ( rc == LDAP_SUCCESS && rs2.sr_err == LDAP_SUCCESS ) {
			*rs = rs2;
928
929
			break;
		}
930

931
		rc = rs2.sr_err;
932
933
	}

934
935
936
937
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
	(void)chaining_control_remove( op, &ctrls );
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */

938
	rs->sr_type = REP_SEARCHREF;
939
940
	rs->sr_entry = save_entry;
	rs->sr_flags = save_flags;
941
942
943

	if ( rc != LDAP_SUCCESS ) {
		/* couldn't chase any of the referrals */
944
945
946
947
948
949
		if ( first_rc != -1 ) {
			rc = first_rc;

		} else {
			rc = SLAP_CB_CONTINUE;
		}
950
951
	}

952
953
954
	return rc;
}

955
956
957
static int
ldap_chain_response( Operation *op, SlapReply *rs )
{
958
	slap_overinst	*on = (slap_overinst *)op->o_bd->bd_info;
959
	ldap_chain_t	*lc = (ldap_chain_t *)on->on_bi.bi_private;
960
	BackendDB	db, *bd = op->o_bd;
961
	ldap_chain_cb_t	lb = { 0 };
962
963
	slap_callback	*sc = op->o_callback,
			sc2 = { 0 };
964
	int		rc = 0;
965
	const char	*text = NULL;
966
	const char	*matched;
967
968
969
	BerVarray	ref;
	struct berval	ndn = op->o_ndn;

970
971
	int		sr_err = rs->sr_err;
	slap_reply_t	sr_type = rs->sr_type;
972
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
973
974
975
976
	slap_mask_t	chain_mask = 0;
	ber_len_t	chain_shift = 0;
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */

977
	if ( rs->sr_err != LDAP_REFERRAL && rs->sr_type != REP_SEARCHREF ) {
978
		return SLAP_CB_CONTINUE;
979
	}
Howard Chu's avatar
Howard Chu committed
980
981
982
	if ( !rs->sr_ref ) {
		return SLAP_CB_CONTINUE;
	}
983

984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
#ifdef LDAP_CONTROL_X_CHAINING_BEHAVIOR
	if ( rs->sr_err == LDAP_REFERRAL && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
		switch ( get_resolveBehavior( op ) ) {
		case SLAP_CH_RESOLVE_REFERRALS_PREFERRED:
		case SLAP_CH_RESOLVE_REFERRALS_REQUIRED:
			return SLAP_CB_CONTINUE;

		default:
			chain_mask = SLAP_CH_RESOLVE_MASK;
			chain_shift = SLAP_CH_RESOLVE_SHIFT;
			break;
		}

	} else if ( rs->sr_type == REP_SEARCHREF && get_chaining( op ) > SLAP_CONTROL_IGNORED ) {
		switch ( get_continuationBehavior( op ) ) {
		case SLAP_CH_CONTINUATION_REFERRALS_PREFERRED:
		case SLAP_CH_CONTINUATION_REFERRALS_REQUIRED:
			return SLAP_CB_CONTINUE;

		default:
			chain_mask = SLAP_CH_CONTINUATION_MASK;
			chain_shift = SLAP_CH_CONTINUATION_SHIFT;
			break;
		}
	}
#endif /* LDAP_CONTROL_X_CHAINING_BEHAVIOR */

1011
1012
1013
1014
1015
1016
1017
1018
	/*
	 * TODO: add checks on who/when chain operations; e.g.:
	 *   a) what identities are authorized
	 *   b) what request DN (e.g. only chain requests rooted at <DN>)
	 *   c) what referral URIs
	 *   d) what protocol scheme (e.g. only ldaps://)
	 *   e) what ssf
	 */
1019

1020
1021
1022
1023
	db = *op->o_bd;
	SLAP_DBFLAGS( &db ) &= ~SLAP_DBFLAG_MONITORING;
	op->o_bd = &db;

1024
1025
	text = rs->sr_text;
	rs->sr_text = NULL;
1026
1027
	matched = rs->sr_matched;
	rs->sr_matched = NULL;
Howard Chu's avatar
Howard Chu committed
1028
	ref = rs->sr_ref;
1029
	rs->sr_ref = NULL;
Howard Chu's avatar
Howard Chu committed
1030

1031
	/* we need this to know if back-ldap returned any result */
1032
	lb.lb_lc = lc;
1033
	sc2.sc_next = sc->sc_next;
1034
	sc2.sc_private = &lb;
1035
1036
	sc2.sc_response = ldap_chain_cb_response;
	op->o_callback = &sc2;
1037

1038
1039
1040
1041
	/* Chaining can be performed by a privileged user on behalf
	 * of normal users, using the ProxyAuthz control, by exploiting
	 * the identity assertion feature of back-ldap; see idassert-*
	 * directives in slapd-ldap(5).
1042
1043
1044
	 *
	 * FIXME: the idassert-authcDN is one, will it be fine regardless
	 * of the URI we obtain from the referral?
Howard Chu's avatar
Howard Chu committed
1045
	 */
1046

1047
	switch ( op->o_tag ) {
Howard Chu's avatar
Howard Chu committed
1048
	case LDAP_REQ_BIND: {
1049
1050
1051
		struct berval	rndn = op->o_req_ndn;
		Connection	*conn = op->o_conn;

1052
		/* FIXME: can we really get a referral for binds? */
Howard Chu's avatar
Howard Chu committed
1053
1054
		op->o_req_ndn = slap_empty_bv;
		op->o_conn = NULL;
1055
		rc = ldap_chain_op( op, rs, op_bind, ref, 0 );
Howard Chu's avatar
Howard Chu committed
1056
1057
1058
		op->o_req_ndn = rndn;
		op->o_conn = conn;
		}
1059
		break;
1060

1061
	case LDAP_REQ_ADD:
1062
		rc = ldap_chain_op( op, rs, op_add, ref, 0 );
1063
		break;
1064

1065
	case LDAP_REQ_DELETE:
1066
		rc = ldap_chain_op( op, rs, op_delete, ref, 0 );
1067
		break;
1068

1069
	case LDAP_REQ_MODRDN:
1070
		rc = ldap_chain_op( op, rs, op_modrdn, ref, 0 );
1071
	    	break;
1072

1073
	case LDAP_REQ_MODIFY:
1074
		rc = ldap_chain_op( op, rs, op_modify, ref, 0 );
1075
		break;
1076

1077
	case LDAP_REQ_COMPARE:
1078
		rc = ldap_chain_op( op, rs, op_compare, ref, 0 );
1079
1080
1081
		if ( rs->sr_err == LDAP_COMPARE_TRUE || rs->sr_err == LDAP_COMPARE_FALSE ) {
			rc = LDAP_SUCCESS;
		}
1082
		break;
1083

1084
	case LDAP_REQ_SEARCH:
1085
		if ( rs->sr_type == REP_SEARCHREF ) {
1086
			sc2.sc_response = ldap_chain_cb_search_response;
1087
			rc = ldap_chain_search( op, rs, ref, 0 );
1088
1089
			
		} else {
1090
1091
1092
1093
1094
			/* we might get here before any database actually 
			 * performed a search; in those cases, we need
			 * to check limits, to make sure safe defaults
			 * are in place */
			if ( op->ors_limit != NULL || limits_check( op, rs ) == 0 ) {
1095
				rc = ldap_chain_op( op, rs, op_search, ref, 0 );
1096
1097
1098
			} else {
				rc = SLAP_CB_CONTINUE;
			}
1099
		}
1100
	    	break;
1101

1102
	case LDAP_REQ_EXTENDED:
1103
		rc = ldap_chain_op( op, rs, op_extended, ref, 0 );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
1104
1105
1106
		/* FIXME: ldap_back_extended() by design 
		 * doesn't send result; frontend is expected
		 * to send it... */
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
1107
		/* FIXME: what about chaining? */
Pierangelo Masarati's avatar
Pierangelo Masarati committed
1108
		if ( rc != SLAPD_ABANDON ) {
1109
			rs->sr_err = rc;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
1110
			send_ldap_extended( op, rs );
1111
			rc = LDAP_SUCCESS;
Pierangelo Masarati's avatar
Pierangelo Masarati committed
1112
		}
1113
		lb.lb_status = LDAP_CH_RES;
1114
		break;
1115

1116
1117
1118
1119
	default:
		rc = SLAP_CB_CONTINUE;
		break;
	}
1120

1121
1122
1123
1124
1125
1126
	switch ( rc ) {
	case SLAPD_ABANDON:
		goto dont_chain;

	case LDAP_SUCCESS:
	case LDAP_REFERRAL:
1127
		sr_err = rs->sr_err;
1128
		/* slapd-ldap sent response */
1129
		if ( !op->o_abandon && lb.lb_status != LDAP_CH_RES ) {
1130
			/* FIXME: should we send response? */
Pierangelo Masarati's avatar
cleanup    
Pierangelo Masarati committed
1131
1132
1133
			Debug( LDAP_DEBUG_ANY,
				"%s: ldap_chain_response: "
				"overlay should have sent result.\n",
1134
				op->o_log_prefix );
1135
		}