search.c 36.1 KB
Newer Older
Howard Chu's avatar
Howard Chu committed
1
2
3
4
/* search.c - search operation */
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
5
 * Copyright 2000-2020 The OpenLDAP Foundation.
Howard Chu's avatar
Howard Chu committed
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 * 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>.
 */

#include "portable.h"

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

#include "back-mdb.h"
#include "idl.h"

static int base_candidate(
	BackendDB	*be,
	Entry	*e,
	ID		*ids );

static int search_candidates(
	Operation *op,
	SlapReply *rs,
	Entry *e,
Howard Chu's avatar
Howard Chu committed
34
	IdScopes *isc,
Howard Chu's avatar
Howard Chu committed
35
	MDB_cursor *mci,
Howard Chu's avatar
Howard Chu committed
36
	ID	*ids,
37
	ID *stack );
Howard Chu's avatar
Howard Chu committed
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100

static int parse_paged_cookie( Operation *op, SlapReply *rs );

static void send_paged_response( 
	Operation *op,
	SlapReply *rs,
	ID  *lastid,
	int tentries );

/* Dereference aliases for a single alias entry. Return the final
 * dereferenced entry on success, NULL on any failure.
 */
static Entry * deref_base (
	Operation *op,
	SlapReply *rs,
	Entry *e,
	Entry **matched,
	MDB_txn *txn,
	ID	*tmp,
	ID	*visited )
{
	struct berval ndn;

	rs->sr_err = LDAP_ALIAS_DEREF_PROBLEM;
	rs->sr_text = "maximum deref depth exceeded";

	for (;;) {
		/* Remember the last entry we looked at, so we can
		 * report broken links
		 */
		*matched = e;

		if (MDB_IDL_N(tmp) >= op->o_bd->be_max_deref_depth) {
			e = NULL;
			break;
		}

		/* If this is part of a subtree or onelevel search,
		 * have we seen this ID before? If so, quit.
		 */
		if ( visited && mdb_idl_insert( visited, e->e_id ) ) {
			e = NULL;
			break;
		}

		/* If we've seen this ID during this deref iteration,
		 * we've hit a loop.
		 */
		if ( mdb_idl_insert( tmp, e->e_id ) ) {
			rs->sr_err = LDAP_ALIAS_PROBLEM;
			rs->sr_text = "circular alias";
			e = NULL;
			break;
		}

		/* If there was a problem getting the aliasedObjectName,
		 * get_alias_dn will have set the error status.
		 */
		if ( get_alias_dn(e, &ndn, &rs->sr_err, &rs->sr_text) ) {
			e = NULL;
			break;
		}

101
		rs->sr_err = mdb_dn2entry( op, txn, NULL, &ndn, &e, NULL, 0 );
Howard Chu's avatar
Howard Chu committed
102
103
104
105
106
107
108
109
110
		if (rs->sr_err) {
			rs->sr_err = LDAP_ALIAS_PROBLEM;
			rs->sr_text = "aliasedObject not found";
			break;
		}

		/* Free the previous entry, continue to work with the
		 * one we just retrieved.
		 */
Howard Chu's avatar
Howard Chu committed
111
		mdb_entry_return( op, *matched );
Howard Chu's avatar
Howard Chu committed
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131

		/* We found a regular entry. Return this to the caller.
		 */
		if (!is_entry_alias(e)) {
			rs->sr_err = LDAP_SUCCESS;
			rs->sr_text = NULL;
			break;
		}
	}
	return e;
}

/* Look for and dereference all aliases within the search scope.
 * Requires "stack" to be able to hold 6 levels of DB_SIZE IDLs.
 * Of course we're hardcoded to require a minimum of 8 UM_SIZE
 * IDLs so this is never a problem.
 */
static int search_aliases(
	Operation *op,
	SlapReply *rs,
132
	ID e_id,
Howard Chu's avatar
Howard Chu committed
133
	IdScopes *isc,
Howard Chu's avatar
Howard Chu committed
134
	MDB_cursor *mci,
Howard Chu's avatar
Howard Chu committed
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
	ID *stack )
{
	ID *aliases, *curscop, *visited, *newsubs, *oldsubs, *tmp;
	ID cursora, ida, cursoro, ido;
	Entry *matched, *a;
	struct berval bv_alias = BER_BVC( "alias" );
	AttributeAssertion aa_alias = ATTRIBUTEASSERTION_INIT;
	Filter	af;

	aliases = stack;	/* IDL of all aliases in the database */
	curscop = aliases + MDB_IDL_DB_SIZE;	/* Aliases in the current scope */
	visited = curscop + MDB_IDL_DB_SIZE;	/* IDs we've seen in this search */
	newsubs = visited + MDB_IDL_DB_SIZE;	/* New subtrees we've added */
	oldsubs = newsubs + MDB_IDL_DB_SIZE;	/* Subtrees added previously */
	tmp = oldsubs + MDB_IDL_DB_SIZE;	/* Scratch space for deref_base() */

	af.f_choice = LDAP_FILTER_EQUALITY;
	af.f_ava = &aa_alias;
	af.f_av_desc = slap_schema.si_ad_objectClass;
	af.f_av_value = bv_alias;
	af.f_next = NULL;

	/* Find all aliases in database */
	MDB_IDL_ZERO( aliases );
Howard Chu's avatar
Howard Chu committed
159
	rs->sr_err = mdb_filter_candidates( op, isc->mt, &af, aliases,
Howard Chu's avatar
Howard Chu committed
160
		curscop, visited );
161
	if (rs->sr_err != LDAP_SUCCESS || MDB_IDL_IS_ZERO( aliases )) {
Howard Chu's avatar
Howard Chu committed
162
163
		return rs->sr_err;
	}
Howard Chu's avatar
Howard Chu committed
164
165
166
167
168
169
	if ( op->ors_limit	/* isroot == FALSE */ &&
		op->ors_limit->lms_s_unchecked != -1 &&
		MDB_IDL_N( aliases ) > (unsigned) op->ors_limit->lms_s_unchecked )
	{
		return LDAP_ADMINLIMIT_EXCEEDED;
	}
Howard Chu's avatar
Howard Chu committed
170
	oldsubs[0] = 1;
171
	oldsubs[1] = e_id;
Howard Chu's avatar
Howard Chu committed
172
173
174
175
176
177
178
179
180
181
182

	MDB_IDL_ZERO( visited );
	MDB_IDL_ZERO( newsubs );

	cursoro = 0;
	ido = mdb_idl_first( oldsubs, &cursoro );

	for (;;) {
		/* Set curscop to only the aliases in the current scope. Start with
		 * all the aliases, then get the intersection with the scope.
		 */
Howard Chu's avatar
Howard Chu committed
183
		rs->sr_err = mdb_idscope( op, isc->mt, e_id, aliases, curscop );
Howard Chu's avatar
Howard Chu committed
184
185
186
187
188
189

		/* Dereference all of the aliases in the current scope. */
		cursora = 0;
		for (ida = mdb_idl_first(curscop, &cursora); ida != NOID;
			ida = mdb_idl_next(curscop, &cursora))
		{
Howard Chu's avatar
Howard Chu committed
190
			rs->sr_err = mdb_id2entry(op, mci, ida, &a);
Howard Chu's avatar
Howard Chu committed
191
192
193
194
195
196
197
198
			if (rs->sr_err != LDAP_SUCCESS) {
				continue;
			}

			/* This should only happen if the curscop IDL has maxed out and
			 * turned into a range that spans IDs indiscriminately
			 */
			if (!is_entry_alias(a)) {
Howard Chu's avatar
Howard Chu committed
199
				mdb_entry_return(op, a);
Howard Chu's avatar
Howard Chu committed
200
201
202
203
204
				continue;
			}

			/* Actually dereference the alias */
			MDB_IDL_ZERO(tmp);
Howard Chu's avatar
Howard Chu committed
205
			a = deref_base( op, rs, a, &matched, isc->mt,
Howard Chu's avatar
Howard Chu committed
206
207
208
209
210
				tmp, visited );
			if (a) {
				/* If the target was not already in our current scopes,
				 * make note of it in the newsubs list.
				 */
Howard Chu's avatar
Howard Chu committed
211
212
213
				ID2 mid;
				mid.mid = a->e_id;
				mid.mval.mv_data = NULL;
Howard Chu's avatar
Howard Chu committed
214
215
216
217
218
219
220
221
222
				if (op->ors_scope == LDAP_SCOPE_SUBTREE) {
					isc->id = a->e_id;
					/* if ID is a child of any of our current scopes,
					 * ignore it, it's already included.
					 */
					if (mdb_idscopechk(op, isc))
						goto skip;
				}
				if (mdb_id2l_insert(isc->scopes, &mid) == 0) {
Howard Chu's avatar
Howard Chu committed
223
224
					mdb_idl_insert(newsubs, a->e_id);
				}
Howard Chu's avatar
Howard Chu committed
225
skip:			mdb_entry_return( op, a );
Howard Chu's avatar
Howard Chu committed
226
227
228
229
230

			} else if (matched) {
				/* Alias could not be dereferenced, or it deref'd to
				 * an ID we've already seen. Ignore it.
				 */
Howard Chu's avatar
Howard Chu committed
231
				mdb_entry_return( op, matched );
Howard Chu's avatar
Howard Chu committed
232
				rs->sr_text = NULL;
Howard Chu's avatar
Howard Chu committed
233
				rs->sr_err = 0;
Howard Chu's avatar
Howard Chu committed
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
			}
		}
		/* If this is a OneLevel search, we're done; oldsubs only had one
		 * ID in it. For a Subtree search, oldsubs may be a list of scope IDs.
		 */
		if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ) break;
nextido:
		ido = mdb_idl_next( oldsubs, &cursoro );
		
		/* If we're done processing the old scopes, did we add any new
		 * scopes in this iteration? If so, go back and do those now.
		 */
		if (ido == NOID) {
			if (MDB_IDL_IS_ZERO(newsubs)) break;
			MDB_IDL_CPY(oldsubs, newsubs);
			MDB_IDL_ZERO(newsubs);
			cursoro = 0;
			ido = mdb_idl_first( oldsubs, &cursoro );
		}

		/* Find the entry corresponding to the next scope. If it can't
		 * be found, ignore it and move on. This should never happen;
		 * we should never see the ID of an entry that doesn't exist.
		 */
258
259
260
261
262
263
264
		{
			MDB_val edata;
			rs->sr_err = mdb_id2edata(op, mci, ido, &edata);
			if ( rs->sr_err != MDB_SUCCESS ) {
				goto nextido;
			}
			e_id = ido;
Howard Chu's avatar
Howard Chu committed
265
266
267
268
269
270
271
272
273
		}
	}
	return rs->sr_err;
}

