1 /* search.cpp - tools for slap tools */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2008-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 initially developed by Howard Chu for inclusion
18  * in OpenLDAP Software. This work was sponsored by MySQL.
19  */
20 
21 #include "portable.h"
22 
23 #include <stdio.h>
24 #include <ac/string.h>
25 #include <ac/errno.h>
26 
27 #include "lutil.h"
28 
29 #include "back-ndb.h"
30 
31 static int
ndb_dn2bound(NdbIndexScanOperation * myop,NdbRdns * rdns)32 ndb_dn2bound(
33 	NdbIndexScanOperation *myop,
34 	NdbRdns *rdns
35 )
36 {
37 	unsigned int i;
38 
39 	/* Walk thru RDNs */
40 	for ( i=0; i<rdns->nr_num; i++ ) {
41 		/* Note: RDN_COLUMN offset not needed here */
42 		if ( myop->setBound( i, NdbIndexScanOperation::BoundEQ, rdns->nr_buf[i] ))
43 			return LDAP_OTHER;
44 	}
45 	return i;
46 }
47 
48 /* Check that all filter terms reside in the same table.
49  *
50  * If any of the filter terms are indexed, then only an IndexScan of the OL_index
51  * will be performed. If none are indexed, but all the terms reside in a single
52  * table, a Scan can be performed with the LDAP filter transformed into a ScanFilter.
53  *
54  * Otherwise, a full scan of the DB must be done with all filtering done by slapd.
55  */
ndb_filter_check(struct ndb_info * ni,Filter * f,NdbOcInfo ** oci,int * indexed,int * ocfilter)56 static int ndb_filter_check( struct ndb_info *ni, Filter *f,
57 	NdbOcInfo **oci, int *indexed, int *ocfilter )
58 {
59 	AttributeDescription *ad = NULL;
60 	ber_tag_t choice = f->f_choice;
61 	int rc = 0, undef = 0;
62 
63 	if ( choice & SLAPD_FILTER_UNDEFINED ) {
64 		choice &= SLAPD_FILTER_MASK;
65 		undef = 1;
66 	}
67 	switch( choice ) {
68 	case LDAP_FILTER_AND:
69 	case LDAP_FILTER_OR:
70 	case LDAP_FILTER_NOT:
71 		for ( f = f->f_list; f; f=f->f_next ) {
72 			rc = ndb_filter_check( ni, f, oci, indexed, ocfilter );
73 			if ( rc ) return rc;
74 		}
75 		break;
76 	case LDAP_FILTER_PRESENT:
77 		ad = f->f_desc;
78 		break;
79 	case LDAP_FILTER_EQUALITY:
80 	case LDAP_FILTER_SUBSTRINGS:
81 	case LDAP_FILTER_GE:
82 	case LDAP_FILTER_LE:
83 	case LDAP_FILTER_APPROX:
84 		ad = f->f_av_desc;
85 		break;
86 	default:
87 		break;
88 	}
89 	if ( ad && !undef ) {
90 		NdbAttrInfo *ai;
91 		/* ObjectClass filtering is in dn2id table */
92 		if ( ad == slap_schema.si_ad_objectClass ) {
93 			if ( choice == LDAP_FILTER_EQUALITY )
94 				(*ocfilter)++;
95 			return 0;
96 		}
97 		ai = ndb_ai_find( ni, ad->ad_type );
98 		if ( ai ) {
99 			if ( ai->na_flag & NDB_INFO_INDEX )
100 				(*indexed)++;
101 			if ( *oci ) {
102 				if ( ai->na_oi != *oci )
103 					rc = -1;
104 			} else {
105 				*oci = ai->na_oi;
106 			}
107 		}
108 	}
109 	return rc;
110 }
111 
ndb_filter_set(Operation * op,struct ndb_info * ni,Filter * f,int indexed,NdbIndexScanOperation * scan,NdbScanFilter * sf,int * bounds)112 static int ndb_filter_set( Operation *op, struct ndb_info *ni, Filter *f, int indexed,
113 	NdbIndexScanOperation *scan, NdbScanFilter *sf, int *bounds )
114 {
115 	AttributeDescription *ad = NULL;
116 	ber_tag_t choice = f->f_choice;
117 	int undef = 0;
118 
119 	if ( choice & SLAPD_FILTER_UNDEFINED ) {
120 		choice &= SLAPD_FILTER_MASK;
121 		undef = 1;
122 	}
123 	switch( choice ) {
124 	case LDAP_FILTER_NOT:
125 		/* no indexing for these */
126 		break;
127 	case LDAP_FILTER_OR:
128 		/* FIXME: these bounds aren't right. */
129 		if ( indexed ) {
130 			scan->end_of_bound( (*bounds)++ );
131 		}
132 	case LDAP_FILTER_AND:
133 		if ( sf ) {
134 			sf->begin( choice == LDAP_FILTER_OR ? NdbScanFilter::OR : NdbScanFilter::AND );
135 		}
136 		for ( f = f->f_list; f; f=f->f_next ) {
137 			if ( ndb_filter_set( op, ni, f, indexed, scan, sf, bounds ))
138 				return -1;
139 		}
140 		if ( sf ) {
141 			sf->end();
142 		}
143 		break;
144 	case LDAP_FILTER_PRESENT:
145 		ad = f->f_desc;
146 		break;
147 	case LDAP_FILTER_EQUALITY:
148 	case LDAP_FILTER_SUBSTRINGS:
149 	case LDAP_FILTER_GE:
150 	case LDAP_FILTER_LE:
151 	case LDAP_FILTER_APPROX:
152 		ad = f->f_av_desc;
153 		break;
154 	default:
155 		break;
156 	}
157 	if ( ad && !undef ) {
158 		NdbAttrInfo *ai;
159 		/* ObjectClass filtering is in dn2id table */
160 		if ( ad == slap_schema.si_ad_objectClass ) {
161 			return 0;
162 		}
163 		ai = ndb_ai_find( ni, ad->ad_type );
164 		if ( ai ) {
165 			int rc;
166 			if ( ai->na_flag & NDB_INFO_INDEX ) {
167 				char *buf, *ptr;
168 				NdbIndexScanOperation::BoundType bt;
169 
170 				switch(choice) {
171 				case LDAP_FILTER_PRESENT:
172 					rc = scan->setBound( ai->na_ixcol - IDX_COLUMN,
173 						NdbIndexScanOperation::BoundGT, NULL );
174 					break;
175 				case LDAP_FILTER_EQUALITY:
176 				case LDAP_FILTER_APPROX:
177 					bt = NdbIndexScanOperation::BoundEQ;
178 					goto setit;
179 				case LDAP_FILTER_GE:
180 					bt = NdbIndexScanOperation::BoundGE;
181 					goto setit;
182 				case LDAP_FILTER_LE:
183 					bt = NdbIndexScanOperation::BoundLE;
184 				setit:
185 					rc = f->f_av_value.bv_len+1;
186 					if ( ai->na_len > 255 )
187 						rc++;
188 					buf = (char *)op->o_tmpalloc( rc, op->o_tmpmemctx );
189 					rc = f->f_av_value.bv_len;
190 					buf[0] = rc & 0xff;
191 					ptr = buf+1;
192 					if ( ai->na_len > 255 ) {
193 						buf[1] = (rc >> 8);
194 						ptr++;
195 					}
196 					memcpy( ptr, f->f_av_value.bv_val, f->f_av_value.bv_len );
197 					rc = scan->setBound( ai->na_ixcol - IDX_COLUMN, bt, buf );
198 					op->o_tmpfree( buf, op->o_tmpmemctx );
199 					break;
200 				default:
201 					break;
202 				}
203 			} else if ( sf ) {
204 				char *buf, *ptr;
205 				NdbScanFilter::BinaryCondition bc;
206 
207 				switch(choice) {
208 				case LDAP_FILTER_PRESENT:
209 					rc = sf->isnotnull( ai->na_column );
210 					break;
211 				case LDAP_FILTER_EQUALITY:
212 				case LDAP_FILTER_APPROX:
213 					bc = NdbScanFilter::COND_EQ;
214 					goto setf;
215 				case LDAP_FILTER_GE:
216 					bc = NdbScanFilter::COND_GE;
217 					goto setf;
218 				case LDAP_FILTER_LE:
219 					bc = NdbScanFilter::COND_LE;
220 				setf:
221 					rc = sf->cmp( bc, ai->na_column, f->f_av_value.bv_val, f->f_av_value.bv_len );
222 					break;
223 				case LDAP_FILTER_SUBSTRINGS:
224 					rc = 0;
225 					if ( f->f_sub_initial.bv_val )
226 						rc += f->f_sub_initial.bv_len + 1;
227 					if ( f->f_sub_any ) {
228 						int i;
229 						if ( !rc ) rc++;
230 						for (i=0; f->f_sub_any[i].bv_val; i++)
231 							rc += f->f_sub_any[i].bv_len + 1;
232 					}
233 					if ( f->f_sub_final.bv_val ) {
234 						if ( !rc ) rc++;
235 						rc += f->f_sub_final.bv_len;
236 					}
237 					buf = (char *)op->o_tmpalloc( rc+1, op->o_tmpmemctx );
238 					ptr = buf;
239 					if ( f->f_sub_initial.bv_val ) {
240 						memcpy( ptr, f->f_sub_initial.bv_val, f->f_sub_initial.bv_len );
241 						ptr += f->f_sub_initial.bv_len;
242 						*ptr++ = '%';
243 					}
244 					if ( f->f_sub_any ) {
245 						int i;
246 						if ( ptr == buf )
247 							*ptr++ = '%';
248 						for (i=0; f->f_sub_any[i].bv_val; i++) {
249 							memcpy( ptr, f->f_sub_any[i].bv_val, f->f_sub_any[i].bv_len );
250 							ptr += f->f_sub_any[i].bv_len;
251 							*ptr++ = '%';
252 						}
253 					}
254 					if ( f->f_sub_final.bv_val ) {
255 						if ( ptr == buf )
256 							*ptr++ = '%';
257 						memcpy( ptr, f->f_sub_final.bv_val, f->f_sub_final.bv_len );
258 						ptr += f->f_sub_final.bv_len;
259 					}
260 					*ptr = '\0';
261 					rc = sf->cmp( NdbScanFilter::COND_LIKE, ai->na_column, buf, ptr - buf );
262 					op->o_tmpfree( buf, op->o_tmpmemctx );
263 					break;
264 				}
265 			}
266 		}
267 	}
268 	return 0;
269 }
270 
ndb_oc_search(Operation * op,SlapReply * rs,Ndb * ndb,NdbTransaction * txn,NdbRdns * rbase,NdbOcInfo * oci,int indexed)271 static int ndb_oc_search( Operation *op, SlapReply *rs, Ndb *ndb, NdbTransaction *txn,
272 	NdbRdns *rbase, NdbOcInfo *oci, int indexed )
273 {
274 	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
275 	const NdbDictionary::Dictionary *myDict = ndb->getDictionary();
276 	const NdbDictionary::Table *myTable;
277 	const NdbDictionary::Index *myIndex;
278 	NdbIndexScanOperation *scan;
279 	NdbIndexOperation *ixop;
280 	NdbScanFilter *sf = NULL;
281 	struct berval *ocs;
282 	NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS];
283 	char dnBuf[2048], *ptr;
284 	NdbRdns rdns;
285 	NdbArgs NA;
286 	char idbuf[2*sizeof(ID)];
287 	char ocbuf[NDB_OC_BUFLEN];
288 	int i, rc, bounds;
289 	Entry e = {0};
290 	Uint64 eid;
291 	time_t stoptime;
292 	int manageDSAit;
293 
294 	stoptime = op->o_time + op->ors_tlimit;
295 	manageDSAit = get_manageDSAit( op );
296 
297 	myTable = myDict->getTable( oci->no_table.bv_val );
298 	if ( indexed ) {
299 		scan = txn->getNdbIndexScanOperation( INDEX_NAME, DN2ID_TABLE );
300 		if ( !scan )
301 			return LDAP_OTHER;
302 		scan->readTuples( NdbOperation::LM_CommittedRead );
303 	} else {
304 		myIndex = myDict->getIndex( "eid$unique", DN2ID_TABLE );
305 		if ( !myIndex ) {
306 			Debug( LDAP_DEBUG_ANY, DN2ID_TABLE " eid index is missing!\n", 0, 0, 0 );
307 			rs->sr_err = LDAP_OTHER;
308 			goto leave;
309 		}
310 		scan = (NdbIndexScanOperation *)txn->getNdbScanOperation( myTable );
311 		if ( !scan )
312 			return LDAP_OTHER;
313 		scan->readTuples( NdbOperation::LM_CommittedRead );
314 #if 1
315 		sf = new NdbScanFilter(scan);
316 		if ( !sf )
317 			return LDAP_OTHER;
318 		switch ( op->ors_filter->f_choice ) {
319 		case LDAP_FILTER_AND:
320 		case LDAP_FILTER_OR:
321 		case LDAP_FILTER_NOT:
322 			break;
323 		default:
324 			if ( sf->begin() < 0 ) {
325 				rc = LDAP_OTHER;
326 				goto leave;
327 			}
328 		}
329 #endif
330 	}
331 
332 	bounds = 0;
333 	rc = ndb_filter_set( op, ni, op->ors_filter, indexed, scan, sf, &bounds );
334 	if ( rc )
335 		goto leave;
336 	if ( sf ) sf->end();
337 
338 	scanID = scan->getValue( EID_COLUMN, idbuf );
339 	if ( indexed ) {
340 		scanOC = scan->getValue( OCS_COLUMN, ocbuf );
341 		for ( i=0; i<NDB_MAX_RDNS; i++ ) {
342 			rdns.nr_buf[i][0] = '\0';
343 			scanDN[i] = scan->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
344 		}
345 	}
346 
347 	if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
348 		rs->sr_err = LDAP_OTHER;
349 		goto leave;
350 	}
351 
352 	e.e_name.bv_val = dnBuf;
353 	NA.e = &e;
354 	NA.ndb = ndb;
355 	while ( scan->nextResult( true, true ) == 0 ) {
356 		NdbTransaction *tx2;
357 		if ( op->o_abandon ) {
358 			rs->sr_err = SLAPD_ABANDON;
359 			break;
360 		}
361 		if ( slapd_shutdown ) {
362 			rs->sr_err = LDAP_UNAVAILABLE;
363 			break;
364 		}
365 		if ( op->ors_tlimit != SLAP_NO_LIMIT &&
366 			slap_get_time() > stoptime ) {
367 			rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
368 			break;
369 		}
370 
371 		eid = scanID->u_64_value();
372 		e.e_id = eid;
373 		if ( !indexed ) {
374 			tx2 = ndb->startTransaction( myTable );
375 			if ( !tx2 ) {
376 				rs->sr_err = LDAP_OTHER;
377 				goto leave;
378 			}
379 
380 			ixop = tx2->getNdbIndexOperation( myIndex );
381 			if ( !ixop ) {
382 				tx2->close();
383 				rs->sr_err = LDAP_OTHER;
384 				goto leave;
385 			}
386 			ixop->readTuple( NdbOperation::LM_CommittedRead );
387 			ixop->equal( EID_COLUMN, eid );
388 
389 			scanOC = ixop->getValue( OCS_COLUMN, ocbuf );
390 			for ( i=0; i<NDB_MAX_RDNS; i++ ) {
391 				rdns.nr_buf[i][0] = '\0';
392 				scanDN[i] = ixop->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
393 			}
394 			rc = tx2->execute( NdbTransaction::Commit, NdbOperation::AbortOnError, 1 );
395 			tx2->close();
396 			if ( rc ) {
397 				rs->sr_err = LDAP_OTHER;
398 				goto leave;
399 			}
400 		}
401 
402 		ocs = ndb_ref2oclist( ocbuf, op->o_tmpmemctx );
403 		for ( i=0; i<NDB_MAX_RDNS; i++ ) {
404 			if ( scanDN[i]->isNULL() || !rdns.nr_buf[i][0] )
405 				break;
406 		}
407 		rdns.nr_num = i;
408 
409 		/* entry must be subordinate to the base */
410 		if ( i < rbase->nr_num ) {
411 			continue;
412 		}
413 
414 		ptr = dnBuf;
415 		for ( --i; i>=0; i-- ) {
416 			char *buf;
417 			int len;
418 			buf = rdns.nr_buf[i];
419 			len = *buf++;
420 			ptr = lutil_strncopy( ptr, buf, len );
421 			if ( i ) *ptr++ = ',';
422 		}
423 		*ptr = '\0';
424 		e.e_name.bv_len = ptr - dnBuf;
425 
426 		/* More scope checks */
427 		/* If indexed, these can be moved into the ScanFilter */
428 		switch( op->ors_scope ) {
429 		case LDAP_SCOPE_ONELEVEL:
430 			if ( rdns.nr_num != rbase->nr_num+1 )
431 				continue;
432 		case LDAP_SCOPE_SUBORDINATE:
433 			if ( rdns.nr_num == rbase->nr_num )
434 				continue;
435 		case LDAP_SCOPE_SUBTREE:
436 		default:
437 			if ( e.e_name.bv_len <= op->o_req_dn.bv_len ) {
438 				if ( op->ors_scope != LDAP_SCOPE_SUBTREE ||
439 					strcasecmp( op->o_req_dn.bv_val, e.e_name.bv_val ))
440 					continue;
441 			} else if ( strcasecmp( op->o_req_dn.bv_val, e.e_name.bv_val +
442 				e.e_name.bv_len - op->o_req_dn.bv_len ))
443 				continue;
444 		}
445 
446 		dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
447 		{
448 #if 1	/* NDBapi was broken here but seems to work now */
449 			Ndb::Key_part_ptr keys[2];
450 			char xbuf[512];
451 			keys[0].ptr = &eid;
452 			keys[0].len = sizeof(eid);
453 			keys[1].ptr = NULL;
454 			keys[1].len = 0;
455 			tx2 = ndb->startTransaction( myTable, keys, xbuf, sizeof(xbuf));
456 #else
457 			tx2 = ndb->startTransaction( myTable );
458 #endif
459 			if ( !tx2 ) {
460 				rs->sr_err = LDAP_OTHER;
461 				goto leave;
462 			}
463 			NA.txn = tx2;
464 			NA.ocs = ocs;
465 			rc = ndb_entry_get_data( op, &NA, 0 );
466 			tx2->close();
467 		}
468 		ber_bvarray_free_x( ocs, op->o_tmpmemctx );
469 		if ( !manageDSAit && is_entry_referral( &e )) {
470 			BerVarray erefs = get_entry_referrals( op, &e );
471 			rs->sr_ref = referral_rewrite( erefs, &e.e_name, NULL,
472 				op->ors_scope == LDAP_SCOPE_ONELEVEL ?
473 					LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
474 			rc = send_search_reference( op, rs );
475 			ber_bvarray_free( rs->sr_ref );
476 			ber_bvarray_free( erefs );
477 			rs->sr_ref = NULL;
478 		} else if ( manageDSAit || !is_entry_glue( &e )) {
479 			rc = test_filter( op, &e, op->ors_filter );
480 			if ( rc == LDAP_COMPARE_TRUE ) {
481 				rs->sr_entry = &e;
482 				rs->sr_attrs = op->ors_attrs;
483 				rs->sr_flags = 0;
484 				rc = send_search_entry( op, rs );
485 				rs->sr_entry = NULL;
486 				rs->sr_attrs = NULL;
487 			} else {
488 				rc = 0;
489 			}
490 		}
491 		attrs_free( e.e_attrs );
492 		e.e_attrs = NULL;
493 		op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
494 		if ( rc ) break;
495 	}
496 leave:
497 	if ( sf ) delete sf;
498 	return rc;
499 }
500 
501 extern "C"
ndb_back_search(Operation * op,SlapReply * rs)502 int ndb_back_search( Operation *op, SlapReply *rs )
503 {
504 	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
505 	NdbTransaction *txn;
506 	NdbIndexScanOperation *scan;
507 	NdbScanFilter *sf = NULL;
508 	Entry e = {0};
509 	int rc, i, ocfilter, indexed;
510 	struct berval matched;
511 	NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS];
512 	char dnBuf[2048], *ptr;
513 	char idbuf[2*sizeof(ID)];
514 	char ocbuf[NDB_OC_BUFLEN];
515 	NdbRdns rdns;
516 	NdbOcInfo *oci;
517 	NdbArgs NA;
518 	slap_mask_t mask;
519 	time_t stoptime;
520 	int manageDSAit;
521 
522 	rc = ndb_thread_handle( op, &NA.ndb );
523 	rdns.nr_num = 0;
524 
525 	manageDSAit = get_manageDSAit( op );
526 
527 	txn = NA.ndb->startTransaction();
528 	if ( !txn ) {
529 		Debug( LDAP_DEBUG_TRACE,
530 			LDAP_XSTRING(ndb_back_search) ": startTransaction failed: %s (%d)\n",
531 			NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
532 		rs->sr_err = LDAP_OTHER;
533 		rs->sr_text = "internal error";
534 		goto leave;
535 	}
536 
537 	NA.txn = txn;
538 	e.e_name = op->o_req_dn;
539 	e.e_nname = op->o_req_ndn;
540 	NA.e = &e;
541 	NA.rdns = &rdns;
542 	NA.ocs = NULL;
543 
544 	rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
545 	if ( rs->sr_err ) {
546 		if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
547 			rs->sr_matched = matched.bv_val;
548 			if ( NA.ocs )
549 				ndb_check_referral( op, rs, &NA );
550 		}
551 		goto leave;
552 	}
553 
554 	if ( !access_allowed_mask( op, &e, slap_schema.si_ad_entry,
555 		NULL, ACL_SEARCH, NULL, &mask )) {
556 		if ( !ACL_GRANT( mask, ACL_DISCLOSE ))
557 			rs->sr_err = LDAP_NO_SUCH_OBJECT;
558 		else
559 			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
560 		ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
561 		goto leave;
562 	}
563 
564 	rs->sr_err = ndb_entry_get_data( op, &NA, 0 );
565 	ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
566 	if ( rs->sr_err )
567 		goto leave;
568 
569 	if ( !manageDSAit && is_entry_referral( &e )) {
570 		rs->sr_ref = get_entry_referrals( op, &e );
571 		rs->sr_err = LDAP_REFERRAL;
572 		if ( rs->sr_ref )
573 			rs->sr_flags |= REP_REF_MUSTBEFREED;
574 		rs->sr_matched = e.e_name.bv_val;
575 		attrs_free( e.e_attrs );
576 		e.e_attrs = NULL;
577 		goto leave;
578 	}
579 
580 	if ( !manageDSAit && is_entry_glue( &e )) {
581 		rs->sr_err = LDAP_NO_SUCH_OBJECT;
582 		goto leave;
583 	}
584 
585 	if ( get_assert( op ) && test_filter( op, &e, (Filter *)get_assertion( op )) !=
586 		LDAP_COMPARE_TRUE ) {
587 		rs->sr_err = LDAP_ASSERTION_FAILED;
588 		attrs_free( e.e_attrs );
589 		e.e_attrs = NULL;
590 		goto leave;
591 	}
592 
593 	/* admin ignores tlimits */
594 	stoptime = op->o_time + op->ors_tlimit;
595 
596 	if ( op->ors_scope == LDAP_SCOPE_BASE ) {
597 		rc = test_filter( op, &e, op->ors_filter );
598 		if ( rc == LDAP_COMPARE_TRUE ) {
599 			rs->sr_entry = &e;
600 			rs->sr_attrs = op->ors_attrs;
601 			rs->sr_flags = 0;
602 			send_search_entry( op, rs );
603 			rs->sr_entry = NULL;
604 		}
605 		attrs_free( e.e_attrs );
606 		e.e_attrs = NULL;
607 		rs->sr_err = LDAP_SUCCESS;
608 		goto leave;
609 	} else {
610 		attrs_free( e.e_attrs );
611 		e.e_attrs = NULL;
612 		if ( rdns.nr_num == NDB_MAX_RDNS ) {
613 			if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ||
614 				op->ors_scope == LDAP_SCOPE_CHILDREN )
615 			rs->sr_err = LDAP_SUCCESS;
616 			goto leave;
617 		}
618 	}
619 
620 	/* See if we can handle the filter. Filtering on objectClass is only done
621 	 * in the DN2ID table scan. If all other filter terms reside in one table,
622 	 * then we scan the OC table instead of the DN2ID table.
623 	 */
624 	oci = NULL;
625 	indexed = 0;
626 	ocfilter = 0;
627 	rc = ndb_filter_check( ni, op->ors_filter, &oci, &indexed, &ocfilter );
628 	if ( rc ) {
629 		Debug( LDAP_DEBUG_TRACE, "ndb_back_search: "
630 			"filter attributes from multiple tables, indexing ignored\n",
631 			0, 0, 0 );
632 	} else if ( oci ) {
633 		rc = ndb_oc_search( op, rs, NA.ndb, txn, &rdns, oci, indexed );
634 		goto leave;
635 	}
636 
637 	scan = txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
638 	if ( !scan ) {
639 		rs->sr_err = LDAP_OTHER;
640 		goto leave;
641 	}
642 	scan->readTuples( NdbOperation::LM_CommittedRead );
643 	rc = ndb_dn2bound( scan, &rdns );
644 
645 	/* TODO: if ( ocfilter ) set up scanfilter for objectclass matches
646 	 * column COND_LIKE "% <class> %"
647 	 */
648 
649 	switch( op->ors_scope ) {
650 	case LDAP_SCOPE_ONELEVEL:
651 		sf = new NdbScanFilter(scan);
652 		if ( sf->begin() < 0 ||
653 			sf->cmp(NdbScanFilter::COND_NOT_LIKE, rc+3, "_%",
654 				STRLENOF("_%")) < 0 ||
655 			sf->end() < 0 ) {
656 			rs->sr_err = LDAP_OTHER;
657 			goto leave;
658 		}
659 		/* FALLTHRU */
660 	case LDAP_SCOPE_CHILDREN:
661 		/* Note: RDN_COLUMN offset not needed here */
662 		scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
663 		/* FALLTHRU */
664 	case LDAP_SCOPE_SUBTREE:
665 		break;
666 	}
667 	scanID = scan->getValue( EID_COLUMN, idbuf );
668 	scanOC = scan->getValue( OCS_COLUMN, ocbuf );
669 	for ( i=0; i<NDB_MAX_RDNS; i++ ) {
670 		rdns.nr_buf[i][0] = '\0';
671 		scanDN[i] = scan->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
672 	}
673 	if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
674 		rs->sr_err = LDAP_OTHER;
675 		goto leave;
676 	}
677 
678 	e.e_name.bv_val = dnBuf;
679 	while ( scan->nextResult( true, true ) == 0 ) {
680 		if ( op->o_abandon ) {
681 			rs->sr_err = SLAPD_ABANDON;
682 			break;
683 		}
684 		if ( slapd_shutdown ) {
685 			rs->sr_err = LDAP_UNAVAILABLE;
686 			break;
687 		}
688 		if ( op->ors_tlimit != SLAP_NO_LIMIT &&
689 			slap_get_time() > stoptime ) {
690 			rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
691 			break;
692 		}
693 		e.e_id = scanID->u_64_value();
694 		NA.ocs = ndb_ref2oclist( ocbuf, op->o_tmpmemctx );
695 		for ( i=0; i<NDB_MAX_RDNS; i++ ) {
696 			if ( scanDN[i]->isNULL() || !rdns.nr_buf[i][0] )
697 				break;
698 		}
699 		ptr = dnBuf;
700 		rdns.nr_num = i;
701 		for ( --i; i>=0; i-- ) {
702 			char *buf;
703 			int len;
704 			buf = rdns.nr_buf[i];
705 			len = *buf++;
706 			ptr = lutil_strncopy( ptr, buf, len );
707 			if ( i ) *ptr++ = ',';
708 		}
709 		*ptr = '\0';
710 		e.e_name.bv_len = ptr - dnBuf;
711 		dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
712 		NA.txn = NA.ndb->startTransaction();
713 		rc = ndb_entry_get_data( op, &NA, 0 );
714 		NA.txn->close();
715 		ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
716 		if ( !manageDSAit && is_entry_referral( &e )) {
717 			BerVarray erefs = get_entry_referrals( op, &e );
718 			rs->sr_ref = referral_rewrite( erefs, &e.e_name, NULL,
719 				op->ors_scope == LDAP_SCOPE_ONELEVEL ?
720 					LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
721 			rc = send_search_reference( op, rs );
722 			ber_bvarray_free( rs->sr_ref );
723 			ber_bvarray_free( erefs );
724 			rs->sr_ref = NULL;
725 		} else if ( manageDSAit || !is_entry_glue( &e )) {
726 			rc = test_filter( op, &e, op->ors_filter );
727 			if ( rc == LDAP_COMPARE_TRUE ) {
728 				rs->sr_entry = &e;
729 				rs->sr_attrs = op->ors_attrs;
730 				rs->sr_flags = 0;
731 				rc = send_search_entry( op, rs );
732 				rs->sr_entry = NULL;
733 			} else {
734 				rc = 0;
735 			}
736 		}
737 		attrs_free( e.e_attrs );
738 		e.e_attrs = NULL;
739 		op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
740 		if ( rc ) break;
741 	}
742 leave:
743 	if ( sf )
744 		delete sf;
745 	if ( txn )
746 		txn->close();
747 	send_ldap_result( op, rs );
748 	return rs->sr_err;
749 }
750 
751 extern NdbInterpretedCode *ndb_lastrow_code;	/* init.cpp */
752 
753 extern "C" int
ndb_has_children(NdbArgs * NA,int * hasChildren)754 ndb_has_children(
755 	NdbArgs *NA,
756 	int *hasChildren
757 )
758 {
759 	NdbIndexScanOperation *scan;
760 	char idbuf[2*sizeof(ID)];
761 	int rc;
762 
763 	if ( NA->rdns->nr_num >= NDB_MAX_RDNS ) {
764 		*hasChildren = LDAP_COMPARE_FALSE;
765 		return 0;
766 	}
767 
768 	scan = NA->txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
769 	if ( !scan )
770 		return LDAP_OTHER;
771 	scan->readTuples( NdbOperation::LM_Read, 0U, 0U, 1U );
772 	rc = ndb_dn2bound( scan, NA->rdns );
773 	if ( rc < NDB_MAX_RDNS ) {
774 		scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
775 	}
776 #if 0
777 	scan->interpret_exit_last_row();
778 #else
779 	scan->setInterpretedCode(ndb_lastrow_code);
780 #endif
781 	scan->getValue( EID_COLUMN, idbuf );
782 	if ( NA->txn->execute( NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1 )) {
783 		return LDAP_OTHER;
784 	}
785 	if (rc < NDB_MAX_RDNS && scan->nextResult( true, true ) == 0 )
786 		*hasChildren = LDAP_COMPARE_TRUE;
787 	else
788 		*hasChildren = LDAP_COMPARE_FALSE;
789 	scan->close();
790 	return 0;
791 }
792 
793 extern "C" int
ndb_has_subordinates(Operation * op,Entry * e,int * hasSubordinates)794 ndb_has_subordinates(
795 	Operation *op,
796 	Entry *e,
797 	int *hasSubordinates )
798 {
799 	NdbArgs NA;
800 	NdbRdns rdns;
801 	int rc;
802 
803 	NA.rdns = &rdns;
804 	rc = ndb_dn2rdns( &e->e_nname, &rdns );
805 
806 	if ( rc == 0 ) {
807 		rc = ndb_thread_handle( op, &NA.ndb );
808 		NA.txn = NA.ndb->startTransaction();
809 		if ( NA.txn ) {
810 			rc = ndb_has_children( &NA, hasSubordinates );
811 			NA.txn->close();
812 		}
813 	}
814 
815 	return rc;
816 }
817 
818 /*
819  * sets the supported operational attributes (if required)
820  */
821 extern "C" int
ndb_operational(Operation * op,SlapReply * rs)822 ndb_operational(
823 	Operation	*op,
824 	SlapReply	*rs )
825 {
826 	Attribute	**ap;
827 
828 	assert( rs->sr_entry != NULL );
829 
830 	for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next ) {
831 		if ( (*ap)->a_desc == slap_schema.si_ad_hasSubordinates ) {
832 			break;
833 		}
834 	}
835 
836 	if ( *ap == NULL &&
837 		attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_hasSubordinates ) == NULL &&
838 		( SLAP_OPATTRS( rs->sr_attr_flags ) ||
839 			ad_inlist( slap_schema.si_ad_hasSubordinates, rs->sr_attrs ) ) )
840 	{
841 		int	hasSubordinates, rc;
842 
843 		rc = ndb_has_subordinates( op, rs->sr_entry, &hasSubordinates );
844 		if ( rc == LDAP_SUCCESS ) {
845 			*ap = slap_operational_hasSubordinate( hasSubordinates == LDAP_COMPARE_TRUE );
846 			assert( *ap != NULL );
847 
848 			ap = &(*ap)->a_next;
849 		}
850 	}
851 
852 	return LDAP_SUCCESS;
853 }
854 
855