]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/search.c
Added bdb_attribute and bdb_group ACL support routines
[openldap] / servers / slapd / back-bdb / search.c
1 /* search.c - search operation */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11 #include <ac/string.h>
12
13 #include "back-bdb.h"
14 #include "idl.h"
15 #include "external.h"
16
17 static int base_candidate(
18         BackendDB       *be,
19         Entry   *e,
20         ID              *ids );
21 static int search_candidates(
22         BackendDB *be,
23         Entry *e,
24         Filter *filter,
25         int scope,
26         int deref,
27         int manageDSAit,
28         ID      *ids );
29
30 int
31 bdb_search(
32         BackendDB       *be,
33         Connection      *conn,
34         Operation       *op,
35         const char      *base,
36         const char      *nbase,
37         int             scope,
38         int             deref,
39         int             slimit,
40         int             tlimit,
41         Filter  *filter,
42         const char      *filterstr,
43         char    **attrs,
44         int             attrsonly )
45 {
46         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
47         int              abandon;
48         int             rc;
49         const char *text = NULL;
50         time_t          stoptime;
51         ID              id, cursor;
52         ID              candidates[BDB_IDL_UM_SIZE];
53         Entry           *e = NULL;
54         struct berval **v2refs = NULL;
55         Entry   *matched = NULL;
56         char    *realbase = NULL;
57         int             nentries = 0;
58         int             manageDSAit;
59
60         struct slap_limits_set *limit = NULL;
61         int isroot = 0;
62
63         Debug( LDAP_DEBUG_TRACE, "=> bdb_back_search\n",
64                 0, 0, 0);
65
66         manageDSAit = get_manageDSAit( op );
67
68 #ifdef BDB_ALIASES
69         /* get entry with reader lock */
70         if ( deref & LDAP_DEREF_FINDING ) {
71                 e = deref_dn_r( be, nbase, &err, &matched, &text );
72
73         } else
74 #endif
75         {
76                 rc = bdb_dn2entry( be, NULL, nbase, &e, &matched, 0 );
77         }
78
79         switch(rc) {
80         case DB_NOTFOUND:
81         case 0:
82                 break;
83         default:
84                 send_ldap_result( conn, op, rc=LDAP_OTHER,
85                         NULL, "internal error", NULL, NULL );
86                 return rc;
87         }
88
89         if ( e == NULL ) {
90                 char *matched_dn = NULL;
91                 struct berval **refs = NULL;
92
93                 if ( matched != NULL ) {
94                         struct berval **erefs;
95                         matched_dn = ch_strdup( matched->e_dn );
96
97                         erefs = is_entry_referral( matched )
98                                 ? get_entry_referrals( be, conn, op, matched,
99                                         base, scope )
100                                 : NULL;
101
102                         bdb_entry_return( be, matched );
103                         matched = NULL;
104
105                         if( erefs ) {
106                                 refs = referral_rewrite( erefs, matched_dn,
107                                         base, scope );
108                                 ber_bvecfree( erefs );
109                         }
110
111                 } else {
112                         refs = referral_rewrite( default_referral,
113                                 NULL, base, scope );
114                 }
115
116                 send_ldap_result( conn, op,     rc=LDAP_REFERRAL ,
117                         matched_dn, text, refs, NULL );
118
119                 ber_bvecfree( refs );
120                 free( matched_dn );
121
122                 return rc;
123         }
124
125         if (!manageDSAit && is_entry_referral( e ) ) {
126                 /* entry is a referral, don't allow add */
127                 char *matched_dn = ch_strdup( e->e_dn );
128                 struct berval **erefs = get_entry_referrals( be,
129                         conn, op, e, base, scope );
130                 struct berval **refs = NULL;
131
132                 bdb_entry_return( be, e );
133                 e = NULL;
134
135                 if( erefs ) {
136                         refs = referral_rewrite( erefs, matched_dn,
137                                 base, scope );
138                         ber_bvecfree( erefs );
139                 }
140
141                 Debug( LDAP_DEBUG_TRACE, "bdb_search: entry is referral\n",
142                         0, 0, 0 );
143
144                 send_ldap_result( conn, op, LDAP_REFERRAL,
145                         matched_dn, refs ? NULL : "bad referral object",
146                         refs, NULL );
147
148                 ber_bvecfree( refs );
149                 free( matched_dn );
150
151                 return 1;
152         }
153
154         /* if not root, get appropriate limits */
155         if ( be_isroot( be, op->o_ndn ) ) {
156                 isroot = 1;
157         } else {
158                 ( void ) get_limits( be, op->o_ndn, &limit );
159         }
160
161         /* The time/size limits come first because they require very little
162          * effort, so there's no chance the candidates are selected and then 
163          * the request is not honored only because of time/size constraints */
164
165         /* if no time limit requested, use soft limit (unless root!) */
166         if ( isroot ) {
167                 if ( tlimit == 0 ) {
168                         tlimit = -1;        /* allow root to set no limit */
169                 }
170
171                 if ( slimit == 0 ) {
172                         slimit = -1;
173                 }
174
175         } else {
176                 /* if no limit is required, use soft limit */
177                 if ( tlimit <= 0 ) {
178                         tlimit = limit->lms_t_soft;
179
180                 /* if requested limit higher than hard limit, abort */
181                 } else if ( tlimit > limit->lms_t_hard ) {
182                         /* no hard limit means use soft instead */
183                         if ( limit->lms_t_hard == 0 ) {
184                                 tlimit = limit->lms_t_soft;
185
186                         /* positive hard limit means abort */
187                         } else if ( limit->lms_t_hard > 0 ) {
188                                 send_search_result( conn, op, 
189                                                 LDAP_UNWILLING_TO_PERFORM,
190                                                 NULL, NULL, NULL, NULL, 0 );
191                                 rc = 0;
192                                 goto done;
193                         }
194                 
195                         /* negative hard limit means no limit */
196                 }
197                 
198                 /* if no limit is required, use soft limit */
199                 if ( slimit <= 0 ) {
200                         slimit = limit->lms_s_soft;
201
202                 /* if requested limit higher than hard limit, abort */
203                 } else if ( slimit > limit->lms_s_hard ) {
204                         /* no hard limit means use soft instead */
205                         if ( limit->lms_s_hard == 0 ) {
206                                 slimit = limit->lms_s_soft;
207
208                         /* positive hard limit means abort */
209                         } else if ( limit->lms_s_hard > 0 ) {
210                                 send_search_result( conn, op, 
211                                                 LDAP_UNWILLING_TO_PERFORM,
212                                                 NULL, NULL, NULL, NULL, 0 );
213                                 rc = 0; 
214                                 goto done;
215                         }
216                         
217                         /* negative hard limit means no limit */
218                 }
219         }
220
221         /* compute it anyway; root does not use it */
222         stoptime = op->o_time + tlimit;
223
224         /* select candidates */
225         if ( scope == LDAP_SCOPE_BASE ) {
226                 rc = base_candidate( be, e, candidates );
227
228         } else {
229                 rc = search_candidates( be, e, filter,
230                         scope, deref, manageDSAit, candidates );
231         }
232
233         /* need normalized dn below */
234         realbase = ch_strdup( e->e_ndn );
235
236         /* start cursor at base entry's id */
237         cursor = e->e_id;
238
239         bdb_entry_return( be, e );
240         e = NULL;
241
242         if ( candidates[0] == 0 ) {
243                 Debug( LDAP_DEBUG_TRACE, "bdb_search: no candidates\n",
244                         0, 0, 0 );
245
246                 send_search_result( conn, op,
247                         LDAP_SUCCESS,
248                         NULL, NULL, NULL, NULL, 0 );
249
250                 rc = 1;
251                 goto done;
252         }
253
254         /* if not root and candidates exceed to-be-checked entries, abort */
255         if ( !isroot && limit->lms_s_unchecked != -1 ) {
256                 if ( BDB_IDL_N(candidates) > limit->lms_s_unchecked ) {
257                         send_search_result( conn, op, 
258                                         LDAP_UNWILLING_TO_PERFORM,
259                                         NULL, NULL, NULL, NULL, 0 );
260                         rc = 1;
261                         goto done;
262                 }
263         }
264
265         for ( id = bdb_idl_first( candidates, &cursor );
266                 id != NOID;
267                 id = bdb_idl_next( candidates, &cursor ) )
268         {
269                 int             scopeok = 0;
270
271                 /* check for abandon */
272                 ldap_pvt_thread_mutex_lock( &op->o_abandonmutex );
273                 abandon = op->o_abandon;
274                 ldap_pvt_thread_mutex_unlock( &op->o_abandonmutex );
275
276                 if ( abandon ) {
277                         rc = 0;
278                         goto done;
279                 }
280
281                 /* check time limit */
282                 if ( tlimit != -1 && slap_get_time() > stoptime ) {
283                         send_search_result( conn, op, rc = LDAP_TIMELIMIT_EXCEEDED,
284                                 NULL, NULL, v2refs, NULL, nentries );
285                         goto done;
286                 }
287
288                 /* get the entry with reader lock */
289                 rc = bdb_id2entry( be, NULL, id, &e );
290
291                 if ( e == NULL ) {
292                         if( !BDB_IDL_IS_RANGE(candidates) ) {
293                                 /* only complain for non-range IDLs */
294                                 Debug( LDAP_DEBUG_TRACE,
295                                         "bdb_search: candidate %ld not found\n",
296                                         (long) id, 0, 0 );
297                         }
298
299                         goto loop_continue;
300                 }
301
302 #ifdef BDB_ALIASES
303                 if ( deref & LDAP_DEREF_SEARCHING && is_entry_alias( e ) ) {
304                         Entry *matched;
305                         int err;
306                         const char *text;
307                         
308                         e = deref_entry_r( be, e, &err, &matched, &text );
309
310                         if( e == NULL ) {
311                                 e = matched;
312                                 goto loop_continue;
313                         }
314
315                         if( e->e_id == id ) {
316                                 /* circular loop */
317                                 goto loop_continue;
318                         }
319
320                         /* need to skip alias which deref into scope */
321                         if( scope & LDAP_SCOPE_ONELEVEL ) {
322                                 char *pdn = dn_parent( NULL, e->e_ndn );
323                                 if ( pdn != NULL ) {
324                                         if( strcmp( pdn, realbase ) ) {
325                                                 free( pdn );
326                                                 goto loop_continue;
327                                         }
328                                         free(pdn);
329                                 }
330
331                         } else if ( dn_issuffix( e->e_ndn, realbase ) ) {
332                                 /* alias is within scope */
333                                 Debug( LDAP_DEBUG_TRACE,
334                                         "bdb_search: \"%s\" in subtree\n",
335                                         e->e_dn, 0, 0 );
336                                 goto loop_continue;
337                         }
338
339                         scopeok = 1;
340                 }
341 #endif
342
343                 /*
344                  * if it's a referral, add it to the list of referrals. only do
345                  * this for non-base searches, and don't check the filter
346                  * explicitly here since it's only a candidate anyway.
347                  */
348                 if ( !manageDSAit && scope != LDAP_SCOPE_BASE &&
349                         is_entry_referral( e ) )
350                 {
351                         struct berval **refs = get_entry_referrals(
352                                 be, conn, op, e, NULL, scope );
353
354                         send_search_reference( be, conn, op,
355                                 e, refs, NULL, &v2refs );
356
357                         ber_bvecfree( refs );
358
359                         goto loop_continue;
360                 }
361
362                 /* if it matches the filter and scope, send it */
363                 rc = test_filter( be, conn, op, e, filter );
364                 if ( rc == LDAP_COMPARE_TRUE ) {
365                         char    *dn;
366
367                         /* check scope */
368                         if ( !scopeok && scope == LDAP_SCOPE_ONELEVEL ) {
369                                 if ( (dn = dn_parent( be, e->e_ndn )) != NULL ) {
370                                         (void) dn_normalize( dn );
371                                         scopeok = (dn == realbase)
372                                                 ? 1
373                                                 : (strcmp( dn, realbase ) ? 0 : 1 );
374                                         free( dn );
375
376                                 } else {
377                                         scopeok = (realbase == NULL || *realbase == '\0');
378                                 }
379
380                         } else if ( !scopeok && scope == LDAP_SCOPE_SUBTREE ) {
381                                 dn = ch_strdup( e->e_ndn );
382                                 scopeok = dn_issuffix( dn, realbase );
383                                 free( dn );
384
385                         } else {
386                                 scopeok = 1;
387                         }
388
389                         if ( scopeok ) {
390                                 /* check size limit */
391                                 if ( --slimit == -1 ) {
392                                         bdb_entry_return( be, e );
393                                         e = NULL;
394                                         send_search_result( conn, op,
395                                                 rc = LDAP_SIZELIMIT_EXCEEDED, NULL, NULL,
396                                                 v2refs, NULL, nentries );
397                                         goto done;
398                                 }
399
400                                 if (e) {
401                                         int result = send_search_entry( be, conn, op,
402                                                 e, attrs, attrsonly, NULL);
403
404                                         switch (result) {
405                                         case 0:         /* entry sent ok */
406                                                 nentries++;
407                                                 break;
408                                         case 1:         /* entry not sent */
409                                                 break;
410                                         case -1:        /* connection closed */
411                                                 bdb_entry_return( be, e );
412                                                 e = NULL;
413                                                 rc = LDAP_OTHER;
414                                                 goto done;
415                                         }
416                                 }
417                         } else {
418                                 Debug( LDAP_DEBUG_TRACE,
419                                         "bdb_search: %ld scope not okay\n",
420                                         (long) id, 0, 0 );
421                         }
422                 } else {
423                         Debug( LDAP_DEBUG_TRACE,
424                                 "bdb_search: %ld does match filter\n",
425                                 (long) id, 0, 0 );
426                 }
427
428 loop_continue:
429                 if( e != NULL ) {
430                         /* free reader lock */
431                         bdb_entry_return( be, e );
432                 }
433
434                 ldap_pvt_thread_yield();
435         }
436         send_search_result( conn, op,
437                 v2refs == NULL ? LDAP_SUCCESS : LDAP_REFERRAL,
438                 NULL, NULL, v2refs, NULL, nentries );
439
440         rc = 0;
441
442 done:
443         ber_bvecfree( v2refs );
444         if( realbase ) ch_free( realbase );
445
446         return rc;
447 }
448
449
450 static int base_candidate(
451         BackendDB       *be,
452         Entry   *e,
453         ID              *ids )
454 {
455         Debug(LDAP_DEBUG_ARGS, "base_candidates: base: \"%s\" (0x%08lx)\n",
456                 e->e_dn, (long) e->e_id, 0);
457
458         ids[0] = 1;
459         ids[1] = e->e_id;
460         return 0;
461 }
462
463 static int search_candidates(
464         BackendDB *be,
465         Entry *e,
466         Filter *filter,
467         int scope,
468         int deref,
469         int manageDSAit,
470         ID      *ids )
471 {
472         int rc;
473         Filter          f, fand, rf, xf;
474         AttributeAssertion aa_ref;
475         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
476 #ifdef BDB_ALIASES
477         Filter  af;
478         AttributeAssertion aa_alias;
479 #endif
480
481         Debug(LDAP_DEBUG_TRACE,
482                 "search_candidates: base=\"%s\" (0x%08lx) scope=%d\n",
483                 e->e_dn, (long) e->e_id, scope );
484
485         xf.f_or = filter;
486         xf.f_choice = LDAP_FILTER_OR;
487         xf.f_next = NULL;
488
489         if( !manageDSAit ) {
490                 /* match referrals */
491                 static struct berval bv_ref = { sizeof("REFERRAL")-1, "REFERRAL" };
492                 rf.f_choice = LDAP_FILTER_EQUALITY;
493                 rf.f_ava = &aa_ref;
494                 rf.f_av_desc = slap_schema.si_ad_objectClass;
495                 rf.f_av_value = &bv_ref;
496                 rf.f_next = xf.f_or;
497                 xf.f_or = &rf;
498         }
499
500 #ifdef BDB_ALIASES
501         if( deref & LDAP_DEREF_SEARCHING ) {
502                 /* match aliases */
503                 static struct berval bv_alias = { sizeof("ALIAS")-1, "ALIAS" };
504                 af.f_choice = LDAP_FILTER_EQUALITY;
505                 af.f_ava = &aa_alias;
506                 af.f_av_desc = slap_schema.si_ad_objectClass;
507                 af.f_av_value = &bv_alias;
508                 af.f_next = xf.f_or;
509                 xf.f_or = &af;
510         }
511 #endif
512
513         f.f_next = NULL;
514         f.f_choice = LDAP_FILTER_AND;
515         f.f_and = &fand;
516         fand.f_choice = scope == LDAP_SCOPE_SUBTREE
517                 ? SLAPD_FILTER_DN_SUBTREE
518                 : SLAPD_FILTER_DN_ONE;
519         fand.f_dn = e->e_ndn;
520         fand.f_next = xf.f_or == filter ? filter : &xf ;
521
522
523 #ifdef BDB_FILTER_INDICES
524         rc = bdb_filter_candidates( be, &f, ids );
525 #else
526         BDB_IDL_ID( bdb, ids, e->e_id );
527         rc = 0;
528 #endif
529
530         Debug(LDAP_DEBUG_TRACE,
531                 "bdb_search_candidates: id=%ld first=%ld last=%ld\n",
532                 (long) ids[0],
533                 (long) BDB_IDL_FIRST(ids),
534                 (long) BDB_IDL_LAST(ids) );
535
536         return rc;
537 }