/* Get the next ID from the DB. Used if the candidate list is
 * a range and simple iteration hits missing entryIDs
 */
static int
Howard Chu's avatar
Howard Chu committed
274
mdb_get_nextid(MDB_cursor *mci, ID *cursor)
Howard Chu's avatar
Howard Chu committed
275
276
277
278
279
280
281
282
{
	MDB_val key;
	ID id;
	int rc;

	id = *cursor + 1;
	key.mv_data = &id;
	key.mv_size = sizeof(ID);
Howard Chu's avatar
Howard Chu committed
283
	rc = mdb_cursor_get( mci, &key, NULL, MDB_SET_RANGE );
Howard Chu's avatar
Howard Chu committed
284
285
286
287
288
289
	if ( rc )
		return rc;
	memcpy( cursor, key.mv_data, sizeof(ID));
	return 0;
}

Howard Chu's avatar
Howard Chu committed
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
static void scope_chunk_free( void *key, void *data )
{
	ID2 *p1, *p2;
	for (p1 = data; p1; p1 = p2) {
		p2 = p1[0].mval.mv_data;
		ber_memfree_x(p1, NULL);
	}
}

static ID2 *scope_chunk_get( Operation *op )
{
	ID2 *ret = NULL;

	ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)scope_chunk_get,
			(void *)&ret, NULL );
	if ( !ret ) {
		ret = ch_malloc( MDB_IDL_UM_SIZE * sizeof( ID2 ));
	} else {
		void *r2 = ret[0].mval.mv_data;
		ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)scope_chunk_get,
			r2, scope_chunk_free, NULL, NULL );
	}
	return ret;
}

static void scope_chunk_ret( Operation *op, ID2 *scopes )
{
	void *ret = NULL;

	ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)scope_chunk_get,
			&ret, NULL );
	scopes[0].mval.mv_data = ret;
	ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)scope_chunk_get,
			(void *)scopes, scope_chunk_free, NULL, NULL );
}

326
327
static void *search_stack( Operation *op );

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
328
329
typedef struct ww_ctx {
	MDB_txn *txn;
Howard Chu's avatar
Howard Chu committed
330
331
332
	MDB_cursor *mcd;	/* if set, save cursor context */
	ID key;
	MDB_val data;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
333
	int flag;
334
	unsigned nentries;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
335
336
} ww_ctx;

Howard Chu's avatar
Howard Chu committed
337
338
339
340
341
342
343
344
345
346
/* ITS#7904 if we get blocked while writing results to client,
 * release the current reader txn and reacquire it after we
 * unblock.
 * Slight problem - if we're doing a scope-based walk (mdb_dn2id_walk)
 * to return results, we need to remember the state of the mcd cursor.
 * If the node that cursor was pointing to gets deleted while we're
 * blocked, we may be unable to restore the cursor position. In that
 * case return an LDAP_BUSY error - let the client know this search
 * couldn't succeed, but might succeed on a retry.
 */
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
static void
mdb_rtxn_snap( Operation *op, ww_ctx *ww )
{
	/* save cursor position and release read txn */
	if ( ww->mcd ) {
		MDB_val key, data;
		mdb_cursor_get( ww->mcd, &key, &data, MDB_GET_CURRENT );
		memcpy( &ww->key, key.mv_data, sizeof(ID) );
		ww->data.mv_size = data.mv_size;
		ww->data.mv_data = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx );
		memcpy(ww->data.mv_data, data.mv_data, data.mv_size);
	}
	mdb_txn_reset( ww->txn );
	ww->flag = 1;
}

Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
363
364
365
366
367
static void
mdb_writewait( Operation *op, slap_callback *sc )
{
	ww_ctx *ww = sc->sc_private;
	if ( !ww->flag ) {
368
		mdb_rtxn_snap( op, ww );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
369
370
371
	}
}

