1 /* OpenLDAP WiredTiger backend */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 * Copyright 2002-2018 The OpenLDAP Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted only as authorized by the OpenLDAP
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>.
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.
25 #include <ac/string.h>
30 static int search_aliases(
39 /* TODO: search_aliases does not implement yet. */
44 static int base_candidate(
49 Debug(LDAP_DEBUG_ARGS,
50 LDAP_XSTRING(base_candidate)
51 ": base: \"%s\" (0x%08lx)\n",
52 e->e_nname.bv_val, (long) e->e_id, 0);
59 /* Look for "objectClass Present" in this filter.
60 * Also count depth of filter tree while we're at it.
71 if( cur > *max ) *max = cur;
73 switch( f->f_choice ) {
74 case LDAP_FILTER_PRESENT:
75 if (f->f_desc == slap_schema.si_ad_objectClass) {
83 for ( f=f->f_and; f; f=f->f_next ) {
84 (void) oc_filter(f, cur, max);
94 static void search_stack_free( void *key, void *data )
96 ber_memfree_x(data, NULL);
99 static void *search_stack( Operation *op )
101 struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
104 if ( op->o_threadctx ) {
105 ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)search_stack,
108 ret = wi->wi_search_stack;
112 ret = ch_malloc( wi->wi_search_stack_depth * WT_IDL_UM_SIZE
114 if ( op->o_threadctx ) {
115 ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)search_stack,
116 ret, search_stack_free, NULL, NULL );
118 wi->wi_search_stack = ret;
124 static int search_candidates(
132 struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
134 Filter f, rf, xf, nf;
136 AttributeAssertion aa_ref = ATTRIBUTEASSERTION_INIT;
138 AttributeAssertion aa_subentry = ATTRIBUTEASSERTION_INIT;
140 Debug(LDAP_DEBUG_TRACE,
141 LDAP_XSTRING(wt_search_candidates)
142 ": base=\"%s\" (0x%08lx) scope=%d\n",
143 e->e_nname.bv_val, (long) e->e_id, op->oq_search.rs_scope );
145 xf.f_or = op->oq_search.rs_filter;
146 xf.f_choice = LDAP_FILTER_OR;
149 /* If the user's filter uses objectClass=*,
150 * these clauses are redundant.
152 if (!oc_filter(op->oq_search.rs_filter, 1, &depth)
153 && !get_subentries_visibility(op)) {
154 if( !get_manageDSAit(op) && !get_domainScope(op) ) {
155 /* match referral objects */
156 struct berval bv_ref = BER_BVC( "referral" );
157 rf.f_choice = LDAP_FILTER_EQUALITY;
159 rf.f_av_desc = slap_schema.si_ad_objectClass;
160 rf.f_av_value = bv_ref;
168 f.f_choice = LDAP_FILTER_AND;
170 /* Dummy; we compute scope separately now */
171 nf.f_choice = SLAPD_FILTER_COMPUTED;
172 nf.f_result = LDAP_SUCCESS;
173 nf.f_next = ( xf.f_or == op->oq_search.rs_filter )
174 ? op->oq_search.rs_filter : &xf ;
175 /* Filter depth increased again, adding dummy clause */
178 if( get_subentries_visibility( op ) ) {
179 struct berval bv_subentry = BER_BVC( "subentry" );
180 sf.f_choice = LDAP_FILTER_EQUALITY;
181 sf.f_ava = &aa_subentry;
182 sf.f_av_desc = slap_schema.si_ad_objectClass;
183 sf.f_av_value = bv_subentry;
184 sf.f_next = nf.f_next;
188 /* Allocate IDL stack, plus 1 more for former tmp */
189 if ( depth+1 > wi->wi_search_stack_depth ) {
190 stack = ch_malloc( (depth + 1) * WT_IDL_UM_SIZE * sizeof( ID ) );
192 stack = search_stack( op );
195 if( op->ors_deref & LDAP_DEREF_SEARCHING ) {
196 rc = search_aliases( op, rs, e, wc->session, ids, scopes, stack );
197 if ( WT_IDL_IS_ZERO( ids ) && rc == LDAP_SUCCESS )
198 rc = wt_dn2idl( op, wc->session, &e->e_nname, e, ids, stack );
200 rc = wt_dn2idl(op, wc->session, &e->e_nname, e, ids, stack );
203 if ( rc == LDAP_SUCCESS ) {
204 rc = wt_filter_candidates( op, wc, &f, ids,
205 stack, stack+WT_IDL_UM_SIZE );
208 if ( depth+1 > wi->wi_search_stack_depth ) {
213 Debug(LDAP_DEBUG_TRACE,
214 LDAP_XSTRING(wt_search_candidates)
215 ": failed (rc=%d)\n",
219 Debug(LDAP_DEBUG_TRACE,
220 LDAP_XSTRING(wt_search_candidates)
221 ": id=%ld first=%ld last=%ld\n",
223 (long) WT_IDL_FIRST(ids),
224 (long) WT_IDL_LAST(ids));
230 parse_paged_cookie( Operation *op, SlapReply *rs )
232 int rc = LDAP_SUCCESS;
233 PagedResultsState *ps = op->o_pagedresults_state;
235 /* this function must be invoked only if the pagedResults
236 * control has been detected, parsed and partially checked
238 assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
240 /* cookie decoding/checks deferred to backend... */
241 if ( ps->ps_cookieval.bv_len ) {
242 PagedResultsCookie reqcookie;
243 if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
245 rs->sr_text = "paged results cookie is invalid";
246 rc = LDAP_PROTOCOL_ERROR;
250 AC_MEMCPY( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));
252 if ( reqcookie > ps->ps_cookie ) {
254 rs->sr_text = "paged results cookie is invalid";
255 rc = LDAP_PROTOCOL_ERROR;
258 } else if ( reqcookie < ps->ps_cookie ) {
259 rs->sr_text = "paged results cookie is invalid or old";
260 rc = LDAP_UNWILLING_TO_PERFORM;
265 /* we're going to use ps_cookie */
266 op->o_conn->c_pagedresults_state.ps_cookie = 0;
281 LDAPControl *ctrls[2];
282 BerElementBuffer berbuf;
283 BerElement *ber = (BerElement *)&berbuf;
284 PagedResultsCookie respcookie;
285 struct berval cookie;
287 Debug(LDAP_DEBUG_ARGS,
288 LDAP_XSTRING(send_paged_response)
289 ": lastid=0x%08lx nentries=%d\n",
290 lastid ? *lastid : 0, rs->sr_nentries, NULL );
294 ber_init2( ber, NULL, LBER_USE_DER );
297 respcookie = ( PagedResultsCookie )(*lastid);
298 cookie.bv_len = sizeof( respcookie );
299 cookie.bv_val = (char *)&respcookie;
302 respcookie = ( PagedResultsCookie )0;
303 BER_BVSTR( &cookie, "" );
306 op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
307 op->o_conn->c_pagedresults_state.ps_count =
308 ((PagedResultsState *)op->o_pagedresults_state)->ps_count +
311 /* return size of 0 -- no estimate */
312 ber_printf( ber, "{iO}", 0, &cookie );
314 ctrls[0] = op->o_tmpalloc( sizeof(LDAPControl), op->o_tmpmemctx );
315 if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
319 ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
320 ctrls[0]->ldctl_iscritical = 0;
322 slap_add_ctrls( op, rs, ctrls );
323 rs->sr_err = LDAP_SUCCESS;
324 send_ldap_result( op, rs );
327 (void) ber_free_buf( ber );
331 wt_search( Operation *op, SlapReply *rs )
333 struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
336 AttributeName *attrs;
346 ID candidates[WT_IDL_UM_SIZE];
347 ID iscopes[WT_IDL_DB_SIZE];
348 ID scopes[WT_IDL_DB_SIZE];
350 unsigned nentries = 0;
352 Debug( LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(wt_search) ": %s\n",
353 op->o_req_dn.bv_val, 0, 0 );
354 attrs = op->oq_search.rs_attrs;
356 manageDSAit = get_manageDSAit( op );
358 wc = wt_ctx_get(op, wi);
360 Debug( LDAP_DEBUG_ANY,
361 LDAP_XSTRING(wt_search)
362 ": wt_ctx_get failed: %d\n",
364 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
369 rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
374 Debug( LDAP_DEBUG_ARGS,
375 "<== " LDAP_XSTRING(wt_search)
376 ": no such object %s\n",
377 op->o_req_dn.bv_val, 0, 0);
378 rs->sr_err = LDAP_REFERRAL;
379 rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
380 send_ldap_result( op, rs );
383 /* TODO: error handling */
384 Debug( LDAP_DEBUG_ANY,
385 LDAP_XSTRING(wt_delete)
386 ": error at wt_dn2entry() rc=%d\n",
388 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
392 if ( op->ors_deref & LDAP_DEREF_FINDING ) {
393 /* not implement yet */
400 /* NOTE: __NEW__ "search" access is required
401 * on searchBase object */
402 if ( ! access_allowed_mask( op, e, slap_schema.si_ad_entry,
403 NULL, ACL_SEARCH, NULL, &mask ) )
405 if ( !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
406 rs->sr_err = LDAP_NO_SUCH_OBJECT;
408 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
411 send_ldap_result( op, rs );
415 if ( !manageDSAit && is_entry_referral( e ) ) {
416 /* entry is a referral */
420 if ( get_assert( op ) &&
421 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
423 rs->sr_err = LDAP_ASSERTION_FAILED;
424 send_ldap_result( op, rs );
428 /* compute it anyway; root does not use it */
429 stoptime = op->o_time + op->ors_tlimit;
435 /* select candidates */
436 if ( op->oq_search.rs_scope == LDAP_SCOPE_BASE ) {
437 rs->sr_err = base_candidate( op->o_bd, base, candidates );
439 WT_IDL_ZERO( candidates );
440 WT_IDL_ZERO( scopes );
441 rc = search_candidates( op, rs, base,
442 wc, candidates, scopes );
448 Debug( LDAP_DEBUG_ANY,
449 LDAP_XSTRING(wt_search) ": error search_candidates\n",
451 send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
456 /* start cursor at beginning of candidates.
460 if ( candidates[0] == 0 ) {
461 Debug( LDAP_DEBUG_TRACE,
462 LDAP_XSTRING(wt_search) ": no candidates\n",
467 if ( op->ors_limit &&
468 op->ors_limit->lms_s_unchecked != -1 &&
469 WT_IDL_N(candidates) > (unsigned) op->ors_limit->lms_s_unchecked )
471 rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
472 send_ldap_result( op, rs );
473 rs->sr_err = LDAP_SUCCESS;
477 if ( op->ors_limit == NULL /* isroot == TRUE */ ||
478 !op->ors_limit->lms_s_pr_hide )
480 tentries = WT_IDL_N(candidates);
483 if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
484 /* TODO: pageresult */
485 PagedResultsState *ps = op->o_pagedresults_state;
486 /* deferred cookie parsing */
487 rs->sr_err = parse_paged_cookie( op, rs );
488 if ( rs->sr_err != LDAP_SUCCESS ) {
489 send_ldap_result( op, rs );
493 cursor = (ID) ps->ps_cookie;
494 if ( cursor && ps->ps_size == 0 ) {
495 rs->sr_err = LDAP_SUCCESS;
496 rs->sr_text = "search abandoned by pagedResult size=0";
497 send_ldap_result( op, rs );
500 id = wt_idl_first( candidates, &cursor );
502 Debug( LDAP_DEBUG_TRACE,
503 LDAP_XSTRING(wt_search)
504 ": no paged results candidates\n",
506 send_paged_response( op, rs, &lastid, 0 );
508 rs->sr_err = LDAP_OTHER;
511 nentries = ps->ps_count;
512 if ( id == (ID)ps->ps_cookie )
513 id = wt_idl_next( candidates, &cursor );
517 for ( id = wt_idl_first( candidates, &cursor );
518 id != NOID ; id = wt_idl_next( candidates, &cursor ) )
524 /* check for abandon */
525 if ( op->o_abandon ) {
526 rs->sr_err = SLAPD_ABANDON;
527 send_ldap_result( op, rs );
531 /* mostly needed by internal searches,
532 * e.g. related to syncrepl, for whom
533 * abandon does not get set... */
534 if ( slapd_shutdown ) {
535 rs->sr_err = LDAP_UNAVAILABLE;
536 send_ldap_disconnect( op, rs );
540 /* check time limit */
541 if ( op->ors_tlimit != SLAP_NO_LIMIT
542 && slap_get_time() > stoptime )
544 rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
545 rs->sr_ref = rs->sr_v2ref;
546 send_ldap_result( op, rs );
547 rs->sr_err = LDAP_SUCCESS;
555 rc = wt_id2entry(op->o_bd, wc->session, id, &e);
556 /* TODO: error handling */
561 if ( is_entry_subentry( e ) ) {
562 if( op->oq_search.rs_scope != LDAP_SCOPE_BASE ) {
563 if(!get_subentries_visibility( op )) {
564 /* only subentries are visible */
568 } else if ( get_subentries( op ) &&
569 !get_subentries_visibility( op ))
571 /* only subentries are visible */
575 } else if ( get_subentries_visibility( op )) {
576 /* only subentries are visible */
581 switch( op->ors_scope ) {
582 case LDAP_SCOPE_BASE:
583 /* This is always true, yes? */
584 if ( id == base->e_id ) scopeok = 1;
586 case LDAP_SCOPE_ONELEVEL:
589 case LDAP_SCOPE_SUBTREE:
594 /* aliases were already dereferenced in candidate list */
595 if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
596 /* but if the search base is an alias, and we didn't
597 * deref it when finding, return it.
599 if ( is_entry_alias(e) &&
600 ((op->ors_deref & LDAP_DEREF_FINDING) ||
601 !bvmatch(&e->e_nname, &op->o_req_ndn)))
605 /* TODO: alias handling */
608 /* Not in scope, ignore it */
611 Debug( LDAP_DEBUG_TRACE,
612 LDAP_XSTRING(wt_search)
613 ": %ld scope not okay\n",
619 * if it's a referral, add it to the list of referrals. only do
620 * this for non-base searches, and don't check the filter
621 * explicitly here since it's only a candidate anyway.
623 if ( !manageDSAit && op->oq_search.rs_scope != LDAP_SCOPE_BASE
624 && is_entry_referral( e ) )
629 if ( !manageDSAit && is_entry_glue( e )) {
633 /* if it matches the filter and scope, send it */
634 rs->sr_err = test_filter( op, e, op->oq_search.rs_filter );
635 if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
636 /* check size limit */
637 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
643 rs->sr_attrs = op->oq_search.rs_attrs;
644 rs->sr_operational_attrs = NULL;
647 RS_ASSERT( e->e_private != NULL );
648 rs->sr_flags = REP_ENTRY_MUSTRELEASE;
649 rs->sr_err = LDAP_SUCCESS;
650 rs->sr_err = send_search_entry( op, rs );
655 switch ( rs->sr_err ) {
656 case LDAP_SUCCESS: /* entry sent ok */
659 /* TODO: error handling */
663 Debug( LDAP_DEBUG_TRACE,
664 LDAP_XSTRING(wt_search)
665 ": %ld does not match filter\n",
671 wt_entry_return( e );
678 rs->sr_ref = rs->sr_v2ref;
679 rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS : LDAP_REFERRAL;
680 rs->sr_rspoid = NULL;
681 if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
682 /* not implement yet */
683 /* send_paged_response( op, rs, NULL, 0 ); */
685 send_ldap_result( op, rs );
688 rs->sr_err = LDAP_SUCCESS;
693 wt_entry_return( base );
697 wt_entry_return( e );
705 * indent-tabs-mode: t