1/* $NetBSD: search.c,v 1.3 2021/08/14 16:15:00 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-2021 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.3 2021/08/14 16:15:00 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 30static int base_candidate( 31 BackendDB *be, 32 Entry *e, 33 ID *ids ); 34 35static 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 44static int parse_paged_cookie( Operation *op, SlapReply *rs ); 45 46static 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 */ 55static 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 */ 134static 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 } 230skip: 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; 245nextido: 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 */ 278static int 279mdb_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 295static 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 304static 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 320static 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 331static void *search_stack( Operation *op ); 332 333typedef 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 */ 352static void 353mdb_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 368static void 369mdb_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 377static int 378mdb_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 424int 425mdb_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, *iscopes, *c0; 431 ID2 *scopes; 432 void *stack; 433 Entry *e = NULL, *base = NULL; 434 Entry *matched = NULL; 435 AttributeName *attrs; 436 slap_mask_t mask; 437 time_t stoptime; 438 int manageDSAit; 439 int tentries = 0; 440 int admincheck = 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" ); 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 candidates = c0 = search_stack( op ); 480 iscopes = candidates + MDB_idl_um_size; 481 stack = iscopes + MDB_idl_db_size; 482 /* if candidates already in use, alloc a new array */ 483 if ( c0[0] ) { 484 candidates = ch_malloc(( MDB_idl_um_size + MDB_idl_db_size ) * sizeof ( ID )); 485 iscopes = candidates + MDB_idl_um_size; 486 } 487 isc.mt = ltid; 488 isc.mc = mcd; 489 isc.scopes = scopes; 490 isc.oscope = op->ors_scope; 491 isc.sctmp = stack; 492 493 if ( op->ors_deref & LDAP_DEREF_FINDING ) { 494 MDB_IDL_ZERO(candidates); 495 } 496dn2entry_retry: 497 /* get entry with reader lock */ 498 rs->sr_err = mdb_dn2entry( op, ltid, mcd, &op->o_req_ndn, &e, &nsubs, 1 ); 499 500 switch(rs->sr_err) { 501 case MDB_NOTFOUND: 502 matched = e; 503 e = NULL; 504 break; 505 case 0: 506 break; 507 case LDAP_BUSY: 508 send_ldap_error( op, rs, LDAP_BUSY, "ldap server busy" ); 509 goto done; 510 default: 511 send_ldap_error( op, rs, LDAP_OTHER, "internal error" ); 512 goto done; 513 } 514 515 if ( op->ors_deref & LDAP_DEREF_FINDING ) { 516 if ( matched && is_entry_alias( matched )) { 517 struct berval stub; 518 519 stub.bv_val = op->o_req_ndn.bv_val; 520 stub.bv_len = op->o_req_ndn.bv_len - matched->e_nname.bv_len - 1; 521 e = deref_base( op, rs, matched, &matched, ltid, 522 candidates, NULL ); 523 if ( e ) { 524 build_new_dn( &op->o_req_ndn, &e->e_nname, &stub, 525 op->o_tmpmemctx ); 526 mdb_entry_return(op, e); 527 matched = NULL; 528 goto dn2entry_retry; 529 } 530 } else if ( e && is_entry_alias( e )) { 531 e = deref_base( op, rs, e, &matched, ltid, 532 candidates, NULL ); 533 } 534 } 535 536 if ( e == NULL ) { 537 struct berval matched_dn = BER_BVNULL; 538 539 if ( matched != NULL ) { 540 BerVarray erefs = NULL; 541 542 /* return referral only if "disclose" 543 * is granted on the object */ 544 if ( ! access_allowed( op, matched, 545 slap_schema.si_ad_entry, 546 NULL, ACL_DISCLOSE, NULL ) ) 547 { 548 rs->sr_err = LDAP_NO_SUCH_OBJECT; 549 550 } else { 551 ber_dupbv( &matched_dn, &matched->e_name ); 552 553 erefs = is_entry_referral( matched ) 554 ? get_entry_referrals( op, matched ) 555 : NULL; 556 if ( rs->sr_err == MDB_NOTFOUND ) 557 rs->sr_err = LDAP_REFERRAL; 558 rs->sr_matched = matched_dn.bv_val; 559 } 560 561 mdb_entry_return(op, matched); 562 matched = NULL; 563 564 if ( erefs ) { 565 rs->sr_ref = referral_rewrite( erefs, &matched_dn, 566 &op->o_req_dn, op->oq_search.rs_scope ); 567 ber_bvarray_free( erefs ); 568 } 569 570 } else { 571 rs->sr_ref = referral_rewrite( default_referral, 572 NULL, &op->o_req_dn, op->oq_search.rs_scope ); 573 rs->sr_err = rs->sr_ref != NULL ? LDAP_REFERRAL : LDAP_NO_SUCH_OBJECT; 574 } 575 576 send_ldap_result( op, rs ); 577 578 if ( rs->sr_ref ) { 579 ber_bvarray_free( rs->sr_ref ); 580 rs->sr_ref = NULL; 581 } 582 if ( !BER_BVISNULL( &matched_dn ) ) { 583 ber_memfree( matched_dn.bv_val ); 584 rs->sr_matched = NULL; 585 } 586 goto done; 587 } 588 589 /* NOTE: __NEW__ "search" access is required 590 * on searchBase object */ 591 if ( ! access_allowed_mask( op, e, slap_schema.si_ad_entry, 592 NULL, ACL_SEARCH, NULL, &mask ) ) 593 { 594 if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) { 595 rs->sr_err = LDAP_NO_SUCH_OBJECT; 596 } else { 597 rs->sr_err = LDAP_INSUFFICIENT_ACCESS; 598 } 599 600 mdb_entry_return( op,e); 601 send_ldap_result( op, rs ); 602 goto done; 603 } 604 605 if ( !manageDSAit && is_entry_referral( e ) ) { 606 /* entry is a referral */ 607 struct berval matched_dn = BER_BVNULL; 608 BerVarray erefs = NULL; 609 610 ber_dupbv( &matched_dn, &e->e_name ); 611 erefs = get_entry_referrals( op, e ); 612 613 rs->sr_err = LDAP_REFERRAL; 614 615 mdb_entry_return( op, e ); 616 e = NULL; 617 618 if ( erefs ) { 619 rs->sr_ref = referral_rewrite( erefs, &matched_dn, 620 &op->o_req_dn, op->oq_search.rs_scope ); 621 ber_bvarray_free( erefs ); 622 623 if ( !rs->sr_ref ) { 624 rs->sr_text = "bad_referral object"; 625 } 626 } 627 628 Debug( LDAP_DEBUG_TRACE, 629 LDAP_XSTRING(mdb_search) ": entry is referral\n" ); 630 631 rs->sr_matched = matched_dn.bv_val; 632 send_ldap_result( op, rs ); 633 634 ber_bvarray_free( rs->sr_ref ); 635 rs->sr_ref = NULL; 636 ber_memfree( matched_dn.bv_val ); 637 rs->sr_matched = NULL; 638 goto done; 639 } 640 641 if ( get_assert( op ) && 642 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE )) 643 { 644 rs->sr_err = LDAP_ASSERTION_FAILED; 645 mdb_entry_return( op,e); 646 send_ldap_result( op, rs ); 647 goto done; 648 } 649 650 /* compute it anyway; root does not use it */ 651 stoptime = op->o_time + op->ors_tlimit; 652 653 base = e; 654 655 e = NULL; 656 657 /* select candidates */ 658 if ( op->oq_search.rs_scope == LDAP_SCOPE_BASE ) { 659 rs->sr_err = base_candidate( op->o_bd, base, candidates ); 660 scopes[0].mid = 0; 661 ncand = 1; 662 } else { 663 if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ) { 664 size_t nkids; 665 MDB_val key, data; 666 key.mv_data = &base->e_id; 667 key.mv_size = sizeof( ID ); 668 mdb_cursor_get( mcd, &key, &data, MDB_SET ); 669 mdb_cursor_count( mcd, &nkids ); 670 nsubs = nkids - 1; 671 } else if ( !base->e_id ) { 672 /* we don't maintain nsubs for entryID 0. 673 * just grab entry count from id2entry stat 674 */ 675 MDB_stat ms; 676 mdb_stat( ltid, mdb->mi_id2entry, &ms ); 677 nsubs = ms.ms_entries; 678 } 679 MDB_IDL_ZERO( candidates ); 680 scopes[0].mid = 1; 681 scopes[1].mid = base->e_id; 682 scopes[1].mval.mv_data = NULL; 683 rs->sr_err = search_candidates( op, rs, base, 684 &isc, mci, candidates, stack ); 685 686 if ( rs->sr_err == LDAP_ADMINLIMIT_EXCEEDED ) { 687adminlimit: 688 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED; 689 send_ldap_result( op, rs ); 690 rs->sr_err = LDAP_SUCCESS; 691 goto done; 692 } 693 694 ncand = MDB_IDL_N( candidates ); 695 if ( !base->e_id || ncand == NOID ) { 696 /* grab entry count from id2entry stat 697 */ 698 MDB_stat ms; 699 mdb_stat( ltid, mdb->mi_id2entry, &ms ); 700 if ( !base->e_id ) 701 nsubs = ms.ms_entries; 702 if ( ncand == NOID ) 703 ncand = ms.ms_entries; 704 } 705 } 706 707 /* start cursor at beginning of candidates. 708 */ 709 cursor = 0; 710 711 if ( candidates[0] == 0 ) { 712 Debug( LDAP_DEBUG_TRACE, 713 LDAP_XSTRING(mdb_search) ": no candidates\n" ); 714 715 goto nochange; 716 } 717 718 /* if not root and candidates exceed to-be-checked entries, abort */ 719 if ( op->ors_limit /* isroot == FALSE */ && 720 op->ors_limit->lms_s_unchecked != -1 && 721 ncand > (unsigned) op->ors_limit->lms_s_unchecked ) 722 { 723 admincheck = 1; 724 } 725 726 if ( op->ors_limit == NULL /* isroot == TRUE */ || 727 !op->ors_limit->lms_s_pr_hide ) 728 { 729 tentries = ncand; 730 } 731 732 wwctx.flag = 0; 733 wwctx.nentries = 0; 734 /* If we're running in our own read txn */ 735 if ( moi == &opinfo ) { 736 cb.sc_writewait = mdb_writewait; 737 cb.sc_private = &wwctx; 738 wwctx.txn = ltid; 739 wwctx.mcd = NULL; 740 cb.sc_next = op->o_callback; 741 op->o_callback = &cb; 742 } 743 744 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) { 745 PagedResultsState *ps = op->o_pagedresults_state; 746 /* deferred cookie parsing */ 747 rs->sr_err = parse_paged_cookie( op, rs ); 748 if ( rs->sr_err != LDAP_SUCCESS ) { 749 send_ldap_result( op, rs ); 750 goto done; 751 } 752 753 cursor = (ID) ps->ps_cookie; 754 if ( cursor && ps->ps_size == 0 ) { 755 rs->sr_err = LDAP_SUCCESS; 756 rs->sr_text = "search abandoned by pagedResult size=0"; 757 send_ldap_result( op, rs ); 758 goto done; 759 } 760 761 if ( admincheck ) 762 goto adminlimit; 763 764 id = mdb_idl_first( candidates, &cursor ); 765 if ( id == NOID ) { 766 Debug( LDAP_DEBUG_TRACE, 767 LDAP_XSTRING(mdb_search) 768 ": no paged results candidates\n" ); 769 send_paged_response( op, rs, &lastid, 0 ); 770 771 rs->sr_err = LDAP_OTHER; 772 goto done; 773 } 774 if ( id == (ID)ps->ps_cookie ) 775 id = mdb_idl_next( candidates, &cursor ); 776 nsubs = ncand; /* always bypass scope'd search */ 777 goto loop_begin; 778 } 779 if ( nsubs < ncand ) { 780 int rc; 781 /* Do scope-based search */ 782 if ( admincheck && nsubs > (unsigned) op->ors_limit->lms_s_unchecked ) 783 goto adminlimit; 784 785 /* if any alias scopes were set, save them */ 786 if (scopes[0].mid > 1) { 787 cursor = 1; 788 for (cscope = 1; cscope <= scopes[0].mid; cscope++) { 789 /* Ignore the original base */ 790 if (scopes[cscope].mid == base->e_id) 791 continue; 792 iscopes[cursor++] = scopes[cscope].mid; 793 } 794 iscopes[0] = scopes[0].mid - 1; 795 } else { 796 iscopes[0] = 0; 797 } 798 799 wwctx.mcd = mcd; 800 isc.id = base->e_id; 801 isc.numrdns = 0; 802 rc = mdb_dn2id_walk( op, &isc ); 803 if ( rc ) 804 id = NOID; 805 else 806 id = isc.id; 807 cscope = 0; 808 } else { 809 if ( admincheck ) 810 goto adminlimit; 811 id = mdb_idl_first( candidates, &cursor ); 812 } 813 814 while (id != NOID) 815 { 816 int scopeok; 817 MDB_val edata; 818 819loop_begin: 820 821 /* check for abandon */ 822 if ( op->o_abandon ) { 823 rs->sr_err = SLAPD_ABANDON; 824 send_ldap_result( op, rs ); 825 goto done; 826 } 827 828 /* mostly needed by internal searches, 829 * e.g. related to syncrepl, for whom 830 * abandon does not get set... */ 831 if ( slapd_shutdown ) { 832 rs->sr_err = LDAP_UNAVAILABLE; 833 send_ldap_disconnect( op, rs ); 834 goto done; 835 } 836 837 /* check time limit */ 838 if ( op->ors_tlimit != SLAP_NO_LIMIT 839 && slap_get_time() > stoptime ) 840 { 841 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED; 842 rs->sr_ref = rs->sr_v2ref; 843 send_ldap_result( op, rs ); 844 rs->sr_err = LDAP_SUCCESS; 845 goto done; 846 } 847 848 849 if ( nsubs < ncand ) { 850 unsigned i; 851 /* Is this entry in the candidate list? */ 852 scopeok = 0; 853 if (MDB_IDL_IS_RANGE( candidates )) { 854 if ( id >= MDB_IDL_RANGE_FIRST( candidates ) && 855 id <= MDB_IDL_RANGE_LAST( candidates )) 856 scopeok = 1; 857 } else { 858 i = mdb_idl_search( candidates, id ); 859 if (i <= candidates[0] && candidates[i] == id ) 860 scopeok = 1; 861 } 862 if ( scopeok ) 863 goto scopeok; 864 goto loop_continue; 865 } 866 867 /* Does this candidate actually satisfy the search scope? 868 */ 869 scopeok = 0; 870 isc.numrdns = 0; 871 switch( op->ors_scope ) { 872 case LDAP_SCOPE_BASE: 873 /* This is always true, yes? */ 874 if ( id == base->e_id ) scopeok = 1; 875 break; 876 877#ifdef LDAP_SCOPE_CHILDREN 878 case LDAP_SCOPE_CHILDREN: 879 if ( id == base->e_id ) break; 880 /* Fall-thru */ 881#endif 882 case LDAP_SCOPE_SUBTREE: 883 if ( id == base->e_id ) { 884 scopeok = 1; 885 break; 886 } 887 /* Fall-thru */ 888 case LDAP_SCOPE_ONELEVEL: 889 if ( id == base->e_id ) break; 890 isc.id = id; 891 isc.nscope = 0; 892 rs->sr_err = mdb_idscopes( op, &isc ); 893 if ( rs->sr_err == MDB_SUCCESS ) { 894 if ( isc.nscope ) 895 scopeok = 1; 896 } else { 897 if ( rs->sr_err == MDB_NOTFOUND ) 898 goto notfound; 899 } 900 break; 901 } 902 903 /* Not in scope, ignore it */ 904 if ( !scopeok ) 905 { 906 Debug( LDAP_DEBUG_TRACE, 907 LDAP_XSTRING(mdb_search) 908 ": %ld scope not okay\n", 909 (long) id ); 910 goto loop_continue; 911 } 912 913scopeok: 914 if ( id == base->e_id ) { 915 e = base; 916 } else { 917 918 /* get the entry */ 919 rs->sr_err = mdb_id2edata( op, mci, id, &edata ); 920 if ( rs->sr_err == MDB_NOTFOUND ) { 921notfound: 922 if( nsubs < ncand ) 923 goto loop_continue; 924 925 if( !MDB_IDL_IS_RANGE(candidates) ) { 926 /* only complain for non-range IDLs */ 927 Debug( LDAP_DEBUG_TRACE, 928 LDAP_XSTRING(mdb_search) 929 ": candidate %ld not found\n", 930 (long) id ); 931 } else { 932 /* get the next ID from the DB */ 933 rs->sr_err = mdb_get_nextid( mci, &cursor ); 934 if ( rs->sr_err == MDB_NOTFOUND ) { 935 break; 936 } 937 if ( rs->sr_err ) { 938 rs->sr_err = LDAP_OTHER; 939 rs->sr_text = "internal error in get_nextid"; 940 send_ldap_result( op, rs ); 941 goto done; 942 } 943 cursor--; 944 } 945 946 goto loop_continue; 947 } else if ( rs->sr_err ) { 948 rs->sr_err = LDAP_OTHER; 949 rs->sr_text = "internal error in mdb_id2edata"; 950 send_ldap_result( op, rs ); 951 goto done; 952 } 953 954 rs->sr_err = mdb_entry_decode( op, ltid, &edata, id, &e ); 955 if ( rs->sr_err ) { 956 rs->sr_err = LDAP_OTHER; 957 rs->sr_text = "internal error in mdb_entry_decode"; 958 send_ldap_result( op, rs ); 959 goto done; 960 } 961 e->e_id = id; 962 e->e_name.bv_val = NULL; 963 e->e_nname.bv_val = NULL; 964 } 965 966 if ( is_entry_subentry( e ) ) { 967 if( op->oq_search.rs_scope != LDAP_SCOPE_BASE ) { 968 if(!get_subentries_visibility( op )) { 969 /* only subentries are visible */ 970 goto loop_continue; 971 } 972 973 } else if ( get_subentries( op ) && 974 !get_subentries_visibility( op )) 975 { 976 /* only subentries are visible */ 977 goto loop_continue; 978 } 979 980 } else if ( get_subentries_visibility( op )) { 981 /* only subentries are visible */ 982 goto loop_continue; 983 } 984 985 /* aliases were already dereferenced in candidate list */ 986 if ( op->ors_deref & LDAP_DEREF_SEARCHING ) { 987 /* but if the search base is an alias, and we didn't 988 * deref it when finding, return it. 989 */ 990 if ( is_entry_alias(e) && 991 ((op->ors_deref & LDAP_DEREF_FINDING) || e != base )) 992 { 993 goto loop_continue; 994 } 995 } 996 997 if ( !manageDSAit && is_entry_glue( e )) { 998 goto loop_continue; 999 } 1000 1001 if (e != base) { 1002 struct berval pdn, pndn; 1003 char *d, *n; 1004 int i; 1005 1006 /* child of base, just append RDNs to base->e_name */ 1007 if ( nsubs < ncand || isc.scopes[isc.nscope].mid == base->e_id ) { 1008 pdn = base->e_name; 1009 pndn = base->e_nname; 1010 } else { 1011 mdb_id2name( op, ltid, &isc.mc, scopes[isc.nscope].mid, &pdn, &pndn ); 1012 } 1013 e->e_name.bv_len = pdn.bv_len; 1014 e->e_nname.bv_len = pndn.bv_len; 1015 for (i=0; i<isc.numrdns; i++) { 1016 e->e_name.bv_len += isc.rdns[i].bv_len + 1; 1017 e->e_nname.bv_len += isc.nrdns[i].bv_len + 1; 1018 } 1019 e->e_name.bv_val = op->o_tmpalloc(e->e_name.bv_len + 1, op->o_tmpmemctx); 1020 e->e_nname.bv_val = op->o_tmpalloc(e->e_nname.bv_len + 1, op->o_tmpmemctx); 1021 d = e->e_name.bv_val; 1022 n = e->e_nname.bv_val; 1023 if (nsubs < ncand) { 1024 /* RDNs are in top-down order */ 1025 for (i=isc.numrdns-1; i>=0; i--) { 1026 memcpy(d, isc.rdns[i].bv_val, isc.rdns[i].bv_len); 1027 d += isc.rdns[i].bv_len; 1028 *d++ = ','; 1029 memcpy(n, isc.nrdns[i].bv_val, isc.nrdns[i].bv_len); 1030 n += isc.nrdns[i].bv_len; 1031 *n++ = ','; 1032 } 1033 } else { 1034 /* RDNs are in bottom-up order */ 1035 for (i=0; i<isc.numrdns; i++) { 1036 memcpy(d, isc.rdns[i].bv_val, isc.rdns[i].bv_len); 1037 d += isc.rdns[i].bv_len; 1038 *d++ = ','; 1039 memcpy(n, isc.nrdns[i].bv_val, isc.nrdns[i].bv_len); 1040 n += isc.nrdns[i].bv_len; 1041 *n++ = ','; 1042 } 1043 } 1044 1045 if (pdn.bv_len) { 1046 memcpy(d, pdn.bv_val, pdn.bv_len+1); 1047 memcpy(n, pndn.bv_val, pndn.bv_len+1); 1048 } else { 1049 *--d = '\0'; 1050 *--n = '\0'; 1051 e->e_name.bv_len--; 1052 e->e_nname.bv_len--; 1053 } 1054 if (pndn.bv_val != base->e_nname.bv_val) { 1055 op->o_tmpfree(pndn.bv_val, op->o_tmpmemctx); 1056 op->o_tmpfree(pdn.bv_val, op->o_tmpmemctx); 1057 } 1058 } 1059 1060 /* 1061 * if it's a referral, add it to the list of referrals. only do 1062 * this for non-base searches, and don't check the filter 1063 * explicitly here since it's only a candidate anyway. 1064 */ 1065 if ( !manageDSAit && op->oq_search.rs_scope != LDAP_SCOPE_BASE 1066 && is_entry_referral( e ) ) 1067 { 1068 BerVarray erefs = get_entry_referrals( op, e ); 1069 rs->sr_ref = referral_rewrite( erefs, &e->e_name, NULL, 1070 op->oq_search.rs_scope == LDAP_SCOPE_ONELEVEL 1071 ? LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE ); 1072 1073 rs->sr_entry = e; 1074 rs->sr_flags = 0; 1075 1076 send_search_reference( op, rs ); 1077 1078 if (e != base) 1079 mdb_entry_return( op, e ); 1080 rs->sr_entry = NULL; 1081 e = NULL; 1082 1083 ber_bvarray_free( rs->sr_ref ); 1084 ber_bvarray_free( erefs ); 1085 rs->sr_ref = NULL; 1086 1087 goto loop_continue; 1088 } 1089 1090 /* if it matches the filter and scope, send it */ 1091 rs->sr_err = test_filter( op, e, op->oq_search.rs_filter ); 1092 1093 if ( rs->sr_err == LDAP_COMPARE_TRUE ) { 1094 /* check size limit */ 1095 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) { 1096 if ( rs->sr_nentries >= ((PagedResultsState *)op->o_pagedresults_state)->ps_size ) { 1097 if (e != base) 1098 mdb_entry_return( op, e ); 1099 e = NULL; 1100 send_paged_response( op, rs, &lastid, tentries ); 1101 goto done; 1102 } 1103 lastid = id; 1104 } 1105 1106 if (e) { 1107 /* safe default */ 1108 rs->sr_attrs = op->oq_search.rs_attrs; 1109 rs->sr_operational_attrs = NULL; 1110 rs->sr_ctrls = NULL; 1111 rs->sr_entry = e; 1112 RS_ASSERT( e->e_private != NULL ); 1113 rs->sr_flags = 0; 1114 rs->sr_err = LDAP_SUCCESS; 1115 rs->sr_err = send_search_entry( op, rs ); 1116 rs->sr_attrs = NULL; 1117 rs->sr_entry = NULL; 1118 if (e != base) 1119 mdb_entry_return( op, e ); 1120 e = NULL; 1121 1122 switch ( rs->sr_err ) { 1123 case LDAP_SUCCESS: /* entry sent ok */ 1124 break; 1125 default: /* entry not sent */ 1126 break; 1127 case LDAP_BUSY: 1128 send_ldap_result( op, rs ); 1129 goto done; 1130 case LDAP_UNAVAILABLE: 1131 case LDAP_SIZELIMIT_EXCEEDED: 1132 if ( rs->sr_err == LDAP_SIZELIMIT_EXCEEDED ) { 1133 rs->sr_ref = rs->sr_v2ref; 1134 send_ldap_result( op, rs ); 1135 rs->sr_err = LDAP_SUCCESS; 1136 1137 } else { 1138 rs->sr_err = LDAP_OTHER; 1139 } 1140 goto done; 1141 } 1142 } 1143 1144 } else { 1145 Debug( LDAP_DEBUG_TRACE, 1146 LDAP_XSTRING(mdb_search) 1147 ": %ld does not match filter\n", 1148 (long) id ); 1149 } 1150 1151loop_continue: 1152 if ( moi == &opinfo && !wwctx.flag && mdb->mi_rtxn_size ) { 1153 wwctx.nentries++; 1154 if ( wwctx.nentries >= mdb->mi_rtxn_size ) { 1155 MDB_envinfo ei; 1156 wwctx.nentries = 0; 1157 mdb_env_info(mdb->mi_dbenv, &ei); 1158 if ( ei.me_last_txnid > mdb_txn_id( ltid )) 1159 mdb_rtxn_snap( op, &wwctx ); 1160 } 1161 } 1162 if ( wwctx.flag ) { 1163 rs->sr_err = mdb_waitfixup( op, &wwctx, mci, mcd, &isc ); 1164 if ( rs->sr_err ) { 1165 send_ldap_result( op, rs ); 1166 goto done; 1167 } 1168 } 1169 1170 if( e != NULL ) { 1171 if ( e != base ) 1172 mdb_entry_return( op, e ); 1173 RS_ASSERT( rs->sr_entry == NULL ); 1174 e = NULL; 1175 rs->sr_entry = NULL; 1176 } 1177 1178 if ( nsubs < ncand ) { 1179 int rc = mdb_dn2id_walk( op, &isc ); 1180 if (rc) { 1181 id = NOID; 1182 /* We got to the end of a subtree. If there are any 1183 * alias scopes left, search them too. 1184 */ 1185 while (iscopes[0] && cscope < iscopes[0]) { 1186 cscope++; 1187 isc.id = iscopes[cscope]; 1188 if ( base ) 1189 mdb_entry_return( op, base ); 1190 rs->sr_err = mdb_id2entry(op, mci, isc.id, &base); 1191 if ( !rs->sr_err ) { 1192 mdb_id2name( op, ltid, &isc.mc, isc.id, &base->e_name, &base->e_nname ); 1193 isc.numrdns = 0; 1194 if (isc.oscope == LDAP_SCOPE_ONELEVEL) 1195 isc.oscope = LDAP_SCOPE_BASE; 1196 rc = mdb_dn2id_walk( op, &isc ); 1197 if ( !rc ) { 1198 id = isc.id; 1199 break; 1200 } 1201 } 1202 } 1203 } else 1204 id = isc.id; 1205 } else { 1206 id = mdb_idl_next( candidates, &cursor ); 1207 } 1208 } 1209 1210nochange: 1211 rs->sr_ctrls = NULL; 1212 rs->sr_ref = rs->sr_v2ref; 1213 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS : LDAP_REFERRAL; 1214 rs->sr_rspoid = NULL; 1215 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) { 1216 send_paged_response( op, rs, NULL, 0 ); 1217 } else { 1218 send_ldap_result( op, rs ); 1219 } 1220 1221 rs->sr_err = LDAP_SUCCESS; 1222 1223done: 1224 if ( cb.sc_private ) { 1225 /* remove our writewait callback */ 1226 slap_callback **scp = &op->o_callback; 1227 while ( *scp ) { 1228 if ( *scp == &cb ) { 1229 *scp = cb.sc_next; 1230 cb.sc_private = NULL; 1231 break; 1232 } 1233 } 1234 } 1235 mdb_cursor_close( mcd ); 1236 mdb_cursor_close( mci ); 1237 if ( moi == &opinfo ) { 1238 mdb_txn_reset( moi->moi_txn ); 1239 LDAP_SLIST_REMOVE( &op->o_extra, &moi->moi_oe, OpExtra, oe_next ); 1240 } else { 1241 moi->moi_ref--; 1242 } 1243 if( rs->sr_v2ref ) { 1244 ber_bvarray_free( rs->sr_v2ref ); 1245 rs->sr_v2ref = NULL; 1246 } 1247 if (base) 1248 mdb_entry_return( op, base ); 1249 scope_chunk_ret( op, scopes ); 1250 if ( candidates != c0 ) { 1251 ch_free( candidates ); 1252 } else { 1253 MDB_IDL_ZERO( candidates ); 1254 } 1255 1256 return rs->sr_err; 1257} 1258 1259 1260static int base_candidate( 1261 BackendDB *be, 1262 Entry *e, 1263 ID *ids ) 1264{ 1265 Debug(LDAP_DEBUG_ARGS, "base_candidates: base: \"%s\" (0x%08lx)\n", 1266 e->e_nname.bv_val, (long) e->e_id ); 1267 1268 ids[0] = 1; 1269 ids[1] = e->e_id; 1270 return 0; 1271} 1272 1273/* Look for "objectClass Present" in this filter. 1274 * Also count depth of filter tree while we're at it. 1275 */ 1276static int oc_filter( 1277 Filter *f, 1278 int cur, 1279 int *max ) 1280{ 1281 int rc = 0; 1282 1283 assert( f != NULL ); 1284 1285 if( cur > *max ) *max = cur; 1286 1287 switch( f->f_choice ) { 1288 case LDAP_FILTER_PRESENT: 1289 if (f->f_desc == slap_schema.si_ad_objectClass) { 1290 rc = 1; 1291 } 1292 break; 1293 1294 case LDAP_FILTER_AND: 1295 case LDAP_FILTER_OR: 1296 cur++; 1297 for ( f=f->f_and; f; f=f->f_next ) { 1298 (void) oc_filter(f, cur, max); 1299 } 1300 break; 1301 1302 default: 1303 break; 1304 } 1305 return rc; 1306} 1307 1308typedef struct IDLchunk { 1309 unsigned int logn; 1310 unsigned int pad; 1311} IDLchunk; 1312 1313static void search_stack_free( void *key, void *data ) 1314{ 1315 ber_memfree_x(data, NULL); 1316} 1317 1318static void *search_stack( Operation *op ) 1319{ 1320 struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private; 1321 IDLchunk *ic = NULL; 1322 1323 if ( op->o_threadctx ) { 1324 ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)search_stack, 1325 (void **)&ic, NULL ); 1326 } else { 1327 ic = mdb->mi_search_stack; 1328 } 1329 1330 if ( ic && ic->logn != MDB_idl_logn ) { 1331 ber_memfree_x( ic, NULL ); 1332 ic = NULL; 1333 } 1334 1335 if ( !ic ) { 1336 ic = ch_malloc(( mdb->mi_search_stack_depth + 2 ) * MDB_idl_um_size 1337 * sizeof( ID ) + sizeof( IDLchunk ) ); 1338 ic->logn = MDB_idl_logn; 1339 if ( op->o_threadctx ) { 1340 ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)search_stack, 1341 ic, search_stack_free, NULL, NULL ); 1342 } else { 1343 mdb->mi_search_stack = ic; 1344 } 1345 ID *idl = (ID *)( ic+1 ); 1346 MDB_IDL_ZERO( idl ); 1347 } 1348 return ic+1; 1349} 1350 1351static int search_candidates( 1352 Operation *op, 1353 SlapReply *rs, 1354 Entry *e, 1355 IdScopes *isc, 1356 MDB_cursor *mci, 1357 ID *ids, 1358 ID *stack ) 1359{ 1360 struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private; 1361 int rc, depth = 1; 1362 Filter *f, rf, xf, nf, sf; 1363 AttributeAssertion aa_ref = ATTRIBUTEASSERTION_INIT; 1364 AttributeAssertion aa_subentry = ATTRIBUTEASSERTION_INIT; 1365 1366 /* 1367 * This routine takes as input a filter (user-filter) 1368 * and rewrites it as follows: 1369 * (&(scope=DN)[(objectClass=subentry)] 1370 * (|[(objectClass=referral)](user-filter)) 1371 */ 1372 1373 Debug(LDAP_DEBUG_TRACE, 1374 "search_candidates: base=\"%s\" (0x%08lx) scope=%d\n", 1375 e->e_nname.bv_val, (long) e->e_id, op->oq_search.rs_scope ); 1376 1377 f = op->oq_search.rs_filter; 1378 1379 /* If the user's filter uses objectClass=*, 1380 * these clauses are redundant. 1381 */ 1382 if (!oc_filter(op->oq_search.rs_filter, 1, &depth) 1383 && !get_subentries_visibility(op)) { 1384 if( !get_manageDSAit(op) && !get_domainScope(op) ) { 1385 /* match referral objects */ 1386 struct berval bv_ref = BER_BVC( "referral" ); 1387 rf.f_choice = LDAP_FILTER_EQUALITY; 1388 rf.f_ava = &aa_ref; 1389 rf.f_av_desc = slap_schema.si_ad_objectClass; 1390 rf.f_av_value = bv_ref; 1391 rf.f_next = f; 1392 xf.f_or = &rf; 1393 xf.f_choice = LDAP_FILTER_OR; 1394 xf.f_next = NULL; 1395 f = &xf; 1396 depth++; 1397 } 1398 } 1399 1400 if( get_subentries_visibility( op ) ) { 1401 struct berval bv_subentry = BER_BVC( "subentry" ); 1402 sf.f_choice = LDAP_FILTER_EQUALITY; 1403 sf.f_ava = &aa_subentry; 1404 sf.f_av_desc = slap_schema.si_ad_objectClass; 1405 sf.f_av_value = bv_subentry; 1406 sf.f_next = f; 1407 nf.f_choice = LDAP_FILTER_AND; 1408 nf.f_and = &sf; 1409 nf.f_next = NULL; 1410 f = &nf; 1411 depth++; 1412 } 1413 1414 /* Allocate IDL stack, plus 1 more for former tmp */ 1415 if ( depth+1 > mdb->mi_search_stack_depth ) { 1416 stack = ch_malloc( (depth + 1) * MDB_idl_um_size * sizeof( ID ) ); 1417 } 1418 1419 if( op->ors_deref & LDAP_DEREF_SEARCHING ) { 1420 rc = search_aliases( op, rs, e->e_id, isc, mci, stack ); 1421 } else { 1422 rc = LDAP_SUCCESS; 1423 } 1424 1425 if ( rc == LDAP_SUCCESS ) { 1426 rc = mdb_filter_candidates( op, isc->mt, f, ids, 1427 stack, stack+MDB_idl_um_size ); 1428 } 1429 1430 if ( depth+1 > mdb->mi_search_stack_depth ) { 1431 ch_free( stack ); 1432 } 1433 1434 if( rc ) { 1435 Debug(LDAP_DEBUG_TRACE, 1436 "mdb_search_candidates: failed (rc=%d)\n", 1437 rc ); 1438 1439 } else { 1440 Debug(LDAP_DEBUG_TRACE, 1441 "mdb_search_candidates: id=%ld first=%ld last=%ld\n", 1442 (long) ids[0], 1443 (long) MDB_IDL_FIRST(ids), 1444 (long) MDB_IDL_LAST(ids) ); 1445 } 1446 1447 return rc; 1448} 1449 1450static int 1451parse_paged_cookie( Operation *op, SlapReply *rs ) 1452{ 1453 int rc = LDAP_SUCCESS; 1454 PagedResultsState *ps = op->o_pagedresults_state; 1455 1456 /* this function must be invoked only if the pagedResults 1457 * control has been detected, parsed and partially checked 1458 * by the frontend */ 1459 assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ); 1460 1461 /* cookie decoding/checks deferred to backend... */ 1462 if ( ps->ps_cookieval.bv_len ) { 1463 PagedResultsCookie reqcookie; 1464 if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) { 1465 /* bad cookie */ 1466 rs->sr_text = "paged results cookie is invalid"; 1467 rc = LDAP_PROTOCOL_ERROR; 1468 goto done; 1469 } 1470 1471 AC_MEMCPY( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie )); 1472 1473 if ( reqcookie > ps->ps_cookie ) { 1474 /* bad cookie */ 1475 rs->sr_text = "paged results cookie is invalid"; 1476 rc = LDAP_PROTOCOL_ERROR; 1477 goto done; 1478 1479 } else if ( reqcookie < ps->ps_cookie ) { 1480 rs->sr_text = "paged results cookie is invalid or old"; 1481 rc = LDAP_UNWILLING_TO_PERFORM; 1482 goto done; 1483 } 1484 1485 } else { 1486 /* we're going to use ps_cookie */ 1487 op->o_conn->c_pagedresults_state.ps_cookie = 0; 1488 } 1489 1490done:; 1491 1492 return rc; 1493} 1494 1495static void 1496send_paged_response( 1497 Operation *op, 1498 SlapReply *rs, 1499 ID *lastid, 1500 int tentries ) 1501{ 1502 LDAPControl *ctrls[2]; 1503 BerElementBuffer berbuf; 1504 BerElement *ber = (BerElement *)&berbuf; 1505 PagedResultsCookie respcookie; 1506 struct berval cookie; 1507 1508 Debug(LDAP_DEBUG_ARGS, 1509 "send_paged_response: lastid=0x%08lx nentries=%d\n", 1510 lastid ? *lastid : 0, rs->sr_nentries ); 1511 1512 ctrls[1] = NULL; 1513 1514 ber_init2( ber, NULL, LBER_USE_DER ); 1515 1516 if ( lastid ) { 1517 respcookie = ( PagedResultsCookie )(*lastid); 1518 cookie.bv_len = sizeof( respcookie ); 1519 cookie.bv_val = (char *)&respcookie; 1520 1521 } else { 1522 respcookie = ( PagedResultsCookie )0; 1523 BER_BVSTR( &cookie, "" ); 1524 } 1525 1526 op->o_conn->c_pagedresults_state.ps_cookie = respcookie; 1527 op->o_conn->c_pagedresults_state.ps_count = 1528 ((PagedResultsState *)op->o_pagedresults_state)->ps_count + 1529 rs->sr_nentries; 1530 1531 /* return size of 0 -- no estimate */ 1532 ber_printf( ber, "{iO}", 0, &cookie ); 1533 1534 ctrls[0] = op->o_tmpalloc( sizeof(LDAPControl), op->o_tmpmemctx ); 1535 if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) { 1536 goto done; 1537 } 1538 1539 ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS; 1540 ctrls[0]->ldctl_iscritical = 0; 1541 1542 slap_add_ctrls( op, rs, ctrls ); 1543 rs->sr_err = LDAP_SUCCESS; 1544 send_ldap_result( op, rs ); 1545 1546done: 1547 (void) ber_free_buf( ber ); 1548} 1549