Howard Chu's avatar
Howard Chu committed
372
static int
373
mdb_waitfixup( Operation *op, ww_ctx *ww, MDB_cursor *mci, MDB_cursor *mcd, IdScopes *isc )
Howard Chu's avatar
Howard Chu committed
374
{
375
	MDB_val key;
Howard Chu's avatar
Howard Chu committed
376
377
378
379
380
	int rc = 0;
	ww->flag = 0;
	mdb_txn_renew( ww->txn );
	mdb_cursor_renew( ww->txn, mci );
	mdb_cursor_renew( ww->txn, mcd );
381

382
383
384
	key.mv_size = sizeof(ID);
	if ( ww->mcd ) {	/* scope-based search using dn2id_walk */
		MDB_val data;
385

386
387
		if ( isc->numrdns )
			mdb_dn2id_wrestore( op, isc );
388

Howard Chu's avatar
Howard Chu committed
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
		key.mv_data = &ww->key;
		data = ww->data;
		rc = mdb_cursor_get( mcd, &key, &data, MDB_GET_BOTH );
		if ( rc == MDB_NOTFOUND ) {
			data = ww->data;
			rc = mdb_cursor_get( mcd, &key, &data, MDB_GET_BOTH_RANGE );
			/* the loop will skip this node using NEXT_DUP but we want it
			 * sent, so go back one space first
			 */
			if ( rc == MDB_SUCCESS )
				mdb_cursor_get( mcd, &key, &data, MDB_PREV_DUP );
			else
				rc = LDAP_BUSY;
		} else if ( rc ) {
			rc = LDAP_OTHER;
		}
		op->o_tmpfree( ww->data.mv_data, op->o_tmpmemctx );
		ww->data.mv_data = NULL;
407
408
409
410
411
412
413
414
	} else if ( isc->scopes[0].mid > 1 ) {	/* candidate-based search */
		int i;
		for ( i=1; i<isc->scopes[0].mid; i++ ) {
			if ( !isc->scopes[i].mval.mv_data )
				continue;
			key.mv_data = &isc->scopes[i].mid;
			mdb_cursor_get( mcd, &key, &isc->scopes[i].mval, MDB_SET );
		}
Howard Chu's avatar
Howard Chu committed
415
416
417
418
	}
	return rc;
}

Howard Chu's avatar
Howard Chu committed
419
420
421
422
int
mdb_search( Operation *op, SlapReply *rs )
{
	struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
Howard Chu's avatar
Howard Chu committed
423
	ID		id, cursor, nsubs, ncand, cscope;
Howard Chu's avatar
Howard Chu committed
424
425
	ID		lastid = NOID;
	ID		candidates[MDB_IDL_UM_SIZE];
Howard Chu's avatar
Howard Chu committed
426
	ID		iscopes[MDB_IDL_DB_SIZE];
Howard Chu's avatar
Howard Chu committed
427
	ID2		*scopes;
428
	void	*stack;
Howard Chu's avatar
Howard Chu committed
429
	Entry		*e = NULL, *base = NULL;
Howard Chu's avatar
Howard Chu committed
430
431
432
433
434
435
	Entry		*matched = NULL;
	AttributeName	*attrs;
	slap_mask_t	mask;
	time_t		stoptime;
	int		manageDSAit;
	int		tentries = 0;
Howard Chu's avatar
Howard Chu committed
436
	IdScopes	isc;
Howard Chu's avatar
Howard Chu committed
437
	MDB_cursor	*mci, *mcd;
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
438
439
	ww_ctx wwctx;
	slap_callback cb = { 0 };
Howard Chu's avatar
Howard Chu committed
440

441
	mdb_op_info	opinfo = {{{0}}}, *moi = &opinfo;
Howard Chu's avatar
Howard Chu committed
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
	MDB_txn			*ltid = NULL;

	Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(mdb_search) "\n", 0, 0, 0);
	attrs = op->oq_search.rs_attrs;

	manageDSAit = get_manageDSAit( op );

	rs->sr_err = mdb_opinfo_get( op, mdb, 1, &moi );
	switch(rs->sr_err) {
	case 0:
		break;
	default:
		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
		return rs->sr_err;
	}

	ltid = moi->moi_txn;

Howard Chu's avatar
Howard Chu committed
460
461
462
	rs->sr_err = mdb_cursor_open( ltid, mdb->mi_id2entry, &mci );
	if ( rs->sr_err ) {
		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
Howard Chu's avatar
Howard Chu committed
463
		return rs->sr_err;
Howard Chu's avatar
Howard Chu committed
464
465
	}

Howard Chu's avatar
Howard Chu committed
466
467
468
469
470
471
472
	rs->sr_err = mdb_cursor_open( ltid, mdb->mi_dn2id, &mcd );
	if ( rs->sr_err ) {
		mdb_cursor_close( mci );
		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
		return rs->sr_err;
	}

Howard Chu's avatar
Howard Chu committed
473
	scopes = scope_chunk_get( op );
474
	stack = search_stack( op );
Howard Chu's avatar
Howard Chu committed
475
	isc.mt = ltid;
Howard Chu's avatar
Howard Chu committed
476
	isc.mc = mcd;
Howard Chu's avatar
Howard Chu committed
477
	isc.scopes = scopes;
Howard Chu's avatar
Howard Chu committed
478
	isc.oscope = op->ors_scope;
479
	isc.sctmp = stack;
Howard Chu's avatar
Howard Chu committed
480

Howard Chu's avatar
Howard Chu committed
481
482
483
484
485
	if ( op->ors_deref & LDAP_DEREF_FINDING ) {
		MDB_IDL_ZERO(candidates);
	}
dn2entry_retry:
	/* get entry with reader lock */
Howard Chu's avatar
Howard Chu committed
486
	rs->sr_err = mdb_dn2entry( op, ltid, mcd, &op->o_req_ndn, &e, &nsubs, 1 );
Howard Chu's avatar
Howard Chu committed
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513

	switch(rs->sr_err) {
	case MDB_NOTFOUND:
		matched = e;
		e = NULL;
		break;
	case 0:
		break;
	case LDAP_BUSY:
		send_ldap_error( op, rs, LDAP_BUSY, "ldap server busy" );
		goto done;
	default:
		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
		goto done;
	}

	if ( op->ors_deref & LDAP_DEREF_FINDING ) {
		if ( matched && is_entry_alias( matched )) {
			struct berval stub;

			stub.bv_val = op->o_req_ndn.bv_val;
			stub.bv_len = op->o_req_ndn.bv_len - matched->e_nname.bv_len - 1;
			e = deref_base( op, rs, matched, &matched, ltid,
				candidates, NULL );
			if ( e ) {
				build_new_dn( &op->o_req_ndn, &e->e_nname, &stub,
					op->o_tmpmemctx );
Howard Chu's avatar
Howard Chu committed
514
				mdb_entry_return(op, e);
Howard Chu's avatar
Howard Chu committed
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
				matched = NULL;
				goto dn2entry_retry;
			}
		} else if ( e && is_entry_alias( e )) {
			e = deref_base( op, rs, e, &matched, ltid,
				candidates, NULL );
		}
	}

	if ( e == NULL ) {
		struct berval matched_dn = BER_BVNULL;

		if ( matched != NULL ) {
			BerVarray erefs = NULL;

			/* return referral only if "disclose"
			 * is granted on the object */
			if ( ! access_allowed( op, matched,
						slap_schema.si_ad_entry,
						NULL, ACL_DISCLOSE, NULL ) )
			{
				rs->sr_err = LDAP_NO_SUCH_OBJECT;

			} else {
				ber_dupbv( &matched_dn, &matched->e_name );

				erefs = is_entry_referral( matched )
					? get_entry_referrals( op, matched )
					: NULL;
				if ( rs->sr_err == MDB_NOTFOUND )
					rs->sr_err = LDAP_REFERRAL;
				rs->sr_matched = matched_dn.bv_val;
			}

Howard Chu's avatar
Howard Chu committed
549
			mdb_entry_return(op, matched);
Howard Chu's avatar
Howard Chu committed
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
			matched = NULL;

			if ( erefs ) {
				rs->sr_ref = referral_rewrite( erefs, &matched_dn,
					&op->o_req_dn, op->oq_search.rs_scope );
				ber_bvarray_free( erefs );
			}

		} else {
			rs->sr_ref = referral_rewrite( default_referral,
				NULL, &op->o_req_dn, op->oq_search.rs_scope );
			rs->sr_err = rs->sr_ref != NULL ? LDAP_REFERRAL : LDAP_NO_SUCH_OBJECT;
		}

		send_ldap_result( op, rs );

		if ( rs->sr_ref ) {
			ber_bvarray_free( rs->sr_ref );
			rs->sr_ref = NULL;
		}
		if ( !BER_BVISNULL( &matched_dn ) ) {
			ber_memfree( matched_dn.bv_val );
			rs->sr_matched = NULL;
		}
		goto done;
	}

	/* NOTE: __NEW__ "search" access is required
	 * on searchBase object */
	if ( ! access_allowed_mask( op, e, slap_schema.si_ad_entry,
				NULL, ACL_SEARCH, NULL, &mask ) )
	{
		if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
			rs->sr_err = LDAP_NO_SUCH_OBJECT;
		} else {
			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
		}

