]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/search.c
e2e5b113c9bbf82dc9031ab4de6ed2164026e5a3
[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-2003 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 = { 0, NULL };
50         int             manageDSAit = get_manageDSAit( op );
51         int             cscope = LDAP_SCOPE_DEFAULT;
52
53         struct slap_limits_set *limit = NULL;
54         int isroot = 0;
55                 
56 #ifdef NEW_LOGGING
57         LDAP_LOG( BACK_LDBM, ENTRY, "ldbm_back_search: enter\n", 0, 0, 0 );
58 #else
59         Debug(LDAP_DEBUG_TRACE, "=> ldbm_back_search\n", 0, 0, 0);
60 #endif
61
62         /* grab giant lock for reading */
63         ldap_pvt_thread_rdwr_rlock(&li->li_giant_rwlock);
64
65         if ( op->o_req_ndn.bv_len == 0 ) {
66                 /* DIT root special case */
67                 e = (Entry *) &slap_entry_root;
68
69                 /* need normalized dn below */
70                 ber_dupbv( &realbase, &e->e_nname );
71
72                 candidates = search_candidates( op, e, op->oq_search.rs_filter,
73                                 op->oq_search.rs_scope, op->oq_search.rs_deref,
74                                 manageDSAit || get_domainScope(op) );
75
76                 goto searchit;
77                 
78         } else if ( op->oq_search.rs_deref & LDAP_DEREF_FINDING ) {
79                 /* deref dn and get entry with reader lock */
80                 e = deref_dn_r( op->o_bd, &op->o_req_ndn, &rs->sr_err, &matched, &rs->sr_text );
81
82                 if( rs->sr_err == LDAP_NO_SUCH_OBJECT ) rs->sr_err = LDAP_REFERRAL;
83
84         } else {
85                 /* get entry with reader lock */
86                 e = dn2entry_r( op->o_bd, &op->o_req_ndn, &matched );
87                 rs->sr_err = e != NULL ? LDAP_SUCCESS : LDAP_REFERRAL;
88                 rs->sr_text = NULL;
89         }
90
91         if ( e == NULL ) {
92                 struct berval matched_dn = { 0, NULL };
93
94                 if ( matched != NULL ) {
95                         BerVarray erefs;
96                         ber_dupbv( &matched_dn, &matched->e_name );
97
98                         erefs = is_entry_referral( matched )
99                                 ? get_entry_referrals( op, matched )
100                                 : NULL;
101
102                         cache_return_entry_r( &li->li_cache, matched );
103
104                         if( erefs ) {
105                                 rs->sr_ref = referral_rewrite( erefs, &matched_dn,
106                                         &op->o_req_dn, op->oq_search.rs_scope );
107
108                                 ber_bvarray_free( erefs );
109                         }
110
111                 } else {
112                         rs->sr_ref = referral_rewrite( default_referral,
113                                 NULL, &op->o_req_dn, op->oq_search.rs_scope );
114                 }
115
116                 ldap_pvt_thread_rdwr_runlock(&li->li_giant_rwlock);
117
118                 rs->sr_matched = matched_dn.bv_val;
119                 send_ldap_result( op, rs );
120
121                 ber_bvarray_free( rs->sr_ref );
122                 ber_memfree( matched_dn.bv_val );
123                 rs->sr_ref = NULL;
124                 rs->sr_matched = NULL;
125                 return LDAP_REFERRAL;
126         }
127
128         if (!manageDSAit && is_entry_referral( e ) ) {
129                 /* entry is a referral, don't allow add */
130                 struct berval matched_dn;
131                 BerVarray erefs;
132
133                 ber_dupbv( &matched_dn, &e->e_name );
134                 erefs = get_entry_referrals( op, e );
135                 rs->sr_ref = NULL;
136
137                 cache_return_entry_r( &li->li_cache, e );
138                 ldap_pvt_thread_rdwr_runlock(&li->li_giant_rwlock);
139
140 #ifdef NEW_LOGGING
141                 LDAP_LOG( BACK_LDBM, INFO,
142                         "ldbm_search: entry (%s) is a referral.\n",
143                         e->e_dn, 0, 0 );
144 #else
145                 Debug( LDAP_DEBUG_TRACE,
146                         "ldbm_search: entry is referral\n",
147                         0, 0, 0 );
148 #endif
149
150                 if( erefs ) {
151                         rs->sr_ref = referral_rewrite( erefs, &matched_dn,
152                                 &op->o_req_dn, op->oq_search.rs_scope );
153
154                         ber_bvarray_free( erefs );
155                 }
156
157                 rs->sr_matched = matched_dn.bv_val;
158                 if( rs->sr_ref ) {
159                         rs->sr_err = LDAP_REFERRAL;
160                         send_ldap_result( op, rs );
161                         ber_bvarray_free( rs->sr_ref );
162
163                 } else {
164                         send_ldap_error( op, rs, LDAP_OTHER,
165                         "bad referral object" );
166                 }
167
168                 ber_memfree( matched_dn.bv_val );
169                 rs->sr_ref = NULL;
170                 rs->sr_matched = NULL;
171                 return LDAP_OTHER;
172         }
173
174         if ( is_entry_alias( e ) ) {
175                 /* don't deref */
176                 op->oq_search.rs_deref = LDAP_DEREF_NEVER;
177         }
178
179         if ( op->oq_search.rs_scope == LDAP_SCOPE_BASE ) {
180                 cscope = LDAP_SCOPE_BASE;
181                 candidates = base_candidate( op->o_bd, e );
182
183         } else {
184                 cscope = ( op->oq_search.rs_scope != LDAP_SCOPE_SUBTREE )
185                         ? LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE;
186                 candidates = search_candidates( op, e, op->oq_search.rs_filter,
187                     op->oq_search.rs_scope, op->oq_search.rs_deref, manageDSAit );
188         }
189
190         /* need normalized dn below */
191         ber_dupbv( &realbase, &e->e_nname );
192
193         cache_return_entry_r( &li->li_cache, e );
194
195 searchit:
196         if ( candidates == NULL ) {
197                 /* no candidates */
198 #ifdef NEW_LOGGING
199                 LDAP_LOG( BACK_LDBM, INFO,
200                         "ldbm_search: no candidates\n" , 0, 0, 0);
201 #else
202                 Debug( LDAP_DEBUG_TRACE, "ldbm_search: no candidates\n",
203                         0, 0, 0 );
204 #endif
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 not root, get appropriate limits */
214         if ( be_isroot( op->o_bd, &op->o_ndn ) )
215         {
216                 /*
217                  * FIXME: I'd consider this dangerous if someone
218                  * uses isroot for anything but handling limits
219                  */
220                 isroot = 1;
221         } else {
222                 ( void ) get_limits( op->o_bd, &op->o_ndn, &limit );
223         }
224
225         /* if candidates exceed to-be-checked entries, abort */
226         if ( !isroot && limit->lms_s_unchecked != -1 ) {
227                 if ( ID_BLOCK_NIDS( candidates ) > (unsigned) limit->lms_s_unchecked ) {
228                         send_ldap_error( op, rs, LDAP_ADMINLIMIT_EXCEEDED,
229                                         NULL );
230                         rc = LDAP_SUCCESS;
231                         goto done;
232                 }
233         }
234         
235         /* if root an no specific limit is required, allow unlimited search */
236         if ( isroot ) {
237                 if ( op->oq_search.rs_tlimit == 0 ) {
238                         op->oq_search.rs_tlimit = -1;
239                 }
240
241                 if ( op->oq_search.rs_slimit == 0 ) {
242                         op->oq_search.rs_slimit = -1;
243                 }
244
245         } else {
246                 /* if no limit is required, use soft limit */
247                 if ( op->oq_search.rs_tlimit <= 0 ) {
248                         op->oq_search.rs_tlimit = limit->lms_t_soft;
249                 
250                 /* if requested limit higher than hard limit, abort */
251                 } else if ( op->oq_search.rs_tlimit > limit->lms_t_hard ) {
252                         /* no hard limit means use soft instead */
253                         if ( limit->lms_t_hard == 0
254                                         && limit->lms_t_soft > -1
255                                         && op->oq_search.rs_tlimit > limit->lms_t_soft ) {
256                                 op->oq_search.rs_tlimit = limit->lms_t_soft;
257                         
258                         /* positive hard limit means abort */
259                         } else if ( limit->lms_t_hard > 0 ) {
260                                 send_ldap_error( op, rs,
261                                                 LDAP_ADMINLIMIT_EXCEEDED,
262                                                 NULL );
263                                 rc = LDAP_SUCCESS; 
264                                 goto done;
265                         }
266
267                         /* negative hard limit means no limit */
268                 }
269
270                 /* if no limit is required, use soft limit */
271                 if ( op->oq_search.rs_slimit <= 0 ) {
272                         op->oq_search.rs_slimit = limit->lms_s_soft;
273
274                 /* if requested limit higher than hard limit, abort */
275                 } else if ( op->oq_search.rs_slimit > limit->lms_s_hard ) {
276                         /* no hard limit means use soft instead */
277                         if ( limit->lms_s_hard == 0
278                                         && limit->lms_s_soft > -1
279                                         && op->oq_search.rs_slimit > limit->lms_s_soft ) {
280                                 op->oq_search.rs_slimit = limit->lms_s_soft;
281
282                         /* positive hard limit means abort */
283                         } else if ( limit->lms_s_hard > 0 ) {
284                                 send_ldap_error( op, rs,
285                                                 LDAP_ADMINLIMIT_EXCEEDED,
286                                                 NULL );
287                                 rc = LDAP_SUCCESS;
288                                 goto done;
289                         }
290
291                         /* negative hard limit means no limit */
292                 }
293         }
294
295         /* compute it anyway; root does not use it */
296         stoptime = op->o_time + op->oq_search.rs_tlimit;
297         rs->sr_attrs = op->oq_search.rs_attrs;
298
299         for ( id = idl_firstid( candidates, &cursor ); id != NOID;
300             id = idl_nextid( candidates, &cursor ) )
301         {
302                 int scopeok = 0;
303                 int result = 0;
304
305                 /* check for abandon */
306                 if ( op->o_abandon ) {
307                         rc = LDAP_SUCCESS;
308                         goto done;
309                 }
310
311                 /* check time limit */
312                 if ( op->oq_search.rs_tlimit != -1 && slap_get_time() > stoptime ) {
313                         rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
314                         send_ldap_result( op, rs );
315                         rc = LDAP_SUCCESS;
316                         goto done;
317                 }
318
319                 /* get the entry with reader lock */
320                 e = id2entry_r( op->o_bd, id );
321
322                 if ( e == NULL ) {
323 #ifdef NEW_LOGGING
324                         LDAP_LOG( BACK_LDBM, INFO,
325                                 "ldbm_search: candidate %ld not found.\n", id, 0, 0 );
326 #else
327                         Debug( LDAP_DEBUG_TRACE,
328                                 "ldbm_search: candidate %ld not found\n",
329                                 id, 0, 0 );
330 #endif
331
332                         goto loop_continue;
333                 }
334
335                 rs->sr_entry = e;
336
337 #ifdef LDBM_SUBENTRIES
338         if ( is_entry_subentry( e ) ) {
339                 if( op->oq_search.rs_scope != LDAP_SCOPE_BASE ) {
340                         if(!get_subentries_visibility( op )) {
341                                 /* only subentries are visible */
342                                 goto loop_continue;
343                         }
344                 } else if ( get_subentries( op ) &&
345                         !get_subentries_visibility( op ))
346                 {
347                         /* only subentries are visible */
348                         goto loop_continue;
349                 }
350         } else if ( get_subentries_visibility( op )) {
351                 /* only subentries are visible */
352                 goto loop_continue;
353         }
354 #endif
355
356                 if ( op->oq_search.rs_deref & LDAP_DEREF_SEARCHING && is_entry_alias( e ) ) {
357                         Entry *matched;
358                         int err;
359                         const char *text;
360                         
361                         e = deref_entry_r( op->o_bd, e, &err, &matched, &text );
362
363                         if( e == NULL ) {
364                                 e = matched;
365                                 goto loop_continue;
366                         }
367
368                         if( e->e_id == id ) {
369                                 /* circular loop */
370                                 goto loop_continue;
371                         }
372
373                         /* need to skip alias which deref into scope */
374                         if( op->oq_search.rs_scope & LDAP_SCOPE_ONELEVEL ) {
375                                 struct berval pdn;
376                                 dnParent( &e->e_nname, &pdn );
377                                 if ( ber_bvcmp( &pdn, &realbase ) ) {
378                                         goto loop_continue;
379                                 }
380
381                         } else if ( dnIsSuffix( &e->e_nname, &realbase ) ) {
382                                 /* alias is within scope */
383 #ifdef NEW_LOGGING
384                                 LDAP_LOG( BACK_LDBM, DETAIL1,
385                                         "ldbm_search: alias \"%s\" in subtree\n", e->e_dn, 0, 0 );
386 #else
387                                 Debug( LDAP_DEBUG_TRACE,
388                                         "ldbm_search: alias \"%s\" in subtree\n",
389                                         e->e_dn, 0, 0 );
390 #endif
391
392                                 goto loop_continue;
393                         }
394
395                         rs->sr_entry = e;
396
397                         scopeok = 1;
398                 }
399
400                 /*
401                  * if it's a referral, add it to the list of referrals. only do
402                  * this for non-base searches, and don't check the filter
403                  * explicitly here since it's only a candidate anyway.
404                  */
405                 if ( !manageDSAit && op->oq_search.rs_scope != LDAP_SCOPE_BASE &&
406                         is_entry_referral( e ) )
407                 {
408                         struct berval   dn;
409
410                         /* check scope */
411                         if ( !scopeok && op->oq_search.rs_scope == LDAP_SCOPE_ONELEVEL ) {
412                                 if ( !be_issuffix( op->o_bd, &e->e_nname ) ) {
413                                         dnParent( &e->e_nname, &dn );
414                                         scopeok = dn_match( &dn, &realbase );
415                                 } else {
416                                         scopeok = (realbase.bv_len == 0);
417                                 }
418
419                         } else if ( !scopeok && op->oq_search.rs_scope == LDAP_SCOPE_SUBTREE ) {
420                                 scopeok = dnIsSuffix( &e->e_nname, &realbase );
421
422                         } else {
423                                 scopeok = 1;
424                         }
425
426                         if( scopeok ) {
427                                 BerVarray erefs = get_entry_referrals( op, e );
428                                 rs->sr_ref = referral_rewrite( erefs,
429                                         &e->e_name, NULL,
430                                         op->oq_search.rs_scope == LDAP_SCOPE_SUBTREE
431                                                 ? LDAP_SCOPE_SUBTREE
432                                                 : LDAP_SCOPE_BASE );
433
434                                 send_search_reference( op, rs );
435
436                                 ber_bvarray_free( rs->sr_ref );
437                                 rs->sr_ref = NULL;
438
439                         } else {
440 #ifdef NEW_LOGGING
441                                 LDAP_LOG( BACK_LDBM, DETAIL2,
442                                         "ldbm_search: candidate referral %ld scope not okay\n",
443                                         id, 0, 0 );
444 #else
445                                 Debug( LDAP_DEBUG_TRACE,
446                                         "ldbm_search: candidate referral %ld scope not okay\n",
447                                         id, 0, 0 );
448 #endif
449                         }
450
451                         goto loop_continue;
452                 }
453
454                 if ( !manageDSAit && is_entry_glue( e )) {
455                         goto loop_continue;
456                 }
457
458                 /* if it matches the filter and scope, send it */
459                 result = test_filter( op, e, op->oq_search.rs_filter );
460
461                 if ( result == LDAP_COMPARE_TRUE ) {
462                         struct berval   dn;
463
464                         /* check scope */
465                         if ( !scopeok && op->oq_search.rs_scope == LDAP_SCOPE_ONELEVEL ) {
466                                 if ( !be_issuffix( op->o_bd, &e->e_nname ) ) {
467                                         dnParent( &e->e_nname, &dn );
468                                         scopeok = dn_match( &dn, &realbase );
469                                 } else {
470                                         scopeok = (realbase.bv_len == 0);
471                                 }
472
473                         } else if ( !scopeok && op->oq_search.rs_scope == LDAP_SCOPE_SUBTREE ) {
474                                 scopeok = dnIsSuffix( &e->e_nname, &realbase );
475
476                         } else {
477                                 scopeok = 1;
478                         }
479
480                         if ( scopeok ) {
481                                 /* check size limit */
482                                 if ( --op->oq_search.rs_slimit == -1 ) {
483                                         cache_return_entry_r( &li->li_cache, e );
484                                         rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
485                                         send_ldap_result( op, rs );
486                                         rc = LDAP_SUCCESS;
487                                         goto done;
488                                 }
489
490                                 if (e) {
491
492                                         result = send_search_entry( op, rs );
493
494                                         switch (result) {
495                                         case 0:         /* entry sent ok */
496                                                 break;
497                                         case 1:         /* entry not sent */
498                                                 break;
499                                         case -1:        /* connection closed */
500                                                 cache_return_entry_r( &li->li_cache, e );
501                                                 rc = LDAP_SUCCESS;
502                                                 goto done;
503                                         }
504                                 }
505                         } else {
506 #ifdef NEW_LOGGING
507                                 LDAP_LOG( BACK_LDBM, DETAIL2,
508                                         "ldbm_search: candidate entry %ld scope not okay\n", 
509                                         id, 0, 0 );
510 #else
511                                 Debug( LDAP_DEBUG_TRACE,
512                                         "ldbm_search: candidate entry %ld scope not okay\n",
513                                         id, 0, 0 );
514 #endif
515                         }
516
517                 } else {
518 #ifdef NEW_LOGGING
519                         LDAP_LOG( BACK_LDBM, DETAIL2,
520                                 "ldbm_search: candidate entry %ld does not match filter\n", 
521                                 id, 0, 0 );
522 #else
523                         Debug( LDAP_DEBUG_TRACE,
524                                 "ldbm_search: candidate entry %ld does not match filter\n",
525                                 id, 0, 0 );
526 #endif
527                 }
528
529 loop_continue:
530                 if( e != NULL ) {
531                         /* free reader lock */
532                         cache_return_entry_r( &li->li_cache, e );
533                 }
534
535                 ldap_pvt_thread_yield();
536         }
537
538         rs->sr_err = rs->sr_v2ref ? LDAP_REFERRAL : LDAP_SUCCESS;
539         rs->sr_ref = rs->sr_v2ref;
540         send_ldap_result( op, rs );
541
542         rc = LDAP_SUCCESS;
543
544 done:
545         ldap_pvt_thread_rdwr_runlock(&li->li_giant_rwlock);
546
547         if( candidates != NULL )
548                 idl_free( candidates );
549
550         if( rs->sr_v2ref ) ber_bvarray_free( rs->sr_v2ref );
551         if( realbase.bv_val ) free( realbase.bv_val );
552
553         return rc;
554 }
555
556 static ID_BLOCK *
557 base_candidate(
558     Backend     *be,
559         Entry   *e )
560 {
561         ID_BLOCK                *idl;
562
563 #ifdef NEW_LOGGING
564         LDAP_LOG( BACK_LDBM, ENTRY, "base_candidate: base (%s)\n", e->e_dn, 0, 0 );
565 #else
566         Debug(LDAP_DEBUG_TRACE, "base_candidates: base: \"%s\"\n",
567                 e->e_dn, 0, 0);
568 #endif
569
570
571         idl = idl_alloc( 1 );
572         idl_insert( &idl, e->e_id, 1 );
573
574         return( idl );
575 }
576
577 static ID_BLOCK *
578 search_candidates(
579     Operation   *op,
580     Entry       *e,
581     Filter      *filter,
582     int         scope,
583         int             deref,
584         int             manageDSAit )
585 {
586         ID_BLOCK                *candidates;
587         Filter          f, fand, rf, af, xf;
588     AttributeAssertion aa_ref, aa_alias;
589         struct berval bv_ref = { sizeof("referral")-1, "referral" };
590         struct berval bv_alias = { sizeof("alias")-1, "alias" };
591 #ifdef LDBM_SUBENTRIES
592         Filter  sf;
593         AttributeAssertion aa_subentry;
594 #endif
595
596 #ifdef NEW_LOGGING
597         LDAP_LOG( BACK_LDBM, DETAIL1,
598                    "search_candidates: base (%s) scope %d deref %d\n",
599                    e->e_ndn, scope, deref );
600 #else
601         Debug(LDAP_DEBUG_TRACE,
602                 "search_candidates: base=\"%s\" s=%d d=%d\n",
603                 e->e_ndn, scope, deref );
604 #endif
605
606
607         xf.f_or = filter;
608         xf.f_choice = LDAP_FILTER_OR;
609         xf.f_next = NULL;
610
611         if( !manageDSAit ) {
612                 /* match referrals */
613                 rf.f_choice = LDAP_FILTER_EQUALITY;
614                 rf.f_ava = &aa_ref;
615                 rf.f_av_desc = slap_schema.si_ad_objectClass;
616                 rf.f_av_value = bv_ref;
617                 rf.f_next = xf.f_or;
618                 xf.f_or = &rf;
619         }
620
621         if( deref & LDAP_DEREF_SEARCHING ) {
622                 /* match aliases */
623                 af.f_choice = LDAP_FILTER_EQUALITY;
624                 af.f_ava = &aa_alias;
625                 af.f_av_desc = slap_schema.si_ad_objectClass;
626                 af.f_av_value = bv_alias;
627                 af.f_next = xf.f_or;
628                 xf.f_or = &af;
629         }
630
631         f.f_next = NULL;
632         f.f_choice = LDAP_FILTER_AND;
633         f.f_and = &fand;
634         fand.f_choice = scope == LDAP_SCOPE_SUBTREE
635                 ? SLAPD_FILTER_DN_SUBTREE
636                 : SLAPD_FILTER_DN_ONE;
637         fand.f_dn = &e->e_nname;
638         fand.f_next = xf.f_or == filter ? filter : &xf ;
639
640 #ifdef LDBM_SUBENTRIES
641         if ( get_subentries_visibility( op )) {
642                 struct berval bv_subentry = { sizeof("SUBENTRY")-1, "SUBENTRY" };
643                 sf.f_choice = LDAP_FILTER_EQUALITY;
644                 sf.f_ava = &aa_subentry;
645                 sf.f_av_desc = slap_schema.si_ad_objectClass;
646                 sf.f_av_value = bv_subentry;
647                 sf.f_next = fand.f_next;
648                 fand.f_next = &sf;
649         }
650 #endif
651
652         candidates = filter_candidates( op, &f );
653
654         return( candidates );
655 }