1 /* OpenLDAP WiredTiger backend */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2002-2021 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* ACKNOWLEDGEMENTS:
17  * This work was developed by HAMANO Tsukasa <hamano@osstech.co.jp>
18  * based on back-bdb for inclusion in OpenLDAP Software.
19  * WiredTiger is a product of MongoDB Inc.
20  */
21 
22 #include "portable.h"
23 
24 #include <stdio.h>
25 #include <ac/string.h>
26 
27 #include "back-wt.h"
28 #include "idl.h"
29 
search_aliases(Operation * op,SlapReply * rs,Entry * e,WT_SESSION * session,ID * ids,ID * scopes,ID * stack)30 static int search_aliases(
31 	Operation *op,
32 	SlapReply *rs,
33 	Entry *e,
34 	WT_SESSION *session,
35 	ID *ids,
36 	ID *scopes,
37 	ID *stack )
38 {
39 	/* TODO: search_aliases does not implement yet. */
40 	WT_IDL_ZERO( ids );
41 	return 0;
42 }
43 
base_candidate(BackendDB * be,Entry * e,ID * ids)44 static int base_candidate(
45 	BackendDB *be,
46 	Entry *e,
47 	ID *ids )
48 {
49 	Debug(LDAP_DEBUG_ARGS,
50 		  "base_candidate: base: \"%s\" (0x%08lx)\n",
51 		  e->e_nname.bv_val, (long) e->e_id );
52 
53 	ids[0] = 1;
54 	ids[1] = e->e_id;
55 	return 0;
56 }
57 
58 /* Look for "objectClass Present" in this filter.
59  * Also count depth of filter tree while we're at it.
60  */
oc_filter(Filter * f,int cur,int * max)61 static int oc_filter(
62 	Filter *f,
63 	int cur,
64 	int *max )
65 {
66 	int rc = 0;
67 
68 	assert( f != NULL );
69 
70 	if( cur > *max ) *max = cur;
71 
72 	switch( f->f_choice ) {
73 	case LDAP_FILTER_PRESENT:
74 		if (f->f_desc == slap_schema.si_ad_objectClass) {
75 			rc = 1;
76 		}
77 		break;
78 
79 	case LDAP_FILTER_AND:
80 	case LDAP_FILTER_OR:
81 		cur++;
82 		for ( f=f->f_and; f; f=f->f_next ) {
83 			(void) oc_filter(f, cur, max);
84 		}
85 		break;
86 
87 	default:
88 		break;
89 	}
90 	return rc;
91 }
92 
search_stack_free(void * key,void * data)93 static void search_stack_free( void *key, void *data )
94 {
95 	ber_memfree_x(data, NULL);
96 }
97 
search_stack(Operation * op)98 static void *search_stack( Operation *op )
99 {
100 	struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
101 	void *ret = NULL;
102 
103 	if ( op->o_threadctx ) {
104 		ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)search_stack,
105 									 &ret, NULL );
106 	} else {
107 		ret = wi->wi_search_stack;
108 	}
109 
110 	if ( !ret ) {
111 		ret = ch_malloc( wi->wi_search_stack_depth * WT_IDL_UM_SIZE
112 						 * sizeof( ID ) );
113 		if ( op->o_threadctx ) {
114 			ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)search_stack,
115 										 ret, search_stack_free, NULL, NULL );
116 		} else {
117 			wi->wi_search_stack = ret;
118 		}
119 	}
120 	return ret;
121 }
122 
search_candidates(Operation * op,SlapReply * rs,Entry * e,wt_ctx * wc,ID * ids,ID * scopes)123 static int search_candidates(
124 	Operation *op,
125 	SlapReply *rs,
126 	Entry *e,
127 	wt_ctx *wc,
128 	ID  *ids,
129 	ID  *scopes )
130 {
131     struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
132 	int rc, depth = 1;
133 	Filter f, rf, xf, nf;
134 	ID *stack;
135 	AttributeAssertion aa_ref = ATTRIBUTEASSERTION_INIT;
136 	Filter sf;
137 	AttributeAssertion aa_subentry = ATTRIBUTEASSERTION_INIT;
138 
139 	Debug(LDAP_DEBUG_TRACE,
140 		  "wt_search_candidates: base=\"%s\" (0x%08lx) scope=%d\n",
141 		  e->e_nname.bv_val, (long) e->e_id, op->oq_search.rs_scope );
142 
143 	xf.f_or = op->oq_search.rs_filter;
144 	xf.f_choice = LDAP_FILTER_OR;
145 	xf.f_next = NULL;
146 
147 	/* If the user's filter uses objectClass=*,
148      * these clauses are redundant.
149      */
150 	if (!oc_filter(op->oq_search.rs_filter, 1, &depth)
151 		&& !get_subentries_visibility(op)) {
152 		if( !get_manageDSAit(op) && !get_domainScope(op) ) {
153 			/* match referral objects */
154 			struct berval bv_ref = BER_BVC( "referral" );
155 			rf.f_choice = LDAP_FILTER_EQUALITY;
156 			rf.f_ava = &aa_ref;
157 			rf.f_av_desc = slap_schema.si_ad_objectClass;
158 			rf.f_av_value = bv_ref;
159 			rf.f_next = xf.f_or;
160 			xf.f_or = &rf;
161 			depth++;
162 		}
163 	}
164 
165 	f.f_next = NULL;
166 	f.f_choice = LDAP_FILTER_AND;
167 	f.f_and = &nf;
168 	/* Dummy; we compute scope separately now */
169 	nf.f_choice = SLAPD_FILTER_COMPUTED;
170 	nf.f_result = LDAP_SUCCESS;
171 	nf.f_next = ( xf.f_or == op->oq_search.rs_filter )
172 		? op->oq_search.rs_filter : &xf ;
173 	/* Filter depth increased again, adding dummy clause */
174 	depth++;
175 
176 	if( get_subentries_visibility( op ) ) {
177 		struct berval bv_subentry = BER_BVC( "subentry" );
178 		sf.f_choice = LDAP_FILTER_EQUALITY;
179 		sf.f_ava = &aa_subentry;
180 		sf.f_av_desc = slap_schema.si_ad_objectClass;
181 		sf.f_av_value = bv_subentry;
182 		sf.f_next = nf.f_next;
183 		nf.f_next = &sf;
184 	}
185 
186 	/* Allocate IDL stack, plus 1 more for former tmp */
187 	if ( depth+1 > wi->wi_search_stack_depth ) {
188 		stack = ch_malloc( (depth + 1) * WT_IDL_UM_SIZE * sizeof( ID ) );
189 	} else {
190 		stack = search_stack( op );
191 	}
192 
193     if( op->ors_deref & LDAP_DEREF_SEARCHING ) {
194 		rc = search_aliases( op, rs, e, wc->session, ids, scopes, stack );
195 		if ( WT_IDL_IS_ZERO( ids ) && rc == LDAP_SUCCESS )
196 			rc = wt_dn2idl( op, wc, &e->e_nname, e, ids, stack );
197 	} else {
198 		rc = wt_dn2idl(op, wc, &e->e_nname, e, ids, stack );
199 	}
200 
201 	if ( rc == LDAP_SUCCESS ) {
202 		rc = wt_filter_candidates( op, wc, &f, ids,
203 								   stack, stack+WT_IDL_UM_SIZE );
204 	}
205 
206 	if ( depth+1 > wi->wi_search_stack_depth ) {
207 		ch_free( stack );
208 	}
209 
210     if( rc ) {
211 		Debug(LDAP_DEBUG_TRACE,
212 			  "wt_search_candidates: failed (rc=%d)\n", rc );
213 
214 	} else {
215 		Debug(LDAP_DEBUG_TRACE,
216 			  "wt_search_candidates: id=%ld first=%ld last=%ld\n",
217 			  (long) ids[0],
218 			  (long) WT_IDL_FIRST(ids),
219 			  (long) WT_IDL_LAST(ids));
220 	}
221 	return 0;
222 }
223 
224 static int
parse_paged_cookie(Operation * op,SlapReply * rs)225 parse_paged_cookie( Operation *op, SlapReply *rs )
226 {
227 	int     rc = LDAP_SUCCESS;
228 	PagedResultsState *ps = op->o_pagedresults_state;
229 
230 	/* this function must be invoked only if the pagedResults
231      * control has been detected, parsed and partially checked
232      * by the frontend */
233 	assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
234 
235 	/* cookie decoding/checks deferred to backend... */
236 	if ( ps->ps_cookieval.bv_len ) {
237 		PagedResultsCookie reqcookie;
238 		if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
239 			/* bad cookie */
240 			rs->sr_text = "paged results cookie is invalid";
241 			rc = LDAP_PROTOCOL_ERROR;
242 			goto done;
243 		}
244 
245 		memcpy( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));
246 
247 		if ( reqcookie > ps->ps_cookie ) {
248 			/* bad cookie */
249 			rs->sr_text = "paged results cookie is invalid";
250 			rc = LDAP_PROTOCOL_ERROR;
251 			goto done;
252 
253 		} else if ( reqcookie < ps->ps_cookie ) {
254 			rs->sr_text = "paged results cookie is invalid or old";
255 			rc = LDAP_UNWILLING_TO_PERFORM;
256 			goto done;
257 		}
258 
259 	} else {
260 		/* we're going to use ps_cookie */
261 		op->o_conn->c_pagedresults_state.ps_cookie = 0;
262 	}
263 
264 done:;
265 
266 	return rc;
267 }
268 
269 static void
send_paged_response(Operation * op,SlapReply * rs,ID * lastid,int tentries)270 send_paged_response(
271 	Operation   *op,
272 	SlapReply   *rs,
273 	ID      *lastid,
274 	int     tentries )
275 {
276 	LDAPControl *ctrls[2];
277 	BerElementBuffer berbuf;
278 	BerElement  *ber = (BerElement *)&berbuf;
279 	PagedResultsCookie respcookie;
280 	struct berval cookie;
281 
282 	Debug(LDAP_DEBUG_ARGS,
283 		  "send_paged_response: lastid=0x%08lx nentries=%d\n",
284 		  lastid ? *lastid : 0, rs->sr_nentries );
285 
286 	ctrls[1] = NULL;
287 
288 	ber_init2( ber, NULL, LBER_USE_DER );
289 
290 	if ( lastid ) {
291 		respcookie = ( PagedResultsCookie )(*lastid);
292 		cookie.bv_len = sizeof( respcookie );
293 		cookie.bv_val = (char *)&respcookie;
294 
295 	} else {
296 		respcookie = ( PagedResultsCookie )0;
297 		BER_BVSTR( &cookie, "" );
298 	}
299 
300 	op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
301 	op->o_conn->c_pagedresults_state.ps_count =
302 		((PagedResultsState *)op->o_pagedresults_state)->ps_count +
303 		rs->sr_nentries;
304 
305 	/* return size of 0 -- no estimate */
306 	ber_printf( ber, "{iO}", 0, &cookie );
307 
308 	ctrls[0] = op->o_tmpalloc( sizeof(LDAPControl), op->o_tmpmemctx );
309 	if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
310 		goto done;
311 	}
312 
313 	ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
314 	ctrls[0]->ldctl_iscritical = 0;
315 
316 	slap_add_ctrls( op, rs, ctrls );
317 	rs->sr_err = LDAP_SUCCESS;
318 	send_ldap_result( op, rs );
319 
320 done:
321 	(void) ber_free_buf( ber );
322 }
323 
324 int
wt_search(Operation * op,SlapReply * rs)325 wt_search( Operation *op, SlapReply *rs )
326 {
327     struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
328 	ID id, cursor;
329 	ID lastid = NOID;
330 	int manageDSAit;
331 	wt_ctx *wc;
332 	int rc = LDAP_OTHER;
333 	Entry *e = NULL;
334 	Entry *ae = NULL;
335 	Entry *base = NULL;
336 	slap_mask_t mask;
337 	time_t stoptime;
338 
339 	ID candidates[WT_IDL_UM_SIZE];
340 	ID scopes[WT_IDL_DB_SIZE];
341 	int tentries = 0;
342 	unsigned nentries = 0;
343 
344 	Debug( LDAP_DEBUG_ARGS, "==> wt_search: %s\n", op->o_req_dn.bv_val );
345 
346 	manageDSAit = get_manageDSAit( op );
347 
348 	wc = wt_ctx_get(op, wi);
349 	if( !wc ){
350         Debug( LDAP_DEBUG_ANY,
351 			   "wt_search: wt_ctx_get failed: %d\n", rc );
352 		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
353         return rc;
354 	}
355 
356 	/* get entry */
357 	rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
358 	switch( rc ) {
359 	case 0:
360 		break;
361 	case WT_NOTFOUND:
362 		rc = wt_dn2aentry(op->o_bd, wc, &op->o_req_ndn, &ae);
363 		break;
364 	default:
365 		/* TODO: error handling */
366 		Debug( LDAP_DEBUG_ANY,
367 			   "<== wt_search: error at wt_dn2entry() rc=%d\n", rc );
368 		send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
369 		goto done;
370 	}
371 
372 	if ( op->ors_deref & LDAP_DEREF_FINDING ) {
373 		/* not implement yet */
374 	}
375 
376 	if ( e == NULL ) {
377 		if ( ae ) {
378 			struct berval matched_dn = BER_BVNULL;
379 			/* found ancestor entry */
380 			if ( access_allowed( op, ae,
381 								 slap_schema.si_ad_entry,
382 								 NULL, ACL_DISCLOSE, NULL ) ) {
383 				BerVarray erefs = NULL;
384 				ber_dupbv( &matched_dn, &ae->e_name );
385 				erefs = is_entry_referral( ae )
386 					? get_entry_referrals( op, ae )
387 					: NULL;
388 				rs->sr_err = LDAP_REFERRAL;
389 				rs->sr_matched = matched_dn.bv_val;
390 				if ( erefs ) {
391 					rs->sr_ref = referral_rewrite( erefs, &matched_dn,
392 												   &op->o_req_dn, op->oq_search.rs_scope );
393 					ber_bvarray_free( erefs );
394 				}
395 				Debug( LDAP_DEBUG_ARGS,
396 					   "wt_search: ancestor is referral\n");
397 				rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
398 				send_ldap_result( op, rs );
399 				goto done;
400 			}
401 		}
402 		Debug( LDAP_DEBUG_ARGS,
403 			   "wt_search: no such object %s\n",
404 			   op->o_req_dn.bv_val);
405 		rs->sr_err = LDAP_NO_SUCH_OBJECT;
406 		send_ldap_result( op, rs );
407 		goto done;
408 	}
409 
410 	/* NOTE: __NEW__ "search" access is required
411      * on searchBase object */
412 	if ( ! access_allowed_mask( op, e, slap_schema.si_ad_entry,
413 								NULL, ACL_SEARCH, NULL, &mask ) )
414 	{
415 		if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
416 			rs->sr_err = LDAP_NO_SUCH_OBJECT;
417 		} else {
418 			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
419 		}
420 
421 		send_ldap_result( op, rs );
422 		goto done;
423 	}
424 
425 	if ( !manageDSAit && is_entry_referral( e ) ) {
426 		struct berval matched_dn = BER_BVNULL;
427 		BerVarray erefs = NULL;
428 		ber_dupbv( &matched_dn, &e->e_name );
429 		erefs = get_entry_referrals( op, e );
430 		rs->sr_err = LDAP_REFERRAL;
431 		if ( erefs ) {
432 			rs->sr_ref = referral_rewrite( erefs, &matched_dn,
433 										   &op->o_req_dn, op->oq_search.rs_scope );
434 			ber_bvarray_free( erefs );
435 			if ( !rs->sr_ref ) {
436 				rs->sr_text = "bad_referral object";
437 			}
438 		}
439 		Debug( LDAP_DEBUG_ARGS, "wt_search: entry is referral\n");
440 		rs->sr_matched = matched_dn.bv_val;
441 		send_ldap_result( op, rs );
442 		ber_bvarray_free( rs->sr_ref );
443 		rs->sr_ref = NULL;
444 		ber_memfree( matched_dn.bv_val );
445 		rs->sr_matched = NULL;
446 		goto done;
447 	}
448 
449 	if ( get_assert( op ) &&
450 		 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
451 	{
452 		rs->sr_err = LDAP_ASSERTION_FAILED;
453 		send_ldap_result( op, rs );
454 		goto done;
455 	}
456 
457 	/* compute it anyway; root does not use it */
458 	stoptime = op->o_time + op->ors_tlimit;
459 
460 	base = e;
461 
462 	e = NULL;
463 
464 	/* select candidates */
465 	if ( op->oq_search.rs_scope == LDAP_SCOPE_BASE ) {
466 		rs->sr_err = base_candidate( op->o_bd, base, candidates );
467 	}else{
468 		WT_IDL_ZERO( candidates );
469 		WT_IDL_ZERO( scopes );
470 		rc = search_candidates( op, rs, base,
471 								wc, candidates, scopes );
472 		switch(rc){
473 		case 0:
474 		case WT_NOTFOUND:
475 			break;
476 		default:
477 			Debug( LDAP_DEBUG_ANY, "wt_search: error search_candidates\n" );
478 			send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
479 			goto done;
480 		}
481 	}
482 
483 	/* start cursor at beginning of candidates.
484      */
485 	cursor = 0;
486 
487 	if ( candidates[0] == 0 ) {
488 		Debug( LDAP_DEBUG_TRACE, "wt_search: no candidates\n" );
489 		goto nochange;
490 	}
491 
492 	if ( op->ors_limit &&
493 		 op->ors_limit->lms_s_unchecked != -1 &&
494 		 WT_IDL_N(candidates) > (unsigned) op->ors_limit->lms_s_unchecked )
495 	{
496 		rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
497 		send_ldap_result( op, rs );
498 		rs->sr_err = LDAP_SUCCESS;
499 		goto done;
500 	}
501 
502 	if ( op->ors_limit == NULL  /* isroot == TRUE */ ||
503 		 !op->ors_limit->lms_s_pr_hide )
504 	{
505 		tentries = WT_IDL_N(candidates);
506 	}
507 
508 	if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
509 		/* TODO: pageresult */
510 		PagedResultsState *ps = op->o_pagedresults_state;
511 		/* deferred cookie parsing */
512 		rs->sr_err = parse_paged_cookie( op, rs );
513 		if ( rs->sr_err != LDAP_SUCCESS ) {
514 			send_ldap_result( op, rs );
515 			goto done;
516 		}
517 
518 		cursor = (ID) ps->ps_cookie;
519 		if ( cursor && ps->ps_size == 0 ) {
520 			rs->sr_err = LDAP_SUCCESS;
521 			rs->sr_text = "search abandoned by pagedResult size=0";
522 			send_ldap_result( op, rs );
523 			goto done;
524 		}
525 		id = wt_idl_first( candidates, &cursor );
526 		if ( id == NOID ) {
527 			Debug( LDAP_DEBUG_TRACE, "wt_search: no paged results candidates\n" );
528 			send_paged_response( op, rs, &lastid, 0 );
529 
530 			rs->sr_err = LDAP_OTHER;
531 			goto done;
532 		}
533 		nentries = ps->ps_count;
534 		if ( id == (ID)ps->ps_cookie )
535 			id = wt_idl_next( candidates, &cursor );
536 		goto loop_begin;
537 	}
538 
539 	for ( id = wt_idl_first( candidates, &cursor );
540 		  id != NOID ; id = wt_idl_next( candidates, &cursor ) )
541 	{
542 		int scopeok;
543 
544 loop_begin:
545 
546 		/* check for abandon */
547 		if ( op->o_abandon ) {
548 			rs->sr_err = SLAPD_ABANDON;
549 			send_ldap_result( op, rs );
550 			goto done;
551 		}
552 
553 		/* mostly needed by internal searches,
554          * e.g. related to syncrepl, for whom
555          * abandon does not get set... */
556 		if ( slapd_shutdown ) {
557 			rs->sr_err = LDAP_UNAVAILABLE;
558 			send_ldap_disconnect( op, rs );
559 			goto done;
560 		}
561 
562 		/* check time limit */
563 		if ( op->ors_tlimit != SLAP_NO_LIMIT
564 			 && slap_get_time() > stoptime )
565 		{
566 			rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
567 			rs->sr_ref = rs->sr_v2ref;
568 			send_ldap_result( op, rs );
569 			rs->sr_err = LDAP_SUCCESS;
570 			goto done;
571 		}
572 
573 		nentries++;
574 
575 	fetch_entry_retry:
576 
577 		rc = wt_id2entry(op->o_bd, wc, id, &e);
578 		/* TODO: error handling */
579 		if ( e == NULL ) {
580 			/* TODO: */
581 			goto loop_continue;
582 		}
583 		if ( is_entry_subentry( e ) ) {
584             if( op->oq_search.rs_scope != LDAP_SCOPE_BASE ) {
585 				if(!get_subentries_visibility( op )) {
586 					/* only subentries are visible */
587 					goto loop_continue;
588 				}
589 
590 			} else if ( get_subentries( op ) &&
591 						!get_subentries_visibility( op ))
592 			{
593 				/* only subentries are visible */
594 				goto loop_continue;
595 			}
596 
597 		} else if ( get_subentries_visibility( op )) {
598 			/* only subentries are visible */
599 			goto loop_continue;
600 		}
601 
602 		scopeok = 0;
603 		switch( op->ors_scope ) {
604 		case LDAP_SCOPE_BASE:
605 			/* This is always true, yes? */
606 			if ( id == base->e_id ) scopeok = 1;
607 			break;
608 		case LDAP_SCOPE_ONELEVEL:
609 			scopeok = 1;
610 			break;
611 		case LDAP_SCOPE_CHILDREN:
612 			if ( id == base->e_id ) break;
613 			/* Fall-thru */
614 		case LDAP_SCOPE_SUBTREE:
615  			scopeok = dnIsSuffix(&e->e_nname, &base->e_nname);
616 			break;
617 		}
618 
619 		/* aliases were already dereferenced in candidate list */
620 		if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
621 			/* but if the search base is an alias, and we didn't
622 			 * deref it when finding, return it.
623 			 */
624 			if ( is_entry_alias(e) &&
625 				 ((op->ors_deref & LDAP_DEREF_FINDING) ||
626 				  !bvmatch(&e->e_nname, &op->o_req_ndn)))
627 			{
628 				goto loop_continue;
629 			}
630 			/* TODO: alias handling */
631 		}
632 
633 		/* Not in scope, ignore it */
634 		if ( !scopeok )
635 		{
636 			Debug( LDAP_DEBUG_TRACE, "wt_search: %ld scope not okay\n",
637 				   (long) id );
638 			goto loop_continue;
639 		}
640 
641 		/*
642          * if it's a referral, add it to the list of referrals. only do
643          * this for non-base searches, and don't check the filter
644          * explicitly here since it's only a candidate anyway.
645          */
646 		if ( !manageDSAit && op->oq_search.rs_scope != LDAP_SCOPE_BASE
647 			 && is_entry_referral( e ) )
648 		{
649 			BerVarray erefs = get_entry_referrals( op, e );
650 			rs->sr_ref = referral_rewrite( erefs, &e->e_name, NULL,
651 										   op->oq_search.rs_scope == LDAP_SCOPE_ONELEVEL
652 										   ? LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
653 			rs->sr_entry = e;
654 			send_search_reference( op, rs );
655 			rs->sr_entry = NULL;
656 			ber_bvarray_free( rs->sr_ref );
657 			ber_bvarray_free( erefs );
658 			goto loop_continue;
659 		}
660 
661 		if ( !manageDSAit && is_entry_glue( e )) {
662 			goto loop_continue;
663 		}
664 
665 		/* if it matches the filter and scope, send it */
666 		rs->sr_err = test_filter( op, e, op->oq_search.rs_filter );
667 		if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
668 			/* check size limit */
669 			if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
670 				if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size ) {
671 					wt_entry_return( e );
672 					e = NULL;
673 					send_paged_response( op, rs, &lastid, tentries );
674 					goto done;
675 				}
676 				lastid = id;
677 			}
678 
679 			if (e) {
680 				/* safe default */
681 				rs->sr_attrs = op->oq_search.rs_attrs;
682 				rs->sr_operational_attrs = NULL;
683 				rs->sr_ctrls = NULL;
684 				rs->sr_entry = e;
685 				RS_ASSERT( e->e_private != NULL );
686 				rs->sr_flags = REP_ENTRY_MUSTRELEASE;
687 				rs->sr_err = LDAP_SUCCESS;
688 				rs->sr_err = send_search_entry( op, rs );
689 				rs->sr_attrs = NULL;
690 				rs->sr_entry = NULL;
691 				e = NULL;
692 
693 				switch ( rs->sr_err ) {
694 				case LDAP_SUCCESS:  /* entry sent ok */
695 					break;
696 				default: /* entry not sent */
697 					break;
698 				case LDAP_BUSY:
699 					send_ldap_result( op, rs );
700 					goto done;
701 				case LDAP_UNAVAILABLE:
702 					rs->sr_err = LDAP_OTHER;
703 					goto done;
704 				case LDAP_SIZELIMIT_EXCEEDED:
705 					rs->sr_ref = rs->sr_v2ref;
706 					send_ldap_result( op, rs );
707 					rs->sr_err = LDAP_SUCCESS;
708 					goto done;
709 				}
710 			}
711 		} else {
712 			Debug( LDAP_DEBUG_TRACE,
713 				   "wt_search: %ld does not match filter\n", (long) id );
714 		}
715 
716 	loop_continue:
717 		if( e ) {
718 			wt_entry_return( e );
719 			e = NULL;
720 		}
721 	}
722 
723 nochange:
724     rs->sr_ctrls = NULL;
725 	rs->sr_ref = rs->sr_v2ref;
726 	rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS : LDAP_REFERRAL;
727 	rs->sr_rspoid = NULL;
728 	if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
729 		send_paged_response( op, rs, NULL, 0 );
730 	} else {
731 		send_ldap_result( op, rs );
732 	}
733 
734 	rs->sr_err = LDAP_SUCCESS;
735 
736 done:
737 
738 	if( base ) {
739 		wt_entry_return( base );
740 	}
741 
742 	if( e ) {
743 		wt_entry_return( e );
744 	}
745 
746 	if( ae ) {
747 		wt_entry_return( ae );
748 	}
749 
750     return rs->sr_err;
751 }
752 
753 /*
754  * Local variables:
755  * indent-tabs-mode: t
756  * tab-width: 4
757  * c-basic-offset: 4
758  * End:
759  */
760