Howard Chu's avatar
Howard Chu committed
588
		mdb_entry_return( op,e);
Howard Chu's avatar
Howard Chu committed
589
590
591
592
593
594
595
596
597
598
599
600
601
602
		send_ldap_result( op, rs );
		goto done;
	}

	if ( !manageDSAit && is_entry_referral( e ) ) {
		/* entry is a referral */
		struct berval matched_dn = BER_BVNULL;
		BerVarray erefs = NULL;
		
		ber_dupbv( &matched_dn, &e->e_name );
		erefs = get_entry_referrals( op, e );

		rs->sr_err = LDAP_REFERRAL;

Howard Chu's avatar
Howard Chu committed
603
		mdb_entry_return( op, e );
Howard Chu's avatar
Howard Chu committed
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
		e = NULL;

		if ( erefs ) {
			rs->sr_ref = referral_rewrite( erefs, &matched_dn,
				&op->o_req_dn, op->oq_search.rs_scope );
			ber_bvarray_free( erefs );

			if ( !rs->sr_ref ) {
				rs->sr_text = "bad_referral object";
			}
		}

		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(mdb_search) ": entry is referral\n",
			0, 0, 0 );

		rs->sr_matched = matched_dn.bv_val;
		send_ldap_result( op, rs );

		ber_bvarray_free( rs->sr_ref );
		rs->sr_ref = NULL;
		ber_memfree( matched_dn.bv_val );
		rs->sr_matched = NULL;
		goto done;
	}

	if ( get_assert( op ) &&
		( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
	{
		rs->sr_err = LDAP_ASSERTION_FAILED;
Howard Chu's avatar
Howard Chu committed
634
		mdb_entry_return( op,e);
Howard Chu's avatar
Howard Chu committed
635
636
637
638
639
640
641
		send_ldap_result( op, rs );
		goto done;
	}

	/* compute it anyway; root does not use it */
	stoptime = op->o_time + op->ors_tlimit;

Howard Chu's avatar
Howard Chu committed
642
	base = e;
Howard Chu's avatar
Howard Chu committed
643
644
645
646
647

	e = NULL;

	/* select candidates */
	if ( op->oq_search.rs_scope == LDAP_SCOPE_BASE ) {
Howard Chu's avatar
Howard Chu committed
648
		rs->sr_err = base_candidate( op->o_bd, base, candidates );
649
		scopes[0].mid = 0;
Howard Chu's avatar
Howard Chu committed
650
		ncand = 1;
Howard Chu's avatar
Howard Chu committed
651
	} else {
Howard Chu's avatar
Howard Chu committed
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
		if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ) {
			size_t nkids;
			MDB_val key, data;
			key.mv_data = &base->e_id;
			key.mv_size = sizeof( ID );
			mdb_cursor_get( mcd, &key, &data, MDB_SET );
			mdb_cursor_count( mcd, &nkids );
			nsubs = nkids - 1;
		} else if ( !base->e_id ) {
			/* we don't maintain nsubs for entryID 0.
			 * just grab entry count from id2entry stat
			 */
			MDB_stat ms;
			mdb_stat( ltid, mdb->mi_id2entry, &ms );
			nsubs = ms.ms_entries;
		}
Howard Chu's avatar
Howard Chu committed
668
		MDB_IDL_ZERO( candidates );
Howard Chu's avatar
Howard Chu committed
669
670
671
		scopes[0].mid = 1;
		scopes[1].mid = base->e_id;
		scopes[1].mval.mv_data = NULL;
Howard Chu's avatar
Howard Chu committed
672
		rs->sr_err = search_candidates( op, rs, base,
Howard Chu's avatar
Howard Chu committed
673
			&isc, mci, candidates, stack );
Howard Chu's avatar
Howard Chu committed
674
675
676
677

		if ( rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED )
			goto adminlimit;

Howard Chu's avatar
Howard Chu committed
678
679
680
681
682
683
684
685
686
687
688
		ncand = MDB_IDL_N( candidates );
		if ( !base->e_id || ncand == NOID ) {
			/* grab entry count from id2entry stat
			 */
			MDB_stat ms;
			mdb_stat( ltid, mdb->mi_id2entry, &ms );
			if ( !base->e_id )
				nsubs = ms.ms_entries;
			if ( ncand == NOID )
				ncand = ms.ms_entries;
		}
Howard Chu's avatar
Howard Chu committed
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
	}

	/* start cursor at beginning of candidates.
	 */
	cursor = 0;

	if ( candidates[0] == 0 ) {
		Debug( LDAP_DEBUG_TRACE,
			LDAP_XSTRING(mdb_search) ": no candidates\n",
			0, 0, 0 );

		goto nochange;
	}

	/* if not root and candidates exceed to-be-checked entries, abort */
	if ( op->ors_limit	/* isroot == FALSE */ &&
		op->ors_limit->lms_s_unchecked != -1 &&
Howard Chu's avatar
Howard Chu committed
706
		ncand > (unsigned) op->ors_limit->lms_s_unchecked )
Howard Chu's avatar
Howard Chu committed
707
708
	{
		rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
Howard Chu's avatar
Howard Chu committed
709
adminlimit:
Howard Chu's avatar
Howard Chu committed
710
711
712
713
714
715
716
717
		send_ldap_result( op, rs );
		rs->sr_err = LDAP_SUCCESS;
		goto done;
	}

	if ( op->ors_limit == NULL	/* isroot == TRUE */ ||
		!op->ors_limit->lms_s_pr_hide )
	{
Howard Chu's avatar
Howard Chu committed
718
		tentries = ncand;
Howard Chu's avatar
Howard Chu committed
719
720
	}

Howard Chu's avatar
Howard Chu committed
721
722
723
724
725
726
727
728
729
730
731
	wwctx.flag = 0;
	/* If we're running in our own read txn */
	if (  moi == &opinfo ) {
		cb.sc_writewait = mdb_writewait;
		cb.sc_private = &wwctx;
		wwctx.txn = ltid;
		wwctx.mcd = NULL;
		cb.sc_next = op->o_callback;
		op->o_callback = &cb;
	}

Howard Chu's avatar
Howard Chu committed
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
	if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
		PagedResultsState *ps = op->o_pagedresults_state;
		/* deferred cookie parsing */
		rs->sr_err = parse_paged_cookie( op, rs );
		if ( rs->sr_err != LDAP_SUCCESS ) {
			send_ldap_result( op, rs );
			goto done;
		}

		cursor = (ID) ps->ps_cookie;
		if ( cursor && ps->ps_size == 0 ) {
			rs->sr_err = LDAP_SUCCESS;
			rs->sr_text = "search abandoned by pagedResult size=0";
			send_ldap_result( op, rs );
			goto done;
		}
		id = mdb_idl_first( candidates, &cursor );
		if ( id == NOID ) {
			Debug( LDAP_DEBUG_TRACE, 
				LDAP_XSTRING(mdb_search)
				": no paged results candidates\n",
				0, 0, 0 );
			send_paged_response( op, rs, &lastid, 0 );

			rs->sr_err = LDAP_OTHER;
			goto done;
		}
		if ( id == (ID)ps->ps_cookie )
			id = mdb_idl_next( candidates, &cursor );
761
		nsubs = ncand;	/* always bypass scope'd search */
Howard Chu's avatar
Howard Chu committed
762
763
		goto loop_begin;
	}
