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