]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/search.c
Sync with HEAD
[openldap] / servers / slapd / back-ldbm / search.c
1 /* search.c - ldbm backend search function */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 1998-2005 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
17 #include "portable.h"
18
19 #include <stdio.h>
20
21 #include <ac/string.h>
22 #include <ac/socket.h>
23
24 #include "slap.h"
25 #include "back-ldbm.h"
26 #include "proto-back-ldbm.h"
27
28 static ID_BLOCK *base_candidate(
29         Backend *be, Entry *e );
30
31 static ID_BLOCK *search_candidates(
32         Operation *op, Entry *e, Filter *filter,
33         int scope, int deref, int manageDSAit );
34
35
36 int
37 ldbm_back_search(
38     Operation   *op,
39     SlapReply   *rs )
40 {
41         struct ldbminfo *li = (struct ldbminfo *) op->o_bd->be_private;
42         int             rc, err;
43         const char *text = NULL;
44         time_t          stoptime;
45         ID_BLOCK                *candidates;
46         ID              id, cursor;
47         Entry           *e;
48         Entry   *matched = NULL;
49         struct berval   realbase = BER_BVNULL;
50         int             manageDSAit = get_manageDSAit( op );
51
52         Debug(LDAP_DEBUG_TRACE, "=> ldbm_back_search\n", 0, 0, 0);
53
54         /* grab giant lock for reading */
55         ldap_pvt_thread_rdwr_rlock(&li->li_giant_rwlock);
56
57         if ( op->o_req_ndn.bv_len == 0 ) {
58                 /* DIT root special case */
59                 e = (Entry *) &slap_entry_root;
60
61                 /* need normalized dn below */
62                 ber_dupbv( &realbase, &e->e_nname );
63
64                 candidates = search_candidates( op, e, op->ors_filter,
65                         op->ors_scope, op->ors_deref,
66                         manageDSAit || get_domainScope(op) );
67
68                 goto searchit;
69                 
70         } else if ( op->ors_deref & LDAP_DEREF_FINDING ) {
71                 /* deref dn and get entry with reader lock */
72                 e = deref_dn_r( op->o_bd, &op->o_req_ndn,
73                         &rs->sr_err, &matched, &rs->sr_text );
74
75                 if( rs->sr_err == LDAP_NO_SUCH_OBJECT ) rs->sr_err = LDAP_REFERRAL;
76
77         } else {
78                 /* get entry with reader lock */
79                 e = dn2entry_r( op->o_bd, &op->o_req_ndn, &matched );
80                 rs->sr_err = e != NULL ? LDAP_SUCCESS : LDAP_REFERRAL;
81                 rs->sr_text = NULL;
82         }
83
84         if ( e == NULL ) {
85                 struct berval matched_dn = BER_BVNULL;
86
87                 if ( matched != NULL ) {
88                         BerVarray erefs = NULL;
89                         
90                         if ( ! access_allowed( op, matched,
91                                                 slap_schema.si_ad_entry,
92                                                 NULL, ACL_DISCLOSE, NULL ) )
93                         {
94                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
95
96                         } else {
97                                 ber_dupbv( &matched_dn, &matched->e_name );
98
99                                 erefs = is_entry_referral( matched )
100                                         ? get_entry_referrals( op, matched )
101                                         : NULL;
102                         }
103
104                         cache_return_entry_r( &li->li_cache, matched );
105
106                         if ( erefs ) {
107                                 rs->sr_ref = referral_rewrite( erefs, &matched_dn,
108                                         &op->o_req_dn, op->ors_scope );
109
110                                 ber_bvarray_free( erefs );
111                         }
112
113                 } else {
114                         rs->sr_ref = referral_rewrite( default_referral,
115                                 NULL, &op->o_req_dn, op->ors_scope );
116                 }
117
118                 ldap_pvt_thread_rdwr_runlock(&li->li_giant_rwlock);
119
120                 rs->sr_matched = matched_dn.bv_val;
121                 send_ldap_result( op, rs );
122
123                 ber_bvarray_free( rs->sr_ref );
124                 ber_memfree( matched_dn.bv_val );
125                 rs->sr_ref = NULL;
126                 rs->sr_matched = NULL;
127                 return rs->sr_err;
128         }
129
130         if ( ! access_allowed( op, e, slap_schema.si_ad_entry,
131                                 NULL, ACL_DISCLOSE, NULL ) )
132         {
133                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
134
135                 cache_return_entry_r( &li->li_cache, e );
136                 ldap_pvt_thread_rdwr_runlock(&li->li_giant_rwlock);
137
138                 send_ldap_result( op, rs );
139                 return rs->sr_err;
140         }
141
142         if ( !manageDSAit && is_entry_referral( e ) ) {
143                 /* entry is a referral, don't allow add */
144                 struct berval   matched_dn = BER_BVNULL;
145                 BerVarray       erefs = NULL;
146
147                 rs->sr_ref = NULL;
148                 rs->sr_err = LDAP_OTHER;
149                 rs->sr_text = "bad referral object";
150
151                 ber_dupbv( &matched_dn, &e->e_name );
152                 erefs = get_entry_referrals( op, e );
153
154                 cache_return_entry_r( &li->li_cache, e );
155                 ldap_pvt_thread_rdwr_runlock(&li->li_giant_rwlock);
156
157                 Debug( LDAP_DEBUG_TRACE,
158                         "ldbm_search: entry is referral\n",
159                         0, 0, 0 );
160
161                 if ( erefs ) {
162                         rs->sr_ref = referral_rewrite( erefs, &matched_dn,
163                                 &op->o_req_dn, op->ors_scope );
164
165                         ber_bvarray_free( erefs );
166                         
167                         if ( rs->sr_ref ) {
168                                 rs->sr_err = LDAP_REFERRAL;
169                                 rs->sr_text = NULL;
170                         }
171                 }
172
173                 rs->sr_matched = matched_dn.bv_val;
174                 send_ldap_result( op, rs );
175                 ber_bvarray_free( rs->sr_ref );
176                 ber_memfree( matched_dn.bv_val );
177                 rs->sr_ref = NULL;
178                 rs->sr_matched = NULL;
179                 return rs->sr_err;
180         }
181
182         if ( is_entry_alias( e ) ) {
183                 /* don't deref */
184                 op->ors_deref = LDAP_DEREF_NEVER;
185         }
186
187         if ( op->ors_scope == LDAP_SCOPE_BASE ) {
188                 candidates = base_candidate( op->o_bd, e );
189
190         } else {
191                 candidates = search_candidates( op, e, op->ors_filter,
192                     op->ors_scope, op->ors_deref, manageDSAit );
193         }
194
195         /* need normalized dn below */
196         ber_dupbv( &realbase, &e->e_nname );
197
198         cache_return_entry_r( &li->li_cache, e );
199
200 searchit:
201         if ( candidates == NULL ) {
202                 /* no candidates */
203                 Debug( LDAP_DEBUG_TRACE, "ldbm_search: no candidates\n",
204                         0, 0, 0 );
205
206                 rs->sr_err = LDAP_SUCCESS;
207                 send_ldap_result( op, rs );
208
209                 rc = LDAP_SUCCESS;
210                 goto done;
211         }
212
213         /* if candidates exceed to-be-checked entries, abort */
214         if ( op->ors_limit      /* isroot == FALSE */
215                         && op->ors_limit->lms_s_unchecked != -1
216                         && ID_BLOCK_NIDS( candidates ) > (unsigned) op->ors_limit->lms_s_unchecked )
217         {
218                 send_ldap_error( op, rs, LDAP_ADMINLIMIT_EXCEEDED, NULL );
219                 rc = LDAP_SUCCESS;
220                 goto done;
221         }
222         
223         /* compute it anyway; root does not use it */
224         stoptime = op->o_time + op->ors_tlimit;
225         rs->sr_attrs = op->ors_attrs;
226
227         for ( id = idl_firstid( candidates, &cursor ); id != NOID;
228             id = idl_nextid( candidates, &cursor ) )
229         {
230                 int scopeok = 0;
231                 int result = 0;
232
233                 /* check for abandon */
234                 if ( op->o_abandon ) {
235                         rc = SLAPD_ABANDON;
236                         goto done;
237                 }
238
239                 /* check time limit */
240                 if ( op->ors_tlimit != SLAP_NO_LIMIT
241                                 && slap_get_time() > stoptime )
242                 {
243                         rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
244                         send_ldap_result( op, rs );
245                         rc = LDAP_SUCCESS;
246                         goto done;
247                 }
248
249                 /* get the entry with reader lock */
250                 e = id2entry_r( op->o_bd, id );
251
252                 if ( e == NULL ) {
253                         Debug( LDAP_DEBUG_TRACE,
254                                 "ldbm_search: candidate %ld not found\n",
255                                 id, 0, 0 );
256
257                         goto loop_continue;
258                 }
259
260                 rs->sr_entry = e;
261
262 #ifdef LDBM_SUBENTRIES
263                 if ( is_entry_subentry( e ) ) {
264                         if( op->ors_scope != LDAP_SCOPE_BASE ) {
265                                 if(!get_subentries_visibility( op )) {
266                                         /* only subentries are visible */
267                                         goto loop_continue;
268                                 }
269                         } else if ( get_subentries( op ) &&
270                                 !get_subentries_visibility( op ))
271                         {
272                                 /* only subentries are visible */
273                                 goto loop_continue;
274                         }
275                 } else if ( get_subentries_visibility( op )) {
276                         /* only subentries are visible */
277                         goto loop_continue;
278                 }
279 #endif
280
281                 if ( op->ors_deref & LDAP_DEREF_SEARCHING &&
282                         is_entry_alias( e ) )
283                 {
284                         Entry *matched;
285                         int err;
286                         const char *text;
287                         
288                         e = deref_entry_r( op->o_bd, e, &err, &matched, &text );
289
290                         if( e == NULL ) {
291                                 e = matched;
292                                 goto loop_continue;
293                         }
294
295                         if( e->e_id == id ) {
296                                 /* circular loop */
297                                 goto loop_continue;
298                         }
299
300                         /* need to skip alias which deref into scope */
301                         if( op->ors_scope == LDAP_SCOPE_ONELEVEL ) {
302                                 struct berval pdn;
303                                 dnParent( &e->e_nname, &pdn );
304                                 if ( ber_bvcmp( &pdn, &realbase ) ) {
305                                         goto loop_continue;
306                                 }
307
308                         } else if ( dnIsSuffix( &e->e_nname, &realbase ) ) {
309                                 /* alias is within scope */
310                                 Debug( LDAP_DEBUG_TRACE,
311                                         "ldbm_search: alias \"%s\" in subtree\n",
312                                         e->e_dn, 0, 0 );
313
314                                 goto loop_continue;
315                         }
316
317                         rs->sr_entry = e;
318
319                         scopeok = 1;
320                 }
321
322                 /*
323                  * If it's a referral, add it to the list of referrals.
324                  * Only do this for non-base searches, and don't check
325                  * the filter explicitly here since it's only a candidate
326                  * anyway.
327                  */
328                 if ( !manageDSAit && op->ors_scope != LDAP_SCOPE_BASE &&
329                         is_entry_referral( e ) )
330                 {
331                         struct berval   dn;
332
333                         /* check scope */
334                         if ( !scopeok && op->ors_scope == LDAP_SCOPE_ONELEVEL ) {
335                                 if ( !be_issuffix( op->o_bd, &e->e_nname ) ) {
336                                         dnParent( &e->e_nname, &dn );
337                                         scopeok = dn_match( &dn, &realbase );
338                                 } else {
339                                         scopeok = (realbase.bv_len == 0);
340                                 }
341
342                         } else if ( !scopeok
343                                 && op->ors_scope == LDAP_SCOPE_SUBTREE )
344                         {
345                                 scopeok = dnIsSuffix( &e->e_nname, &realbase );
346
347 #ifdef LDAP_SCOPE_SUBORDINATE
348                         } else if ( !scopeok
349                                 && op->ors_scope == LDAP_SCOPE_SUBORDINATE )
350                         {
351                                 scopeok = !dn_match( &e->e_nname, &realbase )
352                                         && dnIsSuffix( &e->e_nname, &realbase );
353 #endif
354
355                         } else {
356                                 scopeok = 1;
357                         }
358
359                         if( scopeok ) {
360                                 BerVarray erefs = get_entry_referrals( op, e );
361                                 rs->sr_ref = referral_rewrite( erefs,
362                                         &e->e_name, NULL,
363                                         op->ors_scope == LDAP_SCOPE_ONELEVEL
364                                                 ? LDAP_SCOPE_BASE
365                                                 : LDAP_SCOPE_SUBTREE );
366
367                                 send_search_reference( op, rs );
368
369                                 ber_bvarray_free( rs->sr_ref );
370                                 rs->sr_ref = NULL;
371
372                         } else {
373                                 Debug( LDAP_DEBUG_TRACE,
374                                         "ldbm_search: candidate referral %ld scope not okay\n",
375                                         id, 0, 0 );
376                         }
377
378                         goto loop_continue;
379                 }
380
381                 if ( !manageDSAit && is_entry_glue( e )) {
382                         goto loop_continue;
383                 }
384
385                 /* if it matches the filter and scope, send it */
386                 result = test_filter( op, e, op->ors_filter );
387
388                 if ( result == LDAP_COMPARE_TRUE ) {
389                         struct berval   dn;
390
391                         /* check scope */
392                         if ( !scopeok && op->ors_scope == LDAP_SCOPE_ONELEVEL ) {
393                                 if ( !be_issuffix( op->o_bd, &e->e_nname ) ) {
394                                         dnParent( &e->e_nname, &dn );
395                                         scopeok = dn_match( &dn, &realbase );
396                                 } else {
397                                         scopeok = (realbase.bv_len == 0);
398                                 }
399
400                         } else if ( !scopeok &&
401                                 op->ors_scope == LDAP_SCOPE_SUBTREE )
402                         {
403                                 scopeok = dnIsSuffix( &e->e_nname, &realbase );
404
405 #ifdef LDAP_SCOPE_SUBORDINATE
406                         } else if ( !scopeok &&
407                                 op->ors_scope == LDAP_SCOPE_SUBORDINATE )
408                         {
409                                 scopeok = !dn_match( &e->e_nname, &realbase )
410                                         && dnIsSuffix( &e->e_nname, &realbase );
411 #endif
412
413                         } else {
414                                 scopeok = 1;
415                         }
416
417                         if ( scopeok ) {
418                                 /* check size limit */
419                                 if ( --op->ors_slimit == -1 ) {
420                                         cache_return_entry_r( &li->li_cache, e );
421                                         rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
422                                         rs->sr_entry = NULL;
423                                         send_ldap_result( op, rs );
424                                         rc = LDAP_SUCCESS;
425                                         goto done;
426                                 }
427
428                                 if (e) {
429                                         rs->sr_flags = 0;
430                                         result = send_search_entry( op, rs );
431
432                                         switch (result) {
433                                         case 0:         /* entry sent ok */
434                                                 break;
435                                         case 1:         /* entry not sent */
436                                                 break;
437                                         case -1:        /* connection closed */
438                                                 cache_return_entry_r( &li->li_cache, e );
439                                                 rc = LDAP_SUCCESS;
440                                                 goto done;
441                                         }
442                                 }
443
444                         } else {
445                                 Debug( LDAP_DEBUG_TRACE,
446                                         "ldbm_search: candidate entry %ld scope not okay\n",
447                                         id, 0, 0 );
448                         }
449
450                 } else {
451                         Debug( LDAP_DEBUG_TRACE,
452                                 "ldbm_search: candidate entry %ld does not match filter\n",
453                                 id, 0, 0 );
454                 }
455
456 loop_continue:
457                 if( e != NULL ) {
458                         /* free reader lock */
459                         cache_return_entry_r( &li->li_cache, e );
460                 }
461
462                 ldap_pvt_thread_yield();
463         }
464
465         rs->sr_err = rs->sr_v2ref ? LDAP_REFERRAL : LDAP_SUCCESS;
466         rs->sr_ref = rs->sr_v2ref;
467         send_ldap_result( op, rs );
468
469         rc = LDAP_SUCCESS;
470
471 done:
472         ldap_pvt_thread_rdwr_runlock(&li->li_giant_rwlock);
473
474         if( candidates != NULL )
475                 idl_free( candidates );
476
477         if( rs->sr_v2ref ) ber_bvarray_free( rs->sr_v2ref );
478         if( realbase.bv_val ) free( realbase.bv_val );
479
480         return rc;
481 }
482
483 static ID_BLOCK *
484 base_candidate(
485     Backend     *be,
486         Entry   *e )
487 {
488         ID_BLOCK                *idl;
489
490         Debug(LDAP_DEBUG_TRACE, "base_candidates: base: \"%s\"\n",
491                 e->e_dn, 0, 0);
492
493
494         idl = idl_alloc( 1 );
495         idl_insert( &idl, e->e_id, 1 );
496
497         return( idl );
498 }
499
500 static ID_BLOCK *
501 search_candidates(
502     Operation   *op,
503     Entry       *e,
504     Filter      *filter,
505     int         scope,
506         int             deref,
507         int             manageDSAit )
508 {
509         ID_BLOCK                *candidates;
510         Filter          f, fand, rf, af, xf;
511     AttributeAssertion aa_ref, aa_alias;
512         struct berval bv_ref = { sizeof("referral")-1, "referral" };
513         struct berval bv_alias = { sizeof("alias")-1, "alias" };
514 #ifdef LDBM_SUBENTRIES
515         Filter  sf;
516         AttributeAssertion aa_subentry;
517 #endif
518
519         Debug(LDAP_DEBUG_TRACE,
520                 "search_candidates: base=\"%s\" s=%d d=%d\n",
521                 e->e_ndn, scope, deref );
522
523
524         xf.f_or = filter;
525         xf.f_choice = LDAP_FILTER_OR;
526         xf.f_next = NULL;
527
528         if( !manageDSAit ) {
529                 /* match referrals */
530                 rf.f_choice = LDAP_FILTER_EQUALITY;
531                 rf.f_ava = &aa_ref;
532                 rf.f_av_desc = slap_schema.si_ad_objectClass;
533                 rf.f_av_value = bv_ref;
534                 rf.f_next = xf.f_or;
535                 xf.f_or = &rf;
536         }
537
538         if( deref & LDAP_DEREF_SEARCHING ) {
539                 /* match aliases */
540                 af.f_choice = LDAP_FILTER_EQUALITY;
541                 af.f_ava = &aa_alias;
542                 af.f_av_desc = slap_schema.si_ad_objectClass;
543                 af.f_av_value = bv_alias;
544                 af.f_next = xf.f_or;
545                 xf.f_or = &af;
546         }
547
548         f.f_next = NULL;
549         f.f_choice = LDAP_FILTER_AND;
550         f.f_and = &fand;
551         fand.f_choice = scope == LDAP_SCOPE_ONELEVEL
552                 ? SLAPD_FILTER_DN_ONE
553                 : SLAPD_FILTER_DN_SUBTREE;
554         fand.f_dn = &e->e_nname;
555         fand.f_next = xf.f_or == filter ? filter : &xf ;
556
557 #ifdef LDBM_SUBENTRIES
558         if ( get_subentries_visibility( op )) {
559                 struct berval bv_subentry = { sizeof("SUBENTRY")-1, "SUBENTRY" };
560                 sf.f_choice = LDAP_FILTER_EQUALITY;
561                 sf.f_ava = &aa_subentry;
562                 sf.f_av_desc = slap_schema.si_ad_objectClass;
563                 sf.f_av_value = bv_subentry;
564                 sf.f_next = fand.f_next;
565                 fand.f_next = &sf;
566         }
567 #endif
568
569         candidates = filter_candidates( op, &f );
570         return( candidates );
571 }