Howard Chu's avatar
Howard Chu committed
764
765
766
	if ( nsubs < ncand ) {
		int rc;
		/* Do scope-based search */
Howard Chu's avatar
Howard Chu committed
767
768
769
770
771

		/* if any alias scopes were set, save them */
		if (scopes[0].mid > 1) {
			cursor = 1;
			for (cscope = 1; cscope <= scopes[0].mid; cscope++) {
772
773
774
				/* Ignore the original base */
				if (scopes[cscope].mid == base->e_id)
					continue;
Howard Chu's avatar
Howard Chu committed
775
776
				iscopes[cursor++] = scopes[cscope].mid;
			}
777
			iscopes[0] = scopes[0].mid - 1;
Howard Chu's avatar
Howard Chu committed
778
779
		} else {
			iscopes[0] = 0;
Howard Chu's avatar
Howard Chu committed
780
781
		}

Howard Chu's avatar
Howard Chu committed
782
		wwctx.mcd = mcd;
Howard Chu's avatar
Howard Chu committed
783
784
785
786
787
788
789
		isc.id = base->e_id;
		isc.numrdns = 0;
		rc = mdb_dn2id_walk( op, &isc );
		if ( rc )
			id = NOID;
		else
			id = isc.id;
790
		cscope = 0;
Howard Chu's avatar
Howard Chu committed
791
792
	} else {
		id = mdb_idl_first( candidates, &cursor );
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
793
794
	}

Howard Chu's avatar
Howard Chu committed
795
	while (id != NOID)
Howard Chu's avatar
Howard Chu committed
796
797
	{
		int scopeok;
798
		MDB_val edata;
Howard Chu's avatar
Howard Chu committed
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829

loop_begin:

		/* check for abandon */
		if ( op->o_abandon ) {
			rs->sr_err = SLAPD_ABANDON;
			send_ldap_result( op, rs );
			goto done;
		}

		/* mostly needed by internal searches,
		 * e.g. related to syncrepl, for whom
		 * abandon does not get set... */
		if ( slapd_shutdown ) {
			rs->sr_err = LDAP_UNAVAILABLE;
			send_ldap_disconnect( op, rs );
			goto done;
		}

		/* check time limit */
		if ( op->ors_tlimit != SLAP_NO_LIMIT
				&& slap_get_time() > stoptime )
		{
			rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
			rs->sr_ref = rs->sr_v2ref;
			send_ldap_result( op, rs );
			rs->sr_err = LDAP_SUCCESS;
			goto done;
		}


Howard Chu's avatar
Howard Chu committed
830
831
832
833
834
835
836
837
838
839
		if ( nsubs < ncand ) {
			unsigned i;
			/* Is this entry in the candidate list? */
			scopeok = 0;
			if (MDB_IDL_IS_RANGE( candidates )) {
				if ( id >= MDB_IDL_RANGE_FIRST( candidates ) &&
					id <= MDB_IDL_RANGE_LAST( candidates ))
					scopeok = 1;
			} else {
				i = mdb_idl_search( candidates, id );
840
				if (i <= candidates[0] && candidates[i] == id )
Howard Chu's avatar
Howard Chu committed
841
842
843
844
845
846
847
					scopeok = 1;
			}
			if ( scopeok )
				goto scopeok;
			goto loop_continue;
		}

Howard Chu's avatar
Howard Chu committed
848
849
850
		/* Does this candidate actually satisfy the search scope?
		 */
		scopeok = 0;
Howard Chu's avatar
Howard Chu committed
851
		isc.numrdns = 0;
Howard Chu's avatar
Howard Chu committed
852
853
854
		switch( op->ors_scope ) {
		case LDAP_SCOPE_BASE:
			/* This is always true, yes? */
Howard Chu's avatar
Howard Chu committed
855
			if ( id == base->e_id ) scopeok = 1;
Howard Chu's avatar
Howard Chu committed
856
857
858
859
			break;

#ifdef LDAP_SCOPE_CHILDREN
		case LDAP_SCOPE_CHILDREN:
Howard Chu's avatar
Howard Chu committed
860
			if ( id == base->e_id ) break;
Howard Chu's avatar
Howard Chu committed
861
862
863
			/* Fall-thru */
#endif
		case LDAP_SCOPE_SUBTREE:
Howard Chu's avatar
Howard Chu committed
864
			if ( id == base->e_id ) {
Howard Chu's avatar
Howard Chu committed
865
866
867
868
869
				scopeok = 1;
				break;
			}
			/* Fall-thru */
		case LDAP_SCOPE_ONELEVEL:
Howard Chu's avatar
Howard Chu committed
870
			if ( id == base->e_id ) break;
Howard Chu's avatar
Howard Chu committed
871
			isc.id = id;
872
873
874
875
876
877
878
879
880
			isc.nscope = 0;
			rs->sr_err = mdb_idscopes( op, &isc );
			if ( rs->sr_err == MDB_SUCCESS ) {
				if ( isc.nscope )
					scopeok = 1;
			} else {
				if ( rs->sr_err == MDB_NOTFOUND )
					goto notfound;
			}
Howard Chu's avatar
Howard Chu committed
881
882
883
			break;
		}

884
885
886
887
888
889
890
891
892
893
		/* Not in scope, ignore it */
		if ( !scopeok )
		{
			Debug( LDAP_DEBUG_TRACE,
				LDAP_XSTRING(mdb_search)
				": %ld scope not okay\n",
				(long) id, 0, 0 );
			goto loop_continue;
		}

Howard Chu's avatar
Howard Chu committed
894
scopeok:
895
896
897
898
899
900
901
902
		if ( id == base->e_id ) {
			e = base;
		} else {

			/* get the entry */
			rs->sr_err = mdb_id2edata( op, mci, id, &edata );
			if ( rs->sr_err == MDB_NOTFOUND ) {
notfound:
Howard Chu's avatar
Howard Chu committed
903
904
905
				if( nsubs < ncand )
					goto loop_continue;

906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
				if( !MDB_IDL_IS_RANGE(candidates) ) {
					/* only complain for non-range IDLs */
					Debug( LDAP_DEBUG_TRACE,
						LDAP_XSTRING(mdb_search)
						": candidate %ld not found\n",
						(long) id, 0, 0 );
				} else {
					/* get the next ID from the DB */
					rs->sr_err = mdb_get_nextid( mci, &cursor );
					if ( rs->sr_err == MDB_NOTFOUND ) {
						break;
					}
					if ( rs->sr_err ) {
						rs->sr_err = LDAP_OTHER;
						rs->sr_text = "internal error in get_nextid";
						send_ldap_result( op, rs );
						goto done;
					}
					cursor--;
				}

				goto loop_continue;
			} else if ( rs->sr_err ) {
				rs->sr_err = LDAP_OTHER;
				rs->sr_text = "internal error in mdb_id2edata";
				send_ldap_result( op, rs );
				goto done;
			}

Howard Chu's avatar
Howard Chu committed
935
			rs->sr_err = mdb_entry_decode( op, ltid, &edata, &e );
936
937
			if ( rs->sr_err ) {
				rs->sr_err = LDAP_OTHER;
Howard Chu's avatar
Howard Chu committed
938
				rs->sr_text = "internal error in mdb_entry_decode";
939
940
941
				send_ldap_result( op, rs );
				goto done;
			}
Howard Chu's avatar
Howard Chu committed
942
943
944
			e->e_id = id;
			e->e_name.bv_val = NULL;
			e->e_nname.bv_val = NULL;
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
		}

		if ( is_entry_subentry( e ) ) {
			if( op->oq_search.rs_scope != LDAP_SCOPE_BASE ) {
				if(!get_subentries_visibility( op )) {
					/* only subentries are visible */
					goto loop_continue;
				}

			} else if ( get_subentries( op ) &&
				!get_subentries_visibility( op ))
			{
				/* only subentries are visible */
				goto loop_continue;
			}

		} else if ( get_subentries_visibility( op )) {
			/* only subentries are visible */
			goto loop_continue;
		}

Howard Chu's avatar
Howard Chu committed
966
967
968
969
970
971
		/* aliases were already dereferenced in candidate list */
		if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
			/* but if the search base is an alias, and we didn't
			 * deref it when finding, return it.
			 */
			if ( is_entry_alias(e) &&
Howard Chu's avatar
Howard Chu committed
972
				((op->ors_deref & LDAP_DEREF_FINDING) || e != base ))
Howard Chu's avatar
Howard Chu committed
973
974
975
976
977
978
979
980
981
			{
				goto loop_continue;
			}
		}

		if ( !manageDSAit && is_entry_glue( e )) {
			goto loop_continue;
		}

