xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-mdb/search.c (revision cef8759bd76c1b621f8eab8faa6f208faabc2e15)
1 /*	$NetBSD: search.c,v 1.2 2020/08/11 13:15:40 christos Exp $	*/
2 
3 /* search.c - search operation */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2000-2020 The OpenLDAP Foundation.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 
19 #include <sys/cdefs.h>
20 __RCSID("$NetBSD: search.c,v 1.2 2020/08/11 13:15:40 christos Exp $");
21 
22 #include "portable.h"
23 
24 #include <stdio.h>
25 #include <ac/string.h>
26 
27 #include "back-mdb.h"
28 #include "idl.h"
29 
30 static int base_candidate(
31 	BackendDB	*be,
32 	Entry	*e,
33 	ID		*ids );
34 
35 static int search_candidates(
36 	Operation *op,
37 	SlapReply *rs,
38 	Entry *e,
39 	IdScopes *isc,
40 	MDB_cursor *mci,
41 	ID	*ids,
42 	ID *stack );
43 
44 static int parse_paged_cookie( Operation *op, SlapReply *rs );
45 
46 static void send_paged_response(
47 	Operation *op,
48 	SlapReply *rs,
49 	ID  *lastid,
50 	int tentries );
51 
52 /* Dereference aliases for a single alias entry. Return the final
53  * dereferenced entry on success, NULL on any failure.
54  */
55 static Entry * deref_base (
56 	Operation *op,
57 	SlapReply *rs,
58 	Entry *e,
59 	Entry **matched,
60 	MDB_txn *txn,
61 	ID	*tmp,
62 	ID	*visited )
63 {
64 	struct berval ndn;
65 
66 	rs->sr_err = LDAP_ALIAS_DEREF_PROBLEM;
67 	rs->sr_text = "maximum deref depth exceeded";
68 
69 	for (;;) {
70 		/* Remember the last entry we looked at, so we can
71 		 * report broken links
72 		 */
73 		*matched = e;
74 
75 		if (MDB_IDL_N(tmp) >= op->o_bd->be_max_deref_depth) {
76 			e = NULL;
77 			break;
78 		}
79 
80 		/* If this is part of a subtree or onelevel search,
81 		 * have we seen this ID before? If so, quit.
82 		 */
83 		if ( visited && mdb_idl_insert( visited, e->e_id ) ) {
84 			e = NULL;
85 			break;
86 		}
87 
88 		/* If we've seen this ID during this deref iteration,
89 		 * we've hit a loop.
90 		 */
91 		if ( mdb_idl_insert( tmp, e->e_id ) ) {
92 			rs->sr_err = LDAP_ALIAS_PROBLEM;
93 			rs->sr_text = "circular alias";
94 			e = NULL;
95 			break;
96 		}
97 
98 		/* If there was a problem getting the aliasedObjectName,
99 		 * get_alias_dn will have set the error status.
100 		 */
101 		if ( get_alias_dn(e, &ndn, &rs->sr_err, &rs->sr_text) ) {
102 			e = NULL;
103 			break;
104 		}
105 
106 		rs->sr_err = mdb_dn2entry( op, txn, NULL, &ndn, &e, NULL, 0 );
107 		if (rs->sr_err) {
108 			rs->sr_err = LDAP_ALIAS_PROBLEM;
109 			rs->sr_text = "aliasedObject not found";
110 			break;
111 		}
112 
113 		/* Free the previous entry, continue to work with the
114 		 * one we just retrieved.
115 		 */
116 		mdb_entry_return( op, *matched );
117 
118 		/* We found a regular entry. Return this to the caller.
119 		 */
120 		if (!is_entry_alias(e)) {
121 			rs->sr_err = LDAP_SUCCESS;
122 			rs->sr_text = NULL;
123 			break;
124 		}
125 	}
126 	return e;
127 }
128 
129 /* Look for and dereference all aliases within the search scope.
130  * Requires "stack" to be able to hold 6 levels of DB_SIZE IDLs.
131  * Of course we're hardcoded to require a minimum of 8 UM_SIZE
132  * IDLs so this is never a problem.
133  */
134 static int search_aliases(
135 	Operation *op,
136 	SlapReply *rs,
137 	ID e_id,
138 	IdScopes *isc,
139 	MDB_cursor *mci,
140 	ID *stack )
141 {
142 	ID *aliases, *curscop, *visited, *newsubs, *oldsubs, *tmp;
143 	ID cursora, ida, cursoro, ido;
144 	Entry *matched, *a;
145 	struct berval bv_alias = BER_BVC( "alias" );
146 	AttributeAssertion aa_alias = ATTRIBUTEASSERTION_INIT;
147 	Filter	af;
148 
149 	aliases = stack;	/* IDL of all aliases in the database */
150 	curscop = aliases + MDB_IDL_DB_SIZE;	/* Aliases in the current scope */
151 	visited = curscop + MDB_IDL_DB_SIZE;	/* IDs we've seen in this search */
152 	newsubs = visited + MDB_IDL_DB_SIZE;	/* New subtrees we've added */
153 	oldsubs = newsubs + MDB_IDL_DB_SIZE;	/* Subtrees added previously */
154 	tmp = oldsubs + MDB_IDL_DB_SIZE;	/* Scratch space for deref_base() */
155 
156 	af.f_choice = LDAP_FILTER_EQUALITY;
157 	af.f_ava = &aa_alias;
158 	af.f_av_desc = slap_schema.si_ad_objectClass;
159 	af.f_av_value = bv_alias;
160 	af.f_next = NULL;
161 
162 	/* Find all aliases in database */
163 	MDB_IDL_ZERO( aliases );
164 	rs->sr_err = mdb_filter_candidates( op, isc->mt, &af, aliases,
165 		curscop, visited );
166 	if (rs->sr_err != LDAP_SUCCESS || MDB_IDL_IS_ZERO( aliases )) {
167 		return rs->sr_err;
168 	}
169 	if ( op->ors_limit	/* isroot == FALSE */ &&
170 		op->ors_limit->lms_s_unchecked != -1 &&
171 		MDB_IDL_N( aliases ) > (unsigned) op->ors_limit->lms_s_unchecked )
172 	{
173 		return LDAP_ADMINLIMIT_EXCEEDED;
174 	}
175 	oldsubs[0] = 1;
176 	oldsubs[1] = e_id;
177 
178 	MDB_IDL_ZERO( visited );
179 	MDB_IDL_ZERO( newsubs );
180 
181 	cursoro = 0;
182 	ido = mdb_idl_first( oldsubs, &cursoro );
183 
184 	for (;;) {
185 		/* Set curscop to only the aliases in the current scope. Start with
186 		 * all the aliases, then get the intersection with the scope.
187 		 */
188 		rs->sr_err = mdb_idscope( op, isc->mt, e_id, aliases, curscop );
189 
190 		/* Dereference all of the aliases in the current scope. */
191 		cursora = 0;
192 		for (ida = mdb_idl_first(curscop, &cursora); ida != NOID;
193 			ida = mdb_idl_next(curscop, &cursora))
194 		{
195 			rs->sr_err = mdb_id2entry(op, mci, ida, &a);
196 			if (rs->sr_err != LDAP_SUCCESS) {
197 				continue;
198 			}
199 
200 			/* This should only happen if the curscop IDL has maxed out and
201 			 * turned into a range that spans IDs indiscriminately
202 			 */
203 			if (!is_entry_alias(a)) {
204 				mdb_entry_return(op, a);
205 				continue;
206 			}
207 
208 			/* Actually dereference the alias */
209 			MDB_IDL_ZERO(tmp);
210 			a = deref_base( op, rs, a, &matched, isc->mt,
211 				tmp, visited );
212 			if (a) {
213 				/* If the target was not already in our current scopes,
214 				 * make note of it in the newsubs list.
215 				 */
216 				ID2 mid;
217 				mid.mid = a->e_id;
218 				mid.mval.mv_data = NULL;
219 				if (op->ors_scope == LDAP_SCOPE_SUBTREE) {
220 					isc->id = a->e_id;
221 					/* if ID is a child of any of our current scopes,
222 					 * ignore it, it's already included.
223 					 */
224 					if (mdb_idscopechk(op, isc))
225 						goto skip;
226 				}
227 				if (mdb_id2l_insert(isc->scopes, &mid) == 0) {
228 					mdb_idl_insert(newsubs, a->e_id);
229 				}
230 skip:			mdb_entry_return( op, a );
231 
232 			} else if (matched) {
233 				/* Alias could not be dereferenced, or it deref'd to
234 				 * an ID we've already seen. Ignore it.
235 				 */
236 				mdb_entry_return( op, matched );
237 				rs->sr_text = NULL;
238 				rs->sr_err = 0;
239 			}
240 		}
241 		/* If this is a OneLevel search, we're done; oldsubs only had one
242 		 * ID in it. For a Subtree search, oldsubs may be a list of scope IDs.
243 		 */
244 		if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ) break;
245 nextido:
246 		ido = mdb_idl_next( oldsubs, &cursoro );
247 
248 		/* If we're done processing the old scopes, did we add any new
249 		 * scopes in this iteration? If so, go back and do those now.
250 		 */
251 		if (ido == NOID) {
252 			if (MDB_IDL_IS_ZERO(newsubs)) break;
253 			MDB_IDL_CPY(oldsubs, newsubs);
254 			MDB_IDL_ZERO(newsubs);
255 			cursoro = 0;
256 			ido = mdb_idl_first( oldsubs, &cursoro );
257 		}
258 
259 		/* Find the entry corresponding to the next scope. If it can't
260 		 * be found, ignore it and move on. This should never happen;
261 		 * we should never see the ID of an entry that doesn't exist.
262 		 */
263 		{
264 			MDB_val edata;
265 			rs->sr_err = mdb_id2edata(op, mci, ido, &edata);
266 			if ( rs->sr_err != MDB_SUCCESS ) {
267 				goto nextido;
268 			}
269 			e_id = ido;
270 		}
271 	}
272 	return rs->sr_err;
273 }
274 
275 /* Get the next ID from the DB. Used if the candidate list is
276  * a range and simple iteration hits missing entryIDs
277  */
278 static int
279 mdb_get_nextid(MDB_cursor *mci, ID *cursor)
280 {
281 	MDB_val key;
282 	ID id;
283 	int rc;
284 
285 	id = *cursor + 1;
286 	key.mv_data = &id;
287 	key.mv_size = sizeof(ID);
288 	rc = mdb_cursor_get( mci, &key, NULL, MDB_SET_RANGE );
289 	if ( rc )
290 		return rc;
291 	memcpy( cursor, key.mv_data, sizeof(ID));
292 	return 0;
293 }
294 
295 static void scope_chunk_free( void *key, void *data )
296 {
297 	ID2 *p1, *p2;
298 	for (p1 = data; p1; p1 = p2) {
299 		p2 = p1[0].mval.mv_data;
300 		ber_memfree_x(p1, NULL);
301 	}
302 }
303 
304 static ID2 *scope_chunk_get( Operation *op )
305 {
306 	ID2 *ret = NULL;
307 
308 	ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)scope_chunk_get,
309 			(void *)&ret, NULL );
310 	if ( !ret ) {
311 		ret = ch_malloc( MDB_IDL_UM_SIZE * sizeof( ID2 ));
312 	} else {
313 		void *r2 = ret[0].mval.mv_data;
314 		ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)scope_chunk_get,
315 			r2, scope_chunk_free, NULL, NULL );
316 	}
317 	return ret;
318 }
319 
320 static void scope_chunk_ret( Operation *op, ID2 *scopes )
321 {
322 	void *ret = NULL;
323 
324 	ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)scope_chunk_get,
325 			&ret, NULL );
326 	scopes[0].mval.mv_data = ret;
327 	ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)scope_chunk_get,
328 			(void *)scopes, scope_chunk_free, NULL, NULL );
329 }
330 
331 static void *search_stack( Operation *op );
332 
333 typedef struct ww_ctx {
334 	MDB_txn *txn;
335 	MDB_cursor *mcd;	/* if set, save cursor context */
336 	ID key;
337 	MDB_val data;
338 	int flag;
339 	unsigned nentries;
340 } ww_ctx;
341 
342 /* ITS#7904 if we get blocked while writing results to client,
343  * release the current reader txn and reacquire it after we
344  * unblock.
345  * Slight problem - if we're doing a scope-based walk (mdb_dn2id_walk)
346  * to return results, we need to remember the state of the mcd cursor.
347  * If the node that cursor was pointing to gets deleted while we're
348  * blocked, we may be unable to restore the cursor position. In that
349  * case return an LDAP_BUSY error - let the client know this search
350  * couldn't succeed, but might succeed on a retry.
351  */
352 static void
353 mdb_rtxn_snap( Operation *op, ww_ctx *ww )
354 {
355 	/* save cursor position and release read txn */
356 	if ( ww->mcd ) {
357 		MDB_val key, data;
358 		mdb_cursor_get( ww->mcd, &key, &data, MDB_GET_CURRENT );
359 		memcpy( &ww->key, key.mv_data, sizeof(ID) );
360 		ww->data.mv_size = data.mv_size;
361 		ww->data.mv_data = op->o_tmpalloc( data.mv_size, op->o_tmpmemctx );
362 		memcpy(ww->data.mv_data, data.mv_data, data.mv_size);
363 	}
364 	mdb_txn_reset( ww->txn );
365 	ww->flag = 1;
366 }
367 
368 static void
369 mdb_writewait( Operation *op, slap_callback *sc )
370 {
371 	ww_ctx *ww = sc->sc_private;
372 	if ( !ww->flag ) {
373 		mdb_rtxn_snap( op, ww );
374 	}
375 }
376 
377 static int
378 mdb_waitfixup( Operation *op, ww_ctx *ww, MDB_cursor *mci, MDB_cursor *mcd, IdScopes *isc )
379 {
380 	MDB_val key;
381 	int rc = 0;
382 	ww->flag = 0;
383 	mdb_txn_renew( ww->txn );
384 	mdb_cursor_renew( ww->txn, mci );
385 	mdb_cursor_renew( ww->txn, mcd );
386 
387 	key.mv_size = sizeof(ID);
388 	if ( ww->mcd ) {	/* scope-based search using dn2id_walk */
389 		MDB_val data;
390 
391 		if ( isc->numrdns )
392 			mdb_dn2id_wrestore( op, isc );
393 
394 		key.mv_data = &ww->key;
395 		data = ww->data;
396 		rc = mdb_cursor_get( mcd, &key, &data, MDB_GET_BOTH );
397 		if ( rc == MDB_NOTFOUND ) {
398 			data = ww->data;
399 			rc = mdb_cursor_get( mcd, &key, &data, MDB_GET_BOTH_RANGE );
400 			/* the loop will skip this node using NEXT_DUP but we want it
401 			 * sent, so go back one space first
402 			 */
403 			if ( rc == MDB_SUCCESS )
404 				mdb_cursor_get( mcd, &key, &data, MDB_PREV_DUP );
405 			else
406 				rc = LDAP_BUSY;
407 		} else if ( rc ) {
408 			rc = LDAP_OTHER;
409 		}
410 		op->o_tmpfree( ww->data.mv_data, op->o_tmpmemctx );
411 		ww->data.mv_data = NULL;
412 	} else if ( isc->scopes[0].mid > 1 ) {	/* candidate-based search */
413 		int i;
414 		for ( i=1; i<isc->scopes[0].mid; i++ ) {
415 			if ( !isc->scopes[i].mval.mv_data )
416 				continue;
417 			key.mv_data = &isc->scopes[i].mid;
418 			mdb_cursor_get( mcd, &key, &isc->scopes[i].mval, MDB_SET );
419 		}
420 	}
421 	return rc;
422 }
423 
424 int
425 mdb_search( Operation *op, SlapReply *rs )
426 {
427 	struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
428 	ID		id, cursor, nsubs, ncand, cscope;
429 	ID		lastid = NOID;
430 	ID		candidates[MDB_IDL_UM_SIZE];
431 	ID		iscopes[MDB_IDL_DB_SIZE];
432 	ID2		*scopes;
433 	void	*stack;
434 	Entry		*e = NULL, *base = NULL;
435 	Entry		*matched = NULL;
436 	AttributeName	*attrs;
437 	slap_mask_t	mask;
438 	time_t		stoptime;
439 	int		manageDSAit;
440 	int		tentries = 0;
441 	IdScopes	isc;
442 	MDB_cursor	*mci, *mcd;
443 	ww_ctx wwctx;
444 	slap_callback cb = { 0 };
445 
446 	mdb_op_info	opinfo = {{{0}}}, *moi = &opinfo;
447 	MDB_txn			*ltid = NULL;
448 
449 	Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(mdb_search) "\n", 0, 0, 0);
450 	attrs = op->oq_search.rs_attrs;
451 
452 	manageDSAit = get_manageDSAit( op );
453 
454 	rs->sr_err = mdb_opinfo_get( op, mdb, 1, &moi );
455 	switch(rs->sr_err) {
456 	case 0:
457 		break;
458 	default:
459 		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
460 		return rs->sr_err;
461 	}
462 
463 	ltid = moi->moi_txn;
464 
465 	rs->sr_err = mdb_cursor_open( ltid, mdb->mi_id2entry, &mci );
466 	if ( rs->sr_err ) {
467 		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
468 		return rs->sr_err;
469 	}
470 
471 	rs->sr_err = mdb_cursor_open( ltid, mdb->mi_dn2id, &mcd );
472 	if ( rs->sr_err ) {
473 		mdb_cursor_close( mci );
474 		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
475 		return rs->sr_err;
476 	}
477 
478 	scopes = scope_chunk_get( op );
479 	stack = search_stack( op );
480 	isc.mt = ltid;
481 	isc.mc = mcd;
482 	isc.scopes = scopes;
483 	isc.oscope = op->ors_scope;
484 	isc.sctmp = stack;
485 
486 	if ( op->ors_deref & LDAP_DEREF_FINDING ) {
487 		MDB_IDL_ZERO(candidates);
488 	}
489 dn2entry_retry:
490 	/* get entry with reader lock */
491 	rs->sr_err = mdb_dn2entry( op, ltid, mcd, &op->o_req_ndn, &e, &nsubs, 1 );
492 
493 	switch(rs->sr_err) {
494 	case MDB_NOTFOUND:
495 		matched = e;
496 		e = NULL;
497 		break;
498 	case 0:
499 		break;
500 	case LDAP_BUSY:
501 		send_ldap_error( op, rs, LDAP_BUSY, "ldap server busy" );
502 		goto done;
503 	default:
504 		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
505 		goto done;
506 	}
507 
508 	if ( op->ors_deref & LDAP_DEREF_FINDING ) {
509 		if ( matched && is_entry_alias( matched )) {
510 			struct berval stub;
511 
512 			stub.bv_val = op->o_req_ndn.bv_val;
513 			stub.bv_len = op->o_req_ndn.bv_len - matched->e_nname.bv_len - 1;
514 			e = deref_base( op, rs, matched, &matched, ltid,
515 				candidates, NULL );
516 			if ( e ) {
517 				build_new_dn( &op->o_req_ndn, &e->e_nname, &stub,
518 					op->o_tmpmemctx );
519 				mdb_entry_return(op, e);
520 				matched = NULL;
521 				goto dn2entry_retry;
522 			}
523 		} else if ( e && is_entry_alias( e )) {
524 			e = deref_base( op, rs, e, &matched, ltid,
525 				candidates, NULL );
526 		}
527 	}
528 
529 	if ( e == NULL ) {
530 		struct berval matched_dn = BER_BVNULL;
531 
532 		if ( matched != NULL ) {
533 			BerVarray erefs = NULL;
534 
535 			/* return referral only if "disclose"
536 			 * is granted on the object */
537 			if ( ! access_allowed( op, matched,
538 						slap_schema.si_ad_entry,
539 						NULL, ACL_DISCLOSE, NULL ) )
540 			{
541 				rs->sr_err = LDAP_NO_SUCH_OBJECT;
542 
543 			} else {
544 				ber_dupbv( &matched_dn, &matched->e_name );
545 
546 				erefs = is_entry_referral( matched )
547 					? get_entry_referrals( op, matched )
548 					: NULL;
549 				if ( rs->sr_err == MDB_NOTFOUND )
550 					rs->sr_err = LDAP_REFERRAL;
551 				rs->sr_matched = matched_dn.bv_val;
552 			}
553 
554 			mdb_entry_return(op, matched);
555 			matched = NULL;
556 
557 			if ( erefs ) {
558 				rs->sr_ref = referral_rewrite( erefs, &matched_dn,
559 					&op->o_req_dn, op->oq_search.rs_scope );
560 				ber_bvarray_free( erefs );
561 			}
562 
563 		} else {
564 			rs->sr_ref = referral_rewrite( default_referral,
565 				NULL, &op->o_req_dn, op->oq_search.rs_scope );
566 			rs->sr_err = rs->sr_ref != NULL ? LDAP_REFERRAL : LDAP_NO_SUCH_OBJECT;
567 		}
568 
569 		send_ldap_result( op, rs );
570 
571 		if ( rs->sr_ref ) {
572 			ber_bvarray_free( rs->sr_ref );
573 			rs->sr_ref = NULL;
574 		}
575 		if ( !BER_BVISNULL( &matched_dn ) ) {
576 			ber_memfree( matched_dn.bv_val );
577 			rs->sr_matched = NULL;
578 		}
579 		goto done;
580 	}
581 
582 	/* NOTE: __NEW__ "search" access is required
583 	 * on searchBase object */
584 	if ( ! access_allowed_mask( op, e, slap_schema.si_ad_entry,
585 				NULL, ACL_SEARCH, NULL, &mask ) )
586 	{
587 		if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
588 			rs->sr_err = LDAP_NO_SUCH_OBJECT;
589 		} else {
590 			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
591 		}
592 
593 		mdb_entry_return( op,e);
594 		send_ldap_result( op, rs );
595 		goto done;
596 	}
597 
598 	if ( !manageDSAit && is_entry_referral( e ) ) {
599 		/* entry is a referral */
600 		struct berval matched_dn = BER_BVNULL;
601 		BerVarray erefs = NULL;
602 
603 		ber_dupbv( &matched_dn, &e->e_name );
604 		erefs = get_entry_referrals( op, e );
605 
606 		rs->sr_err = LDAP_REFERRAL;
607 
608 		mdb_entry_return( op, e );
609 		e = NULL;
610 
611 		if ( erefs ) {
612 			rs->sr_ref = referral_rewrite( erefs, &matched_dn,
613 				&op->o_req_dn, op->oq_search.rs_scope );
614 			ber_bvarray_free( erefs );
615 
616 			if ( !rs->sr_ref ) {
617 				rs->sr_text = "bad_referral object";
618 			}
619 		}
620 
621 		Debug( LDAP_DEBUG_TRACE,
622 			LDAP_XSTRING(mdb_search) ": entry is referral\n",
623 			0, 0, 0 );
624 
625 		rs->sr_matched = matched_dn.bv_val;
626 		send_ldap_result( op, rs );
627 
628 		ber_bvarray_free( rs->sr_ref );
629 		rs->sr_ref = NULL;
630 		ber_memfree( matched_dn.bv_val );
631 		rs->sr_matched = NULL;
632 		goto done;
633 	}
634 
635 	if ( get_assert( op ) &&
636 		( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
637 	{
638 		rs->sr_err = LDAP_ASSERTION_FAILED;
639 		mdb_entry_return( op,e);
640 		send_ldap_result( op, rs );
641 		goto done;
642 	}
643 
644 	/* compute it anyway; root does not use it */
645 	stoptime = op->o_time + op->ors_tlimit;
646 
647 	base = e;
648 
649 	e = NULL;
650 
651 	/* select candidates */
652 	if ( op->oq_search.rs_scope == LDAP_SCOPE_BASE ) {
653 		rs->sr_err = base_candidate( op->o_bd, base, candidates );
654 		scopes[0].mid = 0;
655 		ncand = 1;
656 	} else {
657 		if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ) {
658 			size_t nkids;
659 			MDB_val key, data;
660 			key.mv_data = &base->e_id;
661 			key.mv_size = sizeof( ID );
662 			mdb_cursor_get( mcd, &key, &data, MDB_SET );
663 			mdb_cursor_count( mcd, &nkids );
664 			nsubs = nkids - 1;
665 		} else if ( !base->e_id ) {
666 			/* we don't maintain nsubs for entryID 0.
667 			 * just grab entry count from id2entry stat
668 			 */
669 			MDB_stat ms;
670 			mdb_stat( ltid, mdb->mi_id2entry, &ms );
671 			nsubs = ms.ms_entries;
672 		}
673 		MDB_IDL_ZERO( candidates );
674 		scopes[0].mid = 1;
675 		scopes[1].mid = base->e_id;
676 		scopes[1].mval.mv_data = NULL;
677 		rs->sr_err = search_candidates( op, rs, base,
678 			&isc, mci, candidates, stack );
679 
680 		if ( rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED )
681 			goto adminlimit;
682 
683 		ncand = MDB_IDL_N( candidates );
684 		if ( !base->e_id || ncand == NOID ) {
685 			/* grab entry count from id2entry stat
686 			 */
687 			MDB_stat ms;
688 			mdb_stat( ltid, mdb->mi_id2entry, &ms );
689 			if ( !base->e_id )
690 				nsubs = ms.ms_entries;
691 			if ( ncand == NOID )
692 				ncand = ms.ms_entries;
693 		}
694 	}
695 
696 	/* start cursor at beginning of candidates.
697 	 */
698 	cursor = 0;
699 
700 	if ( candidates[0] == 0 ) {
701 		Debug( LDAP_DEBUG_TRACE,
702 			LDAP_XSTRING(mdb_search) ": no candidates\n",
703 			0, 0, 0 );
704 
705 		goto nochange;
706 	}
707 
708 	/* if not root and candidates exceed to-be-checked entries, abort */
709 	if ( op->ors_limit	/* isroot == FALSE */ &&
710 		op->ors_limit->lms_s_unchecked != -1 &&
711 		ncand > (unsigned) op->ors_limit->lms_s_unchecked )
712 	{
713 		rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
714 adminlimit:
715 		send_ldap_result( op, rs );
716 		rs->sr_err = LDAP_SUCCESS;
717 		goto done;
718 	}
719 
720 	if ( op->ors_limit == NULL	/* isroot == TRUE */ ||
721 		!op->ors_limit->lms_s_pr_hide )
722 	{
723 		tentries = ncand;
724 	}
725 
726 	wwctx.flag = 0;
727 	/* If we're running in our own read txn */
728 	if (  moi == &opinfo ) {
729 		cb.sc_writewait = mdb_writewait;
730 		cb.sc_private = &wwctx;
731 		wwctx.txn = ltid;
732 		wwctx.mcd = NULL;
733 		cb.sc_next = op->o_callback;
734 		op->o_callback = &cb;
735 	}
736 
737 	if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
738 		PagedResultsState *ps = op->o_pagedresults_state;
739 		/* deferred cookie parsing */
740 		rs->sr_err = parse_paged_cookie( op, rs );
741 		if ( rs->sr_err != LDAP_SUCCESS ) {
742 			send_ldap_result( op, rs );
743 			goto done;
744 		}
745 
746 		cursor = (ID) ps->ps_cookie;
747 		if ( cursor && ps->ps_size == 0 ) {
748 			rs->sr_err = LDAP_SUCCESS;
749 			rs->sr_text = "search abandoned by pagedResult size=0";
750 			send_ldap_result( op, rs );
751 			goto done;
752 		}
753 		id = mdb_idl_first( candidates, &cursor );
754 		if ( id == NOID ) {
755 			Debug( LDAP_DEBUG_TRACE,
756 				LDAP_XSTRING(mdb_search)
757 				": no paged results candidates\n",
758 				0, 0, 0 );
759 			send_paged_response( op, rs, &lastid, 0 );
760 
761 			rs->sr_err = LDAP_OTHER;
762 			goto done;
763 		}
764 		if ( id == (ID)ps->ps_cookie )
765 			id = mdb_idl_next( candidates, &cursor );
766 		nsubs = ncand;	/* always bypass scope'd search */
767 		goto loop_begin;
768 	}
769 	if ( nsubs < ncand ) {
770 		int rc;
771 		/* Do scope-based search */
772 
773 		/* if any alias scopes were set, save them */
774 		if (scopes[0].mid > 1) {
775 			cursor = 1;
776 			for (cscope = 1; cscope <= scopes[0].mid; cscope++) {
777 				/* Ignore the original base */
778 				if (scopes[cscope].mid == base->e_id)
779 					continue;
780 				iscopes[cursor++] = scopes[cscope].mid;
781 			}
782 			iscopes[0] = scopes[0].mid - 1;
783 		} else {
784 			iscopes[0] = 0;
785 		}
786 
787 		wwctx.mcd = mcd;
788 		isc.id = base->e_id;
789 		isc.numrdns = 0;
790 		rc = mdb_dn2id_walk( op, &isc );
791 		if ( rc )
792 			id = NOID;
793 		else
794 			id = isc.id;
795 		cscope = 0;
796 	} else {
797 		id = mdb_idl_first( candidates, &cursor );
798 	}
799 
800 	while (id != NOID)
801 	{
802 		int scopeok;
803 		MDB_val edata;
804 
805 loop_begin:
806 
807 		/* check for abandon */
808 		if ( op->o_abandon ) {
809 			rs->sr_err = SLAPD_ABANDON;
810 			send_ldap_result( op, rs );
811 			goto done;
812 		}
813 
814 		/* mostly needed by internal searches,
815 		 * e.g. related to syncrepl, for whom
816 		 * abandon does not get set... */
817 		if ( slapd_shutdown ) {
818 			rs->sr_err = LDAP_UNAVAILABLE;
819 			send_ldap_disconnect( op, rs );
820 			goto done;
821 		}
822 
823 		/* check time limit */
824 		if ( op->ors_tlimit != SLAP_NO_LIMIT
825 				&& slap_get_time() > stoptime )
826 		{
827 			rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
828 			rs->sr_ref = rs->sr_v2ref;
829 			send_ldap_result( op, rs );
830 			rs->sr_err = LDAP_SUCCESS;
831 			goto done;
832 		}
833 
834 
835 		if ( nsubs < ncand ) {
836 			unsigned i;
837 			/* Is this entry in the candidate list? */
838 			scopeok = 0;
839 			if (MDB_IDL_IS_RANGE( candidates )) {
840 				if ( id >= MDB_IDL_RANGE_FIRST( candidates ) &&
841 					id <= MDB_IDL_RANGE_LAST( candidates ))
842 					scopeok = 1;
843 			} else {
844 				i = mdb_idl_search( candidates, id );
845 				if (i <= candidates[0] && candidates[i] == id )
846 					scopeok = 1;
847 			}
848 			if ( scopeok )
849 				goto scopeok;
850 			goto loop_continue;
851 		}
852 
853 		/* Does this candidate actually satisfy the search scope?
854 		 */
855 		scopeok = 0;
856 		isc.numrdns = 0;
857 		switch( op->ors_scope ) {
858 		case LDAP_SCOPE_BASE:
859 			/* This is always true, yes? */
860 			if ( id == base->e_id ) scopeok = 1;
861 			break;
862 
863 #ifdef LDAP_SCOPE_CHILDREN
864 		case LDAP_SCOPE_CHILDREN:
865 			if ( id == base->e_id ) break;
866 			/* Fall-thru */
867 #endif
868 		case LDAP_SCOPE_SUBTREE:
869 			if ( id == base->e_id ) {
870 				scopeok = 1;
871 				break;
872 			}
873 			/* Fall-thru */
874 		case LDAP_SCOPE_ONELEVEL:
875 			if ( id == base->e_id ) break;
876 			isc.id = id;
877 			isc.nscope = 0;
878 			rs->sr_err = mdb_idscopes( op, &isc );
879 			if ( rs->sr_err == MDB_SUCCESS ) {
880 				if ( isc.nscope )
881 					scopeok = 1;
882 			} else {
883 				if ( rs->sr_err == MDB_NOTFOUND )
884 					goto notfound;
885 			}
886 			break;
887 		}
888 
889 		/* Not in scope, ignore it */
890 		if ( !scopeok )
891 		{
892 			Debug( LDAP_DEBUG_TRACE,
893 				LDAP_XSTRING(mdb_search)
894 				": %ld scope not okay\n",
895 				(long) id, 0, 0 );
896 			goto loop_continue;
897 		}
898 
899 scopeok:
900 		if ( id == base->e_id ) {
901 			e = base;
902 		} else {
903 
904 			/* get the entry */
905 			rs->sr_err = mdb_id2edata( op, mci, id, &edata );
906 			if ( rs->sr_err == MDB_NOTFOUND ) {
907 notfound:
908 				if( nsubs < ncand )
909 					goto loop_continue;
910 
911 				if( !MDB_IDL_IS_RANGE(candidates) ) {
912 					/* only complain for non-range IDLs */
913 					Debug( LDAP_DEBUG_TRACE,
914 						LDAP_XSTRING(mdb_search)
915 						": candidate %ld not found\n",
916 						(long) id, 0, 0 );
917 				} else {
918 					/* get the next ID from the DB */
919 					rs->sr_err = mdb_get_nextid( mci, &cursor );
920 					if ( rs->sr_err == MDB_NOTFOUND ) {
921 						break;
922 					}
923 					if ( rs->sr_err ) {
924 						rs->sr_err = LDAP_OTHER;
925 						rs->sr_text = "internal error in get_nextid";
926 						send_ldap_result( op, rs );
927 						goto done;
928 					}
929 					cursor--;
930 				}
931 
932 				goto loop_continue;
933 			} else if ( rs->sr_err ) {
934 				rs->sr_err = LDAP_OTHER;
935 				rs->sr_text = "internal error in mdb_id2edata";
936 				send_ldap_result( op, rs );
937 				goto done;
938 			}
939 
940 			rs->sr_err = mdb_entry_decode( op, ltid, &edata, &e );
941 			if ( rs->sr_err ) {
942 				rs->sr_err = LDAP_OTHER;
943 				rs->sr_text = "internal error in mdb_entry_decode";
944 				send_ldap_result( op, rs );
945 				goto done;
946 			}
947 			e->e_id = id;
948 			e->e_name.bv_val = NULL;
949 			e->e_nname.bv_val = NULL;
950 		}
951 
952 		if ( is_entry_subentry( e ) ) {
953 			if( op->oq_search.rs_scope != LDAP_SCOPE_BASE ) {
954 				if(!get_subentries_visibility( op )) {
955 					/* only subentries are visible */
956 					goto loop_continue;
957 				}
958 
959 			} else if ( get_subentries( op ) &&
960 				!get_subentries_visibility( op ))
961 			{
962 				/* only subentries are visible */
963 				goto loop_continue;
964 			}
965 
966 		} else if ( get_subentries_visibility( op )) {
967 			/* only subentries are visible */
968 			goto loop_continue;
969 		}
970 
971 		/* aliases were already dereferenced in candidate list */
972 		if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
973 			/* but if the search base is an alias, and we didn't
974 			 * deref it when finding, return it.
975 			 */
976 			if ( is_entry_alias(e) &&
977 				((op->ors_deref & LDAP_DEREF_FINDING) || e != base ))
978 			{
979 				goto loop_continue;
980 			}
981 		}
982 
983 		if ( !manageDSAit && is_entry_glue( e )) {
984 			goto loop_continue;
985 		}
986 
987 		if (e != base) {
988 			struct berval pdn, pndn;
989 			char *d, *n;
990 			int i;
991 
992 			/* child of base, just append RDNs to base->e_name */
993 			if ( nsubs < ncand || isc.scopes[isc.nscope].mid == base->e_id ) {
994 				pdn = base->e_name;
995 				pndn = base->e_nname;
996 			} else {
997 				mdb_id2name( op, ltid, &isc.mc, scopes[isc.nscope].mid, &pdn, &pndn );
998 			}
999 			e->e_name.bv_len = pdn.bv_len;
1000 			e->e_nname.bv_len = pndn.bv_len;
1001 			for (i=0; i<isc.numrdns; i++) {
1002 				e->e_name.bv_len += isc.rdns[i].bv_len + 1;
1003 				e->e_nname.bv_len += isc.nrdns[i].bv_len + 1;
1004 			}
1005 			e->e_name.bv_val = op->o_tmpalloc(e->e_name.bv_len + 1, op->o_tmpmemctx);
1006 			e->e_nname.bv_val = op->o_tmpalloc(e->e_nname.bv_len + 1, op->o_tmpmemctx);
1007 			d = e->e_name.bv_val;
1008 			n = e->e_nname.bv_val;
1009 			if (nsubs < ncand) {
1010 				/* RDNs are in top-down order */
1011 				for (i=isc.numrdns-1; i>=0; i--) {
1012 					memcpy(d, isc.rdns[i].bv_val, isc.rdns[i].bv_len);
1013 					d += isc.rdns[i].bv_len;
1014 					*d++ = ',';
1015 					memcpy(n, isc.nrdns[i].bv_val, isc.nrdns[i].bv_len);
1016 					n += isc.nrdns[i].bv_len;
1017 					*n++ = ',';
1018 				}
1019 			} else {
1020 				/* RDNs are in bottom-up order */
1021 				for (i=0; i<isc.numrdns; i++) {
1022 					memcpy(d, isc.rdns[i].bv_val, isc.rdns[i].bv_len);
1023 					d += isc.rdns[i].bv_len;
1024 					*d++ = ',';
1025 					memcpy(n, isc.nrdns[i].bv_val, isc.nrdns[i].bv_len);
1026 					n += isc.nrdns[i].bv_len;
1027 					*n++ = ',';
1028 				}
1029 			}
1030 
1031 			if (pdn.bv_len) {
1032 				memcpy(d, pdn.bv_val, pdn.bv_len+1);
1033 				memcpy(n, pndn.bv_val, pndn.bv_len+1);
1034 			} else {
1035 				*--d = '\0';
1036 				*--n = '\0';
1037 				e->e_name.bv_len--;
1038 				e->e_nname.bv_len--;
1039 			}
1040 			if (pndn.bv_val != base->e_nname.bv_val) {
1041 				op->o_tmpfree(pndn.bv_val, op->o_tmpmemctx);
1042 				op->o_tmpfree(pdn.bv_val, op->o_tmpmemctx);
1043 			}
1044 		}
1045 
1046 		/*
1047 		 * if it's a referral, add it to the list of referrals. only do
1048 		 * this for non-base searches, and don't check the filter
1049 		 * explicitly here since it's only a candidate anyway.
1050 		 */
1051 		if ( !manageDSAit && op->oq_search.rs_scope != LDAP_SCOPE_BASE
1052 			&& is_entry_referral( e ) )
1053 		{
1054 			BerVarray erefs = get_entry_referrals( op, e );
1055 			rs->sr_ref = referral_rewrite( erefs, &e->e_name, NULL,
1056 				op->oq_search.rs_scope == LDAP_SCOPE_ONELEVEL
1057 					? LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
1058 
1059 			rs->sr_entry = e;
1060 			rs->sr_flags = 0;
1061 
1062 			send_search_reference( op, rs );
1063 
1064 			if (e != base)
1065 				mdb_entry_return( op, e );
1066 			rs->sr_entry = NULL;
1067 			e = NULL;
1068 
1069 			ber_bvarray_free( rs->sr_ref );
1070 			ber_bvarray_free( erefs );
1071 			rs->sr_ref = NULL;
1072 
1073 			goto loop_continue;
1074 		}
1075 
1076 		/* if it matches the filter and scope, send it */
1077 		rs->sr_err = test_filter( op, e, op->oq_search.rs_filter );
1078 
1079 		if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
1080 			/* check size limit */
1081 			if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
1082 				if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size ) {
1083 					if (e != base)
1084 						mdb_entry_return( op, e );
1085 					e = NULL;
1086 					send_paged_response( op, rs, &lastid, tentries );
1087 					goto done;
1088 				}
1089 				lastid = id;
1090 			}
1091 
1092 			if (e) {
1093 				/* safe default */
1094 				rs->sr_attrs = op->oq_search.rs_attrs;
1095 				rs->sr_operational_attrs = NULL;
1096 				rs->sr_ctrls = NULL;
1097 				rs->sr_entry = e;
1098 				RS_ASSERT( e->e_private != NULL );
1099 				rs->sr_flags = 0;
1100 				rs->sr_err = LDAP_SUCCESS;
1101 				rs->sr_err = send_search_entry( op, rs );
1102 				rs->sr_attrs = NULL;
1103 				rs->sr_entry = NULL;
1104 				if (e != base)
1105 					mdb_entry_return( op, e );
1106 				e = NULL;
1107 
1108 				switch ( rs->sr_err ) {
1109 				case LDAP_SUCCESS:	/* entry sent ok */
1110 					break;
1111 				default:		/* entry not sent */
1112 					break;
1113 				case LDAP_BUSY:
1114 					send_ldap_result( op, rs );
1115 					goto done;
1116 				case LDAP_UNAVAILABLE:
1117 				case LDAP_SIZELIMIT_EXCEEDED:
1118 					if ( rs->sr_err == LDAP_SIZELIMIT_EXCEEDED ) {
1119 						rs->sr_ref = rs->sr_v2ref;
1120 						send_ldap_result( op, rs );
1121 						rs->sr_err = LDAP_SUCCESS;
1122 
1123 					} else {
1124 						rs->sr_err = LDAP_OTHER;
1125 					}
1126 					goto done;
1127 				}
1128 			}
1129 
1130 		} else {
1131 			Debug( LDAP_DEBUG_TRACE,
1132 				LDAP_XSTRING(mdb_search)
1133 				": %ld does not match filter\n",
1134 				(long) id, 0, 0 );
1135 		}
1136 
1137 loop_continue:
1138 		if ( moi == &opinfo && !wwctx.flag && mdb->mi_rtxn_size ) {
1139 			wwctx.nentries++;
1140 			if ( wwctx.nentries >= mdb->mi_rtxn_size ) {
1141 				MDB_envinfo ei;
1142 				wwctx.nentries = 0;
1143 				mdb_env_info(mdb->mi_dbenv, &ei);
1144 				if ( ei.me_last_txnid > mdb_txn_id( ltid ))
1145 					mdb_rtxn_snap( op, &wwctx );
1146 			}
1147 		}
1148 		if ( wwctx.flag ) {
1149 			rs->sr_err = mdb_waitfixup( op, &wwctx, mci, mcd, &isc );
1150 			if ( rs->sr_err ) {
1151 				send_ldap_result( op, rs );
1152 				goto done;
1153 			}
1154 		}
1155 
1156 		if( e != NULL ) {
1157 			if ( e != base )
1158 				mdb_entry_return( op, e );
1159 			RS_ASSERT( rs->sr_entry == NULL );
1160 			e = NULL;
1161 			rs->sr_entry = NULL;
1162 		}
1163 
1164 		if ( nsubs < ncand ) {
1165 			int rc = mdb_dn2id_walk( op, &isc );
1166 			if (rc) {
1167 				id = NOID;
1168 				/* We got to the end of a subtree. If there are any
1169 				 * alias scopes left, search them too.
1170 				 */
1171 				while (iscopes[0] && cscope < iscopes[0]) {
1172 					cscope++;
1173 					isc.id = iscopes[cscope];
1174 					if ( base )
1175 						mdb_entry_return( op, base );
1176 					rs->sr_err = mdb_id2entry(op, mci, isc.id, &base);
1177 					if ( !rs->sr_err ) {
1178 						mdb_id2name( op, ltid, &isc.mc, isc.id, &base->e_name, &base->e_nname );
1179 						isc.numrdns = 0;
1180 						if (isc.oscope == LDAP_SCOPE_ONELEVEL)
1181 							isc.oscope = LDAP_SCOPE_BASE;
1182 						rc = mdb_dn2id_walk( op, &isc );
1183 						if ( !rc ) {
1184 							id = isc.id;
1185 							break;
1186 						}
1187 					}
1188 				}
1189 			} else
1190 				id = isc.id;
1191 		} else {
1192 			id = mdb_idl_next( candidates, &cursor );
1193 		}
1194 	}
1195 
1196 nochange:
1197 	rs->sr_ctrls = NULL;
1198 	rs->sr_ref = rs->sr_v2ref;
1199 	rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS : LDAP_REFERRAL;
1200 	rs->sr_rspoid = NULL;
1201 	if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
1202 		send_paged_response( op, rs, NULL, 0 );
1203 	} else {
1204 		send_ldap_result( op, rs );
1205 	}
1206 
1207 	rs->sr_err = LDAP_SUCCESS;
1208 
1209 done:
1210 	if ( cb.sc_private ) {
1211 		/* remove our writewait callback */
1212 		slap_callback **scp = &op->o_callback;
1213 		while ( *scp ) {
1214 			if ( *scp == &cb ) {
1215 				*scp = cb.sc_next;
1216 				cb.sc_private = NULL;
1217 				break;
1218 			}
1219 		}
1220 	}
1221 	mdb_cursor_close( mcd );
1222 	mdb_cursor_close( mci );
1223 	if ( moi == &opinfo ) {
1224 		mdb_txn_reset( moi->moi_txn );
1225 		LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next );
1226 	} else {
1227 		moi->moi_ref--;
1228 	}
1229 	if( rs->sr_v2ref ) {
1230 		ber_bvarray_free( rs->sr_v2ref );
1231 		rs->sr_v2ref = NULL;
1232 	}
1233 	if (base)
1234 		mdb_entry_return( op, base );
1235 	scope_chunk_ret( op, scopes );
1236 
1237 	return rs->sr_err;
1238 }
1239 
1240 
1241 static int base_candidate(
1242 	BackendDB	*be,
1243 	Entry	*e,
1244 	ID		*ids )
1245 {
1246 	Debug(LDAP_DEBUG_ARGS, "base_candidates: base: \"%s\" (0x%08lx)\n",
1247 		e->e_nname.bv_val, (long) e->e_id, 0);
1248 
1249 	ids[0] = 1;
1250 	ids[1] = e->e_id;
1251 	return 0;
1252 }
1253 
1254 /* Look for "objectClass Present" in this filter.
1255  * Also count depth of filter tree while we're at it.
1256  */
1257 static int oc_filter(
1258 	Filter *f,
1259 	int cur,
1260 	int *max )
1261 {
1262 	int rc = 0;
1263 
1264 	assert( f != NULL );
1265 
1266 	if( cur > *max ) *max = cur;
1267 
1268 	switch( f->f_choice ) {
1269 	case LDAP_FILTER_PRESENT:
1270 		if (f->f_desc == slap_schema.si_ad_objectClass) {
1271 			rc = 1;
1272 		}
1273 		break;
1274 
1275 	case LDAP_FILTER_AND:
1276 	case LDAP_FILTER_OR:
1277 		cur++;
1278 		for ( f=f->f_and; f; f=f->f_next ) {
1279 			(void) oc_filter(f, cur, max);
1280 		}
1281 		break;
1282 
1283 	default:
1284 		break;
1285 	}
1286 	return rc;
1287 }
1288 
1289 static void search_stack_free( void *key, void *data )
1290 {
1291 	ber_memfree_x(data, NULL);
1292 }
1293 
1294 static void *search_stack( Operation *op )
1295 {
1296 	struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
1297 	void *ret = NULL;
1298 
1299 	if ( op->o_threadctx ) {
1300 		ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)search_stack,
1301 			&ret, NULL );
1302 	} else {
1303 		ret = mdb->mi_search_stack;
1304 	}
1305 
1306 	if ( !ret ) {
1307 		ret = ch_malloc( mdb->mi_search_stack_depth * MDB_IDL_UM_SIZE
1308 			* sizeof( ID ) );
1309 		if ( op->o_threadctx ) {
1310 			ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)search_stack,
1311 				ret, search_stack_free, NULL, NULL );
1312 		} else {
1313 			mdb->mi_search_stack = ret;
1314 		}
1315 	}
1316 	return ret;
1317 }
1318 
1319 static int search_candidates(
1320 	Operation *op,
1321 	SlapReply *rs,
1322 	Entry *e,
1323 	IdScopes *isc,
1324 	MDB_cursor *mci,
1325 	ID	*ids,
1326 	ID *stack )
1327 {
1328 	struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
1329 	int rc, depth = 1;
1330 	Filter		*f, rf, xf, nf, sf;
1331 	AttributeAssertion aa_ref = ATTRIBUTEASSERTION_INIT;
1332 	AttributeAssertion aa_subentry = ATTRIBUTEASSERTION_INIT;
1333 
1334 	/*
1335 	 * This routine takes as input a filter (user-filter)
1336 	 * and rewrites it as follows:
1337 	 *	(&(scope=DN)[(objectClass=subentry)]
1338 	 *		(|[(objectClass=referral)](user-filter))
1339 	 */
1340 
1341 	Debug(LDAP_DEBUG_TRACE,
1342 		"search_candidates: base=\"%s\" (0x%08lx) scope=%d\n",
1343 		e->e_nname.bv_val, (long) e->e_id, op->oq_search.rs_scope );
1344 
1345 	f = op->oq_search.rs_filter;
1346 
1347 	/* If the user's filter uses objectClass=*,
1348 	 * these clauses are redundant.
1349 	 */
1350 	if (!oc_filter(op->oq_search.rs_filter, 1, &depth)
1351 		&& !get_subentries_visibility(op)) {
1352 		if( !get_manageDSAit(op) && !get_domainScope(op) ) {
1353 			/* match referral objects */
1354 			struct berval bv_ref = BER_BVC( "referral" );
1355 			rf.f_choice = LDAP_FILTER_EQUALITY;
1356 			rf.f_ava = &aa_ref;
1357 			rf.f_av_desc = slap_schema.si_ad_objectClass;
1358 			rf.f_av_value = bv_ref;
1359 			rf.f_next = f;
1360 			xf.f_or = &rf;
1361 			xf.f_choice = LDAP_FILTER_OR;
1362 			xf.f_next = NULL;
1363 			f = &xf;
1364 			depth++;
1365 		}
1366 	}
1367 
1368 	if( get_subentries_visibility( op ) ) {
1369 		struct berval bv_subentry = BER_BVC( "subentry" );
1370 		sf.f_choice = LDAP_FILTER_EQUALITY;
1371 		sf.f_ava = &aa_subentry;
1372 		sf.f_av_desc = slap_schema.si_ad_objectClass;
1373 		sf.f_av_value = bv_subentry;
1374 		sf.f_next = f;
1375 		nf.f_choice = LDAP_FILTER_AND;
1376 		nf.f_and = &sf;
1377 		nf.f_next = NULL;
1378 		f = &nf;
1379 		depth++;
1380 	}
1381 
1382 	/* Allocate IDL stack, plus 1 more for former tmp */
1383 	if ( depth+1 > mdb->mi_search_stack_depth ) {
1384 		stack = ch_malloc( (depth + 1) * MDB_IDL_UM_SIZE * sizeof( ID ) );
1385 	}
1386 
1387 	if( op->ors_deref & LDAP_DEREF_SEARCHING ) {
1388 		rc = search_aliases( op, rs, e->e_id, isc, mci, stack );
1389 	} else {
1390 		rc = LDAP_SUCCESS;
1391 	}
1392 
1393 	if ( rc == LDAP_SUCCESS ) {
1394 		rc = mdb_filter_candidates( op, isc->mt, f, ids,
1395 			stack, stack+MDB_IDL_UM_SIZE );
1396 	}
1397 
1398 	if ( depth+1 > mdb->mi_search_stack_depth ) {
1399 		ch_free( stack );
1400 	}
1401 
1402 	if( rc ) {
1403 		Debug(LDAP_DEBUG_TRACE,
1404 			"mdb_search_candidates: failed (rc=%d)\n",
1405 			rc, NULL, NULL );
1406 
1407 	} else {
1408 		Debug(LDAP_DEBUG_TRACE,
1409 			"mdb_search_candidates: id=%ld first=%ld last=%ld\n",
1410 			(long) ids[0],
1411 			(long) MDB_IDL_FIRST(ids),
1412 			(long) MDB_IDL_LAST(ids) );
1413 	}
1414 
1415 	return rc;
1416 }
1417 
1418 static int
1419 parse_paged_cookie( Operation *op, SlapReply *rs )
1420 {
1421 	int		rc = LDAP_SUCCESS;
1422 	PagedResultsState *ps = op->o_pagedresults_state;
1423 
1424 	/* this function must be invoked only if the pagedResults
1425 	 * control has been detected, parsed and partially checked
1426 	 * by the frontend */
1427 	assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
1428 
1429 	/* cookie decoding/checks deferred to backend... */
1430 	if ( ps->ps_cookieval.bv_len ) {
1431 		PagedResultsCookie reqcookie;
1432 		if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
1433 			/* bad cookie */
1434 			rs->sr_text = "paged results cookie is invalid";
1435 			rc = LDAP_PROTOCOL_ERROR;
1436 			goto done;
1437 		}
1438 
1439 		AC_MEMCPY( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));
1440 
1441 		if ( reqcookie > ps->ps_cookie ) {
1442 			/* bad cookie */
1443 			rs->sr_text = "paged results cookie is invalid";
1444 			rc = LDAP_PROTOCOL_ERROR;
1445 			goto done;
1446 
1447 		} else if ( reqcookie < ps->ps_cookie ) {
1448 			rs->sr_text = "paged results cookie is invalid or old";
1449 			rc = LDAP_UNWILLING_TO_PERFORM;
1450 			goto done;
1451 		}
1452 
1453 	} else {
1454 		/* we're going to use ps_cookie */
1455 		op->o_conn->c_pagedresults_state.ps_cookie = 0;
1456 	}
1457 
1458 done:;
1459 
1460 	return rc;
1461 }
1462 
1463 static void
1464 send_paged_response(
1465 	Operation	*op,
1466 	SlapReply	*rs,
1467 	ID		*lastid,
1468 	int		tentries )
1469 {
1470 	LDAPControl	*ctrls[2];
1471 	BerElementBuffer berbuf;
1472 	BerElement	*ber = (BerElement *)&berbuf;
1473 	PagedResultsCookie respcookie;
1474 	struct berval cookie;
1475 
1476 	Debug(LDAP_DEBUG_ARGS,
1477 		"send_paged_response: lastid=0x%08lx nentries=%d\n",
1478 		lastid ? *lastid : 0, rs->sr_nentries, NULL );
1479 
1480 	ctrls[1] = NULL;
1481 
1482 	ber_init2( ber, NULL, LBER_USE_DER );
1483 
1484 	if ( lastid ) {
1485 		respcookie = ( PagedResultsCookie )(*lastid);
1486 		cookie.bv_len = sizeof( respcookie );
1487 		cookie.bv_val = (char *)&respcookie;
1488 
1489 	} else {
1490 		respcookie = ( PagedResultsCookie )0;
1491 		BER_BVSTR( &cookie, "" );
1492 	}
1493 
1494 	op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
1495 	op->o_conn->c_pagedresults_state.ps_count =
1496 		((PagedResultsState *)op->o_pagedresults_state)->ps_count +
1497 		rs->sr_nentries;
1498 
1499 	/* return size of 0 -- no estimate */
1500 	ber_printf( ber, "{iO}", 0, &cookie );
1501 
1502 	ctrls[0] = op->o_tmpalloc( sizeof(LDAPControl), op->o_tmpmemctx );
1503 	if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
1504 		goto done;
1505 	}
1506 
1507 	ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
1508 	ctrls[0]->ldctl_iscritical = 0;
1509 
1510 	slap_add_ctrls( op, rs, ctrls );
1511 	rs->sr_err = LDAP_SUCCESS;
1512 	send_ldap_result( op, rs );
1513 
1514 done:
1515 	(void) ber_free_buf( ber );
1516 }
1517