Howard Chu's avatar
Howard Chu committed
982
983
984
985
		if (e != base) {
			struct berval pdn, pndn;
			char *d, *n;
			int i;
Howard Chu's avatar
Howard Chu committed
986

Howard Chu's avatar
Howard Chu committed
987
			/* child of base, just append RDNs to base->e_name */
Howard Chu's avatar
Howard Chu committed
988
			if ( nsubs < ncand || isc.scopes[isc.nscope].mid == base->e_id ) {
Howard Chu's avatar
Howard Chu committed
989
990
991
				pdn = base->e_name;
				pndn = base->e_nname;
			} else {
992
				mdb_id2name( op, ltid, &isc.mc, scopes[isc.nscope].mid, &pdn, &pndn );
Howard Chu's avatar
Howard Chu committed
993
994
995
996
997
998
999
1000
1001
1002
1003
			}
			e->e_name.bv_len = pdn.bv_len;
			e->e_nname.bv_len = pndn.bv_len;
			for (i=0; i<isc.numrdns; i++) {
				e->e_name.bv_len += isc.rdns[i].bv_len + 1;
				e->e_nname.bv_len += isc.nrdns[i].bv_len + 1;
			}
			e->e_name.bv_val = op->o_tmpalloc(e->e_name.bv_len + 1, op->o_tmpmemctx);
			e->e_nname.bv_val = op->o_tmpalloc(e->e_nname.bv_len + 1, op->o_tmpmemctx);
			d = e->e_name.bv_val;
			n = e->e_nname.bv_val;
Howard Chu's avatar
Howard Chu committed
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
			if (nsubs < ncand) {
				/* RDNs are in top-down order */
				for (i=isc.numrdns-1; i>=0; i--) {
					memcpy(d, isc.rdns[i].bv_val, isc.rdns[i].bv_len);
					d += isc.rdns[i].bv_len;
					*d++ = ',';
					memcpy(n, isc.nrdns[i].bv_val, isc.nrdns[i].bv_len);
					n += isc.nrdns[i].bv_len;
					*n++ = ',';
				}
			} else {
				/* RDNs are in bottom-up order */
				for (i=0; i<isc.numrdns; i++) {
					memcpy(d, isc.rdns[i].bv_val, isc.rdns[i].bv_len);
					d += isc.rdns[i].bv_len;
					*d++ = ',';
					memcpy(n, isc.nrdns[i].bv_val, isc.nrdns[i].bv_len);
					n += isc.nrdns[i].bv_len;
					*n++ = ',';
				}
Howard Chu's avatar
Howard Chu committed
1024
			}
Howard Chu's avatar
Howard Chu committed
1025

Howard Chu's avatar
Howard Chu committed
1026
1027
1028
1029
1030
1031
1032
1033
1034
			if (pdn.bv_len) {
				memcpy(d, pdn.bv_val, pdn.bv_len+1);
				memcpy(n, pndn.bv_val, pndn.bv_len+1);
			} else {
				*--d = '\0';
				*--n = '\0';
				e->e_name.bv_len--;
				e->e_nname.bv_len--;
			}
Howard Chu's avatar
Howard Chu committed
1035
			if (pndn.bv_val != base->e_nname.bv_val) {
Howard Chu's avatar
Howard Chu committed
1036
1037
1038
1039
				op->o_tmpfree(pndn.bv_val, op->o_tmpmemctx);
				op->o_tmpfree(pdn.bv_val, op->o_tmpmemctx);
			}
		}
Howard Chu's avatar
Howard Chu committed
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058

		/*
		 * if it's a referral, add it to the list of referrals. only do
		 * this for non-base searches, and don't check the filter
		 * explicitly here since it's only a candidate anyway.
		 */
		if ( !manageDSAit && op->oq_search.rs_scope != LDAP_SCOPE_BASE
			&& is_entry_referral( e ) )
		{
			BerVarray erefs = get_entry_referrals( op, e );
			rs->sr_ref = referral_rewrite( erefs, &e->e_name, NULL,
				op->oq_search.rs_scope == LDAP_SCOPE_ONELEVEL
					? LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );

			rs->sr_entry = e;
			rs->sr_flags = 0;

			send_search_reference( op, rs );

1059
1060
			if (e != base)
				mdb_entry_return( op, e );
Howard Chu's avatar
Howard Chu committed
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
			rs->sr_entry = NULL;
			e = NULL;

			ber_bvarray_free( rs->sr_ref );
			ber_bvarray_free( erefs );
			rs->sr_ref = NULL;

			goto loop_continue;
		}

		/* if it matches the filter and scope, send it */
		rs->sr_err = test_filter( op, e, op->oq_search.rs_filter );

		if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
			/* check size limit */
			if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
				if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size ) {
1078
1079
					if (e != base)
						mdb_entry_return( op, e );
Howard Chu's avatar
Howard Chu committed
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
					e = NULL;
					send_paged_response( op, rs, &lastid, tentries );
					goto done;
				}
				lastid = id;
			}

			if (e) {
				/* safe default */
				rs->sr_attrs = op->oq_search.rs_attrs;
				rs->sr_operational_attrs = NULL;
				rs->sr_ctrls = NULL;
				rs->sr_entry = e;
				RS_ASSERT( e->e_private != NULL );
				rs->sr_flags = 0;
				rs->sr_err = LDAP_SUCCESS;
				rs->sr_err = send_search_entry( op, rs );
				rs->sr_attrs = NULL;
				rs->sr_entry = NULL;
Howard Chu's avatar
Howard Chu committed
1099
				if (e != base)
Howard Chu's avatar
Howard Chu committed
1100
					mdb_entry_return( op, e );
Howard Chu's avatar
Howard Chu committed
1101
1102
1103
1104
1105
1106
1107
				e = NULL;

				switch ( rs->sr_err ) {
				case LDAP_SUCCESS:	/* entry sent ok */
					break;
				default:		/* entry not sent */
					break;
1108
1109
1110
				case LDAP_BUSY:
					send_ldap_result( op, rs );
					goto done;
Howard Chu's avatar
Howard Chu committed
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
				case LDAP_UNAVAILABLE:
				case LDAP_SIZELIMIT_EXCEEDED:
					if ( rs->sr_err == LDAP_SIZELIMIT_EXCEEDED ) {
						rs->sr_ref = rs->sr_v2ref;
						send_ldap_result( op, rs );
						rs->sr_err = LDAP_SUCCESS;

					} else {
						rs->sr_err = LDAP_OTHER;
					}
					goto done;
				}
			}

		} else {
			Debug( LDAP_DEBUG_TRACE,
				LDAP_XSTRING(mdb_search)
				": %ld does not match filter\n",
				(long) id, 0, 0 );
		}

loop_continue:
1133
1134
1135
		if ( moi == &opinfo && !wwctx.flag && mdb->mi_rtxn_size ) {
			wwctx.nentries++;
			if ( wwctx.nentries >= mdb->mi_rtxn_size ) {
Howard Chu's avatar
Howard Chu committed
1136
				MDB_envinfo ei;
1137
				wwctx.nentries = 0;
Howard Chu's avatar
Howard Chu committed
1138
1139
1140
				mdb_env_info(mdb->mi_dbenv, &ei);
				if ( ei.me_last_txnid > mdb_txn_id( ltid ))
					mdb_rtxn_snap( op, &wwctx );
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
			}
		}
		if ( wwctx.flag ) {
			rs->sr_err = mdb_waitfixup( op, &wwctx, mci, mcd, &isc );
			if ( rs->sr_err ) {
				send_ldap_result( op, rs );
				goto done;
			}
		}

Howard Chu's avatar
Howard Chu committed
1151
		if( e != NULL ) {
Howard Chu's avatar
Howard Chu committed
1152
			if ( e != base )
Howard Chu's avatar
Howard Chu committed
1153
				mdb_entry_return( op, e );
Howard Chu's avatar
Howard Chu committed
1154
1155
1156
1157
			RS_ASSERT( rs->sr_entry == NULL );
			e = NULL;
			rs->sr_entry = NULL;
		}
Howard Chu's avatar
Howard Chu committed
1158
1159
1160

		if ( nsubs < ncand ) {
			int rc = mdb_dn2id_walk( op, &isc );
Howard Chu's avatar
Howard Chu committed
1161
			if (rc) {
Howard Chu's avatar
Howard Chu committed
1162
				id = NOID;
Howard Chu's avatar
Howard Chu committed
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
				/* We got to the end of a subtree. If there are any
				 * alias scopes left, search them too.
				 */
				while (iscopes[0] && cscope < iscopes[0]) {
					cscope++;
					isc.id = iscopes[cscope];
					if ( base )
						mdb_entry_return( op, base );
					rs->sr_err = mdb_id2entry(op, mci, isc.id, &base);
					if ( !rs->sr_err ) {
1173
						mdb_id2name( op, ltid, &isc.mc, isc.id, &base->e_name, &base->e_nname );
Howard Chu's avatar
Howard Chu committed
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
						isc.numrdns = 0;
						if (isc.oscope == LDAP_SCOPE_ONELEVEL)
							isc.oscope = LDAP_SCOPE_BASE;
						rc = mdb_dn2id_walk( op, &isc );
						if ( !rc ) {
							id = isc.id;
							break;
						}
					}
				}
			} else
Howard Chu's avatar
Howard Chu committed
1185
1186
1187
1188
				id = isc.id;
		} else {
			id = mdb_idl_next( candidates, &cursor );
		}
Howard Chu's avatar
Howard Chu committed
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
	}

nochange:
	rs->sr_ctrls = NULL;
	rs->sr_ref = rs->sr_v2ref;
	rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS : LDAP_REFERRAL;
	rs->sr_rspoid = NULL;
	if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
		send_paged_response( op, rs, NULL, 0 );
	} else {
		send_ldap_result( op, rs );
	}

	rs->sr_err = LDAP_SUCCESS;

done:
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
	if ( cb.sc_private ) {
		/* remove our writewait callback */
		slap_callback **scp = &op->o_callback;
		while ( *scp ) {
			if ( *scp == &cb ) {
				*scp = cb.sc_next;
				cb.sc_private = NULL;
				break;
			}
		}
	}
Howard Chu's avatar
Howard Chu committed
1216
1217
	mdb_cursor_close( mcd );
	mdb_cursor_close( mci );
Howard Chu's avatar
Howard Chu committed
1218
1219
1220
	if ( moi == &opinfo ) {
		mdb_txn_reset( moi->moi_txn );
		LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
1221
1222
	} else {
		moi->moi_ref--;
Howard Chu's avatar
Howard Chu committed
1223
1224
1225
1226
1227
	}
	if( rs->sr_v2ref ) {
		ber_bvarray_free( rs->sr_v2ref );
		rs->sr_v2ref = NULL;
	}
Howard Chu's avatar
Howard Chu committed
1228
	if (base)
1229
		mdb_entry_return( op, base );
Howard Chu's avatar
Howard Chu committed
1230
	scope_chunk_ret( op, scopes );
Howard Chu's avatar
Howard Chu committed
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317

	return rs->sr_err;
}


static int base_candidate(
	BackendDB	*be,
	Entry	*e,
	ID		*ids )
{
	Debug(LDAP_DEBUG_ARGS, "base_candidates: base: \"%s\" (0x%08lx)\n",
		e->e_nname.bv_val, (long) e->e_id, 0);

	ids[0] = 1;
	ids[1] = e->e_id;
	return 0;
}

/* Look for "objectClass Present" in this filter.
 * Also count depth of filter tree while we're at it.
 */
static int oc_filter(
	Filter *f,
	int cur,
	int *max )
{
	int rc = 0;

	assert( f != NULL );

	if( cur > *max ) *max = cur;

	switch( f->f_choice ) {
	case LDAP_FILTER_PRESENT:
		if (f->f_desc == slap_schema.si_ad_objectClass) {
			rc = 1;
		}
		break;

	case LDAP_FILTER_AND:
	case LDAP_FILTER_OR:
		cur++;
		for ( f=f->f_and; f; f=f->f_next ) {
			(void) oc_filter(f, cur, max);
		}
		break;

	default:
		break;
	}
	return rc;
}

static void search_stack_free( void *key, void *data )
{
	ber_memfree_x(data, NULL);
}

static void *search_stack( Operation *op )
{
	struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
	void *ret = NULL;

	if ( op->o_threadctx ) {
		ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)search_stack,
			&ret, NULL );
	} else {
		ret = mdb->mi_search_stack;
	}

	if ( !ret ) {
		ret = ch_malloc( mdb->mi_search_stack_depth * MDB_IDL_UM_SIZE
			* sizeof( ID ) );
		if ( op->o_threadctx ) {
			ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)search_stack,
				ret, search_stack_free, NULL, NULL );
		} else {
			mdb->mi_search_stack = ret;
		}
	}
	return ret;
}

static int search_candidates(
	Operation *op,
	SlapReply *rs,
	Entry *e,
Howard Chu's avatar
Howard Chu committed
1318
	IdScopes *isc,
Howard Chu's avatar
Howard Chu committed
1319
	MDB_cursor *mci,
Howard Chu's avatar
Howard Chu committed
1320
	ID	*ids,
1321
	ID *stack )
Howard Chu's avatar
Howard Chu committed
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
{
	struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
	int rc, depth = 1;
	Filter		*f, rf, xf, nf, sf;
	AttributeAssertion aa_ref = ATTRIBUTEASSERTION_INIT;
	AttributeAssertion aa_subentry = ATTRIBUTEASSERTION_INIT;

	/*
	 * This routine takes as input a filter (user-filter)
	 * and rewrites it as follows:
	 *	(&(scope=DN)[(objectClass=subentry)]
	 *		(|[(objectClass=referral)](user-filter))
	 */

	Debug(LDAP_DEBUG_TRACE,
		"search_candidates: base=\"%s\" (0x%08lx) scope=%d\n",
		e->e_nname.bv_val, (long) e->e_id, op->oq_search.rs_scope );

	f = op->oq_search.rs_filter;

	/* If the user's filter uses objectClass=*,
	 * these clauses are redundant.
	 */
	if (!oc_filter(op->oq_search.rs_filter, 1, &depth)
		&& !get_subentries_visibility(op)) {
		if( !get_manageDSAit(op) && !get_domainScope(op) ) {
			/* match referral objects */
			struct berval bv_ref = BER_BVC( "referral" );
			rf.f_choice = LDAP_FILTER_EQUALITY;
			rf.f_ava = &aa_ref;
			rf.f_av_desc = slap_schema.si_ad_objectClass;
			rf.f_av_value = bv_ref;
			rf.f_next = f;
			xf.f_or = &rf;
			xf.f_choice = LDAP_FILTER_OR;
			xf.f_next = NULL;
			f = &xf;
			depth++;
		}
	}

	if( get_subentries_visibility( op ) ) {
		struct berval bv_subentry = BER_BVC( "subentry" );
		sf.f_choice = LDAP_FILTER_EQUALITY;
		sf.f_ava = &aa_subentry;
		sf.f_av_desc = slap_schema.si_ad_objectClass;
		sf.f_av_value = bv_subentry;
		sf.f_next = f;
		nf.f_choice = LDAP_FILTER_AND;
		nf.f_and = &sf;
		nf.f_next = NULL;
		f = &nf;
		depth++;
	}

	/* Allocate IDL stack, plus 1 more for former tmp */
	if ( depth+1 > mdb->mi_search_stack_depth ) {
		stack = ch_malloc( (depth + 1) * MDB_IDL_UM_SIZE * sizeof( ID ) );
	}

	if( op->ors_deref & LDAP_DEREF_SEARCHING ) {
Howard Chu's avatar
Howard Chu committed
1383
		rc = search_aliases( op, rs, e->e_id, isc, mci, stack );
Howard Chu's avatar
Howard Chu committed
1384
1385
1386
1387
1388
	} else {
		rc = LDAP_SUCCESS;
	}

	if ( rc == LDAP_SUCCESS ) {
Howard Chu's avatar
Howard Chu committed
1389
		rc = mdb_filter_candidates( op, isc->mt, f, ids,
Howard Chu's avatar
Howard Chu committed
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
			stack, stack+MDB_IDL_UM_SIZE );
	}

	if ( depth+1 > mdb->mi_search_stack_depth ) {
		ch_free( stack );
	}

	if( rc ) {
		Debug(LDAP_DEBUG_TRACE,
			"mdb_search_candidates: failed (rc=%d)\n",
			rc, NULL, NULL );

	} else {
		Debug(LDAP_DEBUG_TRACE,
			"mdb_search_candidates: id=%ld first=%ld last=%ld\n",
			(long) ids[0],
			(long) MDB_IDL_FIRST(ids),
			(long) MDB_IDL_LAST(ids) );
	}

	return rc;
}

static int
parse_paged_cookie( Operation *op, SlapReply *rs )
{
	int		rc = LDAP_SUCCESS;
	PagedResultsState *ps = op->o_pagedresults_state;

	/* this function must be invoked only if the pagedResults
	 * control has been detected, parsed and partially checked
	 * by the frontend */
	assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );

	/* cookie decoding/checks deferred to backend... */
	if ( ps->ps_cookieval.bv_len ) {
		PagedResultsCookie reqcookie;
		if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
			/* bad cookie */
			rs->sr_text = "paged results cookie is invalid";
			rc = LDAP_PROTOCOL_ERROR;
			goto done;
		}

		AC_MEMCPY( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));

		if ( reqcookie > ps->ps_cookie ) {
			/* bad cookie */
			rs->sr_text = "paged results cookie is invalid";
			rc = LDAP_PROTOCOL_ERROR;
			goto done;

		} else if ( reqcookie < ps->ps_cookie ) {
			rs->sr_text = "paged results cookie is invalid or old";
			rc = LDAP_UNWILLING_TO_PERFORM;
			goto done;
		}

	} else {
		/* we're going to use ps_cookie */
		op->o_conn->c_pagedresults_state.ps_cookie = 0;
	}

done:;

	return rc;
}

static void
send_paged_response( 
	Operation	*op,
	SlapReply	*rs,
	ID		*lastid,
	int		tentries )
{
	LDAPControl	*ctrls[2];
	BerElementBuffer berbuf;
	BerElement	*ber = (BerElement *)&berbuf;
	PagedResultsCookie respcookie;
	struct berval cookie;

	Debug(LDAP_DEBUG_ARGS,
		"send_paged_response: lastid=0x%08lx nentries=%d\n", 
		lastid ? *lastid : 0, rs->sr_nentries, NULL );

	ctrls[1] = NULL;

	ber_init2( ber, NULL, LBER_USE_DER );

	if ( lastid ) {
		respcookie = ( PagedResultsCookie )(*lastid);
		cookie.bv_len = sizeof( respcookie );
		cookie.bv_val = (char *)&respcookie;

	} else {
		respcookie = ( PagedResultsCookie )0;
		BER_BVSTR( &cookie, "" );
	}

	op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
	op->o_conn->c_pagedresults_state.ps_count =
		((PagedResultsState *)op->o_pagedresults_state)->ps_count +
		rs->sr_nentries;

	/* return size of 0 -- no estimate */
	ber_printf( ber, "{iO}", 0, &cookie ); 

	ctrls[0] = op->o_tmpalloc( sizeof(LDAPControl), op->o_tmpmemctx );
	if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
		goto done;
	}

	ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
	ctrls[0]->ldctl_iscritical = 0;

	slap_add_ctrls( op, rs, ctrls );
	rs->sr_err = LDAP_SUCCESS;
	send_ldap_result( op, rs );

done:
	(void) ber_free_buf( ber );
}