]> git.sur5r.net Git - openldap/blob - servers/slapd/search.c
8aca9c3723611b6181259a819d3a33adc8ca047f
[openldap] / servers / slapd / search.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6 /* Portions
7  * Copyright (c) 1995 Regents of the University of Michigan.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms are permitted
11  * provided that this notice is preserved and that due credit is given
12  * to the University of Michigan at Ann Arbor. The name of the University
13  * may not be used to endorse or promote products derived from this
14  * software without specific prior written permission. This software
15  * is provided ``as is'' without express or implied warranty.
16  */
17
18 #include "portable.h"
19
20 #include <stdio.h>
21
22 #include <ac/string.h>
23 #include <ac/socket.h>
24
25 #include "ldap_pvt.h"
26 #include "lutil.h"
27 #include "slap.h"
28
29 #ifdef LDAP_SLAPI
30 #include "slapi.h"
31 static char **anlist2charray( Operation *op, AttributeName *an );
32 static void initSearchPlugin( Operation *op, char **attrs, int managedsait );
33 static int doPreSearchPluginFNs( Operation *op );
34 static int doSearchRewriteFNs( Operation *op );
35 static void doPostSearchPluginFNs( Operation *op );
36 #endif /* LDAPI_SLAPI */
37
38 int
39 do_search(
40     Operation   *op,    /* info about the op to which we're responding */
41     SlapReply   *rs     /* all the response data we'll send */
42 ) {
43         struct berval base = { 0, NULL };
44         ber_len_t       siz, off, i;
45         int                     manageDSAit;
46 #ifdef LDAP_SLAPI
47         char            **attrs = NULL;
48         Operation       ch_op;
49 #endif
50
51 #ifdef NEW_LOGGING
52         LDAP_LOG( OPERATION, ENTRY, "do_search: conn %d\n", op->o_connid, 0, 0 );
53 #else
54         Debug( LDAP_DEBUG_TRACE, "do_search\n", 0, 0, 0 );
55 #endif
56
57         /*
58          * Parse the search request.  It looks like this:
59          *
60          *      SearchRequest := [APPLICATION 3] SEQUENCE {
61          *              baseObject      DistinguishedName,
62          *              scope           ENUMERATED {
63          *                      baseObject      (0),
64          *                      singleLevel     (1),
65          *                      wholeSubtree    (2)
66          *              },
67          *              derefAliases    ENUMERATED {
68          *                      neverDerefaliases       (0),
69          *                      derefInSearching        (1),
70          *                      derefFindingBaseObj     (2),
71          *                      alwaysDerefAliases      (3)
72          *              },
73          *              sizelimit       INTEGER (0 .. 65535),
74          *              timelimit       INTEGER (0 .. 65535),
75          *              attrsOnly       BOOLEAN,
76          *              filter          Filter,
77          *              attributes      SEQUENCE OF AttributeType
78          *      }
79          */
80
81         /* baseObject, scope, derefAliases, sizelimit, timelimit, attrsOnly */
82         if ( ber_scanf( op->o_ber, "{miiiib" /*}*/,
83                 &base, &op->ors_scope, &op->ors_deref, &op->ors_slimit,
84             &op->ors_tlimit, &op->ors_attrsonly ) == LBER_ERROR )
85         {
86                 send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
87                 rs->sr_err = SLAPD_DISCONNECT;
88                 goto return_results;
89         }
90
91         switch( op->ors_scope ) {
92         case LDAP_SCOPE_BASE:
93         case LDAP_SCOPE_ONELEVEL:
94         case LDAP_SCOPE_SUBTREE:
95                 break;
96         default:
97                 send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "invalid scope" );
98                 goto return_results;
99         }
100
101         switch( op->ors_deref ) {
102         case LDAP_DEREF_NEVER:
103         case LDAP_DEREF_FINDING:
104         case LDAP_DEREF_SEARCHING:
105         case LDAP_DEREF_ALWAYS:
106                 break;
107         default:
108                 send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR, "invalid deref" );
109                 goto return_results;
110         }
111
112         rs->sr_err = dnPrettyNormal( NULL, &base, &op->o_req_dn, &op->o_req_ndn, op->o_tmpmemctx );
113         if( rs->sr_err != LDAP_SUCCESS ) {
114 #ifdef NEW_LOGGING
115                 LDAP_LOG( OPERATION, ERR, 
116                         "do_search: conn %d  invalid dn (%s)\n",
117                         op->o_connid, base.bv_val, 0 );
118 #else
119                 Debug( LDAP_DEBUG_ANY,
120                         "do_search: invalid dn (%s)\n", base.bv_val, 0, 0 );
121 #endif
122                 send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" );
123                 goto return_results;
124         }
125
126 #ifdef NEW_LOGGING
127         LDAP_LOG( OPERATION, ARGS, "SRCH \"%s\" %d %d",
128                 base.bv_val, op->ors_scope, op->ors_deref );
129         LDAP_LOG( OPERATION, ARGS, "    %d %d %d\n",
130                 op->ors_slimit, op->ors_tlimit, op->ors_attrsonly);
131 #else
132         Debug( LDAP_DEBUG_ARGS, "SRCH \"%s\" %d %d",
133                 base.bv_val, op->ors_scope, op->ors_deref );
134         Debug( LDAP_DEBUG_ARGS, "    %d %d %d\n",
135                 op->ors_slimit, op->ors_tlimit, op->ors_attrsonly);
136 #endif
137
138         /* filter - returns a "normalized" version */
139 #ifdef LDAP_SLAPI
140         /*
141          * SLAPI computed search rewriter plugins can reset the search
142          * filter, and because they have no way to specify the use of
143          * the thread-local malloc replacement, we must always use
144          * ch_malloc() to allocate the filter when SLAPI is enabled.
145          */
146         ch_op = *op; /* struct copy */
147         ch_op.o_tmpmemctx = NULL;
148         ch_op.o_tmpmfuncs = &ch_mfuncs;
149         rs->sr_err = get_filter( &ch_op, op->o_ber, &op->ors_filter, &rs->sr_text );
150 #else
151         rs->sr_err = get_filter( op, op->o_ber, &op->ors_filter, &rs->sr_text );
152 #endif /* LDAP_SLAPI */
153         if( rs->sr_err != LDAP_SUCCESS ) {
154                 if( rs->sr_err == SLAPD_DISCONNECT ) {
155                         rs->sr_err = LDAP_PROTOCOL_ERROR;
156                         send_ldap_disconnect( op, rs );
157                 } else {
158                         send_ldap_result( op, rs );
159                 }
160                 goto return_results;
161         }
162         filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
163
164 #ifdef NEW_LOGGING
165         LDAP_LOG( OPERATION, ARGS, 
166                 "do_search: conn %d     filter: %s\n", 
167                 op->o_connid, op->ors_filterstr.bv_len ? op->ors_filterstr.bv_val : "empty", 0 );
168 #else
169         Debug( LDAP_DEBUG_ARGS, "    filter: %s\n",
170                 op->ors_filterstr.bv_len ? op->ors_filterstr.bv_val : "empty", 0, 0 );
171 #endif
172
173         /* attributes */
174         siz = sizeof(AttributeName);
175         off = 0;
176         if ( ber_scanf( op->o_ber, "{M}}", &op->ors_attrs, &siz, off ) == LBER_ERROR ) {
177                 send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding attrs error" );
178                 rs->sr_err = SLAPD_DISCONNECT;
179                 goto return_results;
180         }
181         for ( i=0; i<siz; i++ ) {
182                 const char *dummy;      /* ignore msgs from bv2ad */
183                 op->ors_attrs[i].an_desc = NULL;
184                 op->ors_attrs[i].an_oc = NULL;
185                 slap_bv2ad(&op->ors_attrs[i].an_name, &op->ors_attrs[i].an_desc, &dummy);
186         }
187
188         if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) {
189 #ifdef NEW_LOGGING
190                 LDAP_LOG( OPERATION, INFO, 
191                         "do_search: conn %d  get_ctrls failed (%d)\n",
192                         op->o_connid, rs->sr_err, 0 );
193 #else
194                 Debug( LDAP_DEBUG_ANY, "do_search: get_ctrls failed\n", 0, 0, 0 );
195 #endif
196
197                 goto return_results;
198         }
199
200 #ifdef NEW_LOGGING
201         LDAP_LOG( OPERATION, ARGS, 
202                 "do_search: conn %d     attrs:", op->o_connid, 0, 0 );
203 #else
204         Debug( LDAP_DEBUG_ARGS, "    attrs:", 0, 0, 0 );
205 #endif
206
207         if ( siz != 0 ) {
208                 for ( i = 0; i<siz; i++ ) {
209 #ifdef NEW_LOGGING
210                         LDAP_LOG( OPERATION, ARGS, 
211                                 "do_search: %s", op->ors_attrs[i].an_name.bv_val, 0, 0 );
212 #else
213                         Debug( LDAP_DEBUG_ARGS, " %s", op->ors_attrs[i].an_name.bv_val, 0, 0 );
214 #endif
215                 }
216         }
217
218 #ifdef NEW_LOGGING
219         LDAP_LOG( OPERATION, ARGS, "\n" , 0, 0, 0 );
220 #else
221         Debug( LDAP_DEBUG_ARGS, "\n", 0, 0, 0 );
222 #endif
223
224         if ( StatslogTest( LDAP_DEBUG_STATS ) ) {
225                 char abuf[BUFSIZ/2], *ptr = abuf;
226                 int len = 0, alen;
227
228                 Statslog( LDAP_DEBUG_STATS,
229                         "conn=%lu op=%lu SRCH base=\"%s\" scope=%d filter=\"%s\"\n",
230                         op->o_connid, op->o_opid, op->o_req_dn.bv_val, op->ors_scope, op->ors_filterstr.bv_val );
231
232                 for ( i = 0; i<siz; i++ ) {
233                         alen = op->ors_attrs[i].an_name.bv_len;
234                         if (alen >= sizeof(abuf)) {
235                                 alen = sizeof(abuf)-1;
236                         }
237                         if (len && (len + 1 + alen >= sizeof(abuf))) {
238                                 Statslog( LDAP_DEBUG_STATS, "conn=%lu op=%lu SRCH attr=%s\n",
239                                     op->o_connid, op->o_opid, abuf, 0, 0 );
240                                 len = 0;
241                                 ptr = abuf;
242                         }
243                         if (len) {
244                                 *ptr++ = ' ';
245                                 len++;
246                         }
247                         ptr = lutil_strncopy(ptr, op->ors_attrs[i].an_name.bv_val, alen);
248                         len += alen;
249                         *ptr = '\0';
250                 }
251                 if (len) {
252                         Statslog( LDAP_DEBUG_STATS, "conn=%lu op=%lu SRCH attr=%s\n",
253                                 op->o_connid, op->o_opid, abuf, 0, 0 );
254                 }
255         }
256
257         manageDSAit = get_manageDSAit( op );
258
259         if ( op->ors_scope == LDAP_SCOPE_BASE ) {
260                 Entry *entry = NULL;
261
262                 if ( op->o_req_ndn.bv_len == 0 ) {
263 #ifdef LDAP_CONNECTIONLESS
264                         /* Ignore LDAPv2 CLDAP Root DSE queries */
265                         if (op->o_protocol == LDAP_VERSION2 && op->o_conn->c_is_udp) {
266                                 goto return_results;
267                         }
268 #endif
269                         /* check restrictions */
270                         if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
271                                 send_ldap_result( op, rs );
272                                 goto return_results;
273                         }
274
275 #ifdef LDAP_SLAPI
276                         attrs = anlist2charray( op, op->ors_attrs );
277                         initSearchPlugin( op, attrs, manageDSAit );
278                         rs->sr_err = doPreSearchPluginFNs( op );
279                         if ( rs->sr_err == LDAP_SUCCESS ) {
280                                 doSearchRewriteFNs( op );
281 #endif /* LDAP_SLAPI */
282                         rs->sr_err = root_dse_info( op->o_conn, &entry, &rs->sr_text );
283 #ifdef LDAP_SLAPI
284                         }
285 #endif /* LDAP_SLAPI */
286
287                 } else if ( bvmatch( &op->o_req_ndn, &global_schemandn ) ) {
288                         /* check restrictions */
289                         if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
290                                 send_ldap_result( op, rs );
291                                 goto return_results;
292                         }
293
294 #ifdef LDAP_SLAPI
295                         attrs = anlist2charray( op, op->ors_attrs );
296                         initSearchPlugin( op, attrs, manageDSAit );
297                         rs->sr_err = doPreSearchPluginFNs( op );
298                         if ( rs->sr_err == LDAP_SUCCESS ) {
299                                 doSearchRewriteFNs( op );
300 #endif /* LDAP_SLAPI */
301                         rs->sr_err = schema_info( &entry, &rs->sr_text );
302 #ifdef LDAP_SLAPI
303                         }
304 #endif /* LDAP_SLAPI */
305                 }
306
307                 if( rs->sr_err != LDAP_SUCCESS ) {
308                         send_ldap_result( op, rs );
309 #ifdef LDAP_SLAPI
310                         doPostSearchPluginFNs( op );
311 #endif /* LDAP_SLAPI */
312                         goto return_results;
313
314                 } else if ( entry != NULL ) {
315                         rs->sr_err = test_filter( op, entry, op->ors_filter );
316
317                         if( rs->sr_err == LDAP_COMPARE_TRUE ) {
318                                 rs->sr_entry = entry;
319                                 rs->sr_attrs = op->ors_attrs;
320                                 send_search_entry( op, rs );
321                                 rs->sr_entry = NULL;
322                         }
323                         entry_free( entry );
324
325                         rs->sr_err = LDAP_SUCCESS;
326                         send_ldap_result( op, rs );
327 #ifdef LDAP_SLAPI
328                         doPostSearchPluginFNs( op );
329 #endif /* LDAP_SLAPI */
330                         goto return_results;
331                 }
332         }
333
334         if( !op->o_req_ndn.bv_len && default_search_nbase.bv_len ) {
335                 sl_free( op->o_req_dn.bv_val, op->o_tmpmemctx );
336                 sl_free( op->o_req_ndn.bv_val, op->o_tmpmemctx );
337
338                 ber_dupbv_x( &op->o_req_dn, &default_search_base, op->o_tmpmemctx );
339                 ber_dupbv_x( &op->o_req_ndn, &default_search_nbase, op->o_tmpmemctx );
340         }
341
342         /*
343          * We could be serving multiple database backends.  Select the
344          * appropriate one, or send a referral to our "referral server"
345          * if we don't hold it.
346          */
347         if ( (op->o_bd = select_backend( &op->o_req_ndn, manageDSAit, 1 )) == NULL ) {
348                 rs->sr_ref = referral_rewrite( default_referral,
349                         NULL, &op->o_req_dn, op->ors_scope );
350
351                 if (!rs->sr_ref) rs->sr_ref = default_referral;
352                 rs->sr_err = LDAP_REFERRAL;
353                 send_ldap_result( op, rs );
354
355                 if (rs->sr_ref != default_referral)
356                 ber_bvarray_free( rs->sr_ref );
357                 rs->sr_ref = NULL;
358                 goto return_results;
359         }
360
361         /* check restrictions */
362         if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
363                 send_ldap_result( op, rs );
364                 goto return_results;
365         }
366
367         /* check for referrals */
368         if( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
369                 goto return_results;
370         }
371
372 #ifdef LDAP_SLAPI
373         attrs = anlist2charray( op, op->ors_attrs );
374         initSearchPlugin( op, attrs, manageDSAit );
375         rs->sr_err = doPreSearchPluginFNs( op );
376         if ( rs->sr_err != LDAP_SUCCESS ) {
377                 goto return_results;
378         }
379
380         doSearchRewriteFNs( op );
381 #endif /* LDAP_SLAPI */
382
383         /* actually do the search and send the result(s) */
384         if ( op->o_bd->be_search ) {
385                 (op->o_bd->be_search)( op, rs );
386         } else {
387                 send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
388                         "operation not supported within namingContext" );
389         }
390
391 #ifdef LDAP_SLAPI
392         doPostSearchPluginFNs( op );
393 #endif /* LDAP_SLAPI */
394
395 return_results:;
396
397 #ifdef LDAP_CLIENT_UPDATE
398         if ( ( op->o_clientupdate_type & SLAP_LCUP_PERSIST ) )
399                 return rs->sr_err;
400 #endif
401 #if defined(LDAP_CLIENT_UPDATE) && defined(LDAP_SYNC)
402         else
403 #endif
404 #ifdef LDAP_SYNC
405         if ( ( op->o_sync_mode & SLAP_SYNC_PERSIST ) )
406                 return rs->sr_err;
407 #endif
408
409         if( op->o_req_dn.bv_val != NULL) sl_free( op->o_req_dn.bv_val, op->o_tmpmemctx );
410         if( op->o_req_ndn.bv_val != NULL) sl_free( op->o_req_ndn.bv_val, op->o_tmpmemctx );
411
412         if( op->ors_filterstr.bv_val != NULL) op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
413 #ifdef LDAP_SLAPI
414         if( op->ors_filter != NULL) filter_free( op->ors_filter );
415 #else
416         if( op->ors_filter != NULL) filter_free_x( op, op->ors_filter );
417 #endif
418         if( op->ors_attrs != NULL ) op->o_tmpfree( op->ors_attrs, op->o_tmpmemctx );
419 #ifdef LDAP_SLAPI
420         if( attrs != NULL) op->o_tmpfree( attrs, op->o_tmpmemctx );
421 #endif /* LDAP_SLAPI */
422
423         return rs->sr_err;
424 }
425
426 #ifdef LDAP_SLAPI
427
428 static char **anlist2charray( Operation *op, AttributeName *an )
429 {
430         char **attrs;
431         int i;
432
433         if ( an != NULL ) {
434                 for ( i = 0; an[i].an_name.bv_val != NULL; i++ )
435                         ;
436                 attrs = (char **)op->o_tmpalloc( (i + 1) * sizeof(char *), op->o_tmpmemctx );
437                 for ( i = 0; an[i].an_name.bv_val != NULL; i++ ) {
438                         attrs[i] = an[i].an_name.bv_val;
439                 }
440                 attrs[i] = NULL;
441         } else {
442                 attrs = NULL;
443         }
444
445         return attrs;
446 }
447
448 static void initSearchPlugin( Operation *op,
449         char **attrs, int managedsait )
450 {
451         slapi_x_pblock_set_operation( op->o_pb, op );
452         slapi_pblock_set( op->o_pb, SLAPI_SEARCH_TARGET, (void *)op->o_req_dn.bv_val );
453         slapi_pblock_set( op->o_pb, SLAPI_SEARCH_SCOPE, (void *)op->ors_scope );
454         slapi_pblock_set( op->o_pb, SLAPI_SEARCH_DEREF, (void *)op->ors_deref );
455         slapi_pblock_set( op->o_pb, SLAPI_SEARCH_SIZELIMIT, (void *)op->ors_slimit );
456         slapi_pblock_set( op->o_pb, SLAPI_SEARCH_TIMELIMIT, (void *)op->ors_tlimit );
457         slapi_pblock_set( op->o_pb, SLAPI_SEARCH_FILTER, (void *)op->ors_filter );
458         slapi_pblock_set( op->o_pb, SLAPI_SEARCH_STRFILTER, (void *)op->ors_filterstr.bv_val );
459         slapi_pblock_set( op->o_pb, SLAPI_SEARCH_ATTRS, (void *)attrs );
460         slapi_pblock_set( op->o_pb, SLAPI_SEARCH_ATTRSONLY, (void *)op->ors_attrsonly );
461         slapi_pblock_set( op->o_pb, SLAPI_MANAGEDSAIT, (void *)managedsait );
462 }
463
464 static int doPreSearchPluginFNs( Operation *op )
465 {
466         int rc;
467
468         rc = doPluginFNs( op->o_bd, SLAPI_PLUGIN_PRE_SEARCH_FN, op->o_pb );
469         if ( rc != 0 ) {
470                 /*
471                  * A preoperation plugin failure will abort the
472                  * entire operation.
473                  */
474 #ifdef NEW_LOGGING
475                 LDAP_LOG( OPERATION, INFO, "doPreSearchPluginFNs: search preoperation plugin "
476                                 "returned %d\n", rc, 0, 0 );
477 #else
478                 Debug(LDAP_DEBUG_TRACE, "doPreSearchPluginFNs: search preoperation plugin "
479                                 "returned %d.\n", rc, 0, 0);
480 #endif
481                 if ( slapi_pblock_get( op->o_pb, SLAPI_RESULT_CODE, (void *)&rc ) != 0)
482                         rc = LDAP_OTHER;
483         } else {
484                 rc = LDAP_SUCCESS;
485         }
486
487         return rc;
488 }
489
490 static int doSearchRewriteFNs( Operation *op )
491 {
492         if ( doPluginFNs( op->o_bd, SLAPI_PLUGIN_COMPUTE_SEARCH_REWRITER_FN, op->o_pb ) == 0 ) {
493                 /*
494                  * The plugin can set the SLAPI_SEARCH_FILTER.
495                  * SLAPI_SEARCH_STRFILER is not normative.
496                  */
497                 slapi_pblock_get( op->o_pb, SLAPI_SEARCH_FILTER, (void *)&op->ors_filter );
498                 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
499                 filter2bv_x( op, op->ors_filter, &op->ors_filterstr );
500 #ifdef NEW_LOGGING
501                 LDAP_LOG( OPERATION, ARGS, 
502                         "doSearchRewriteFNs: after compute_rewrite_search filter: %s\n", 
503                         op->ors_filterstr.bv_len ? op->ors_filterstr.bv_val : "empty", 0, 0 );
504 #else
505                 Debug( LDAP_DEBUG_ARGS, "    after compute_rewrite_search filter: %s\n",
506                         op->ors_filterstr.bv_len ? op->ors_filterstr.bv_val : "empty", 0, 0 );
507 #endif
508         }
509
510         return LDAP_SUCCESS;
511 }
512
513 static void doPostSearchPluginFNs( Operation *op )
514 {
515         if ( doPluginFNs( op->o_bd, SLAPI_PLUGIN_POST_SEARCH_FN, op->o_pb ) != 0 ) {
516 #ifdef NEW_LOGGING
517                 LDAP_LOG( OPERATION, INFO, "doPostSearchPluginFNs: search postoperation plugins "
518                                 "failed\n", 0, 0, 0 );
519 #else
520                 Debug(LDAP_DEBUG_TRACE, "doPostSearchPluginFNs: search postoperation plugins "
521                                 "failed.\n", 0, 0, 0);
522 #endif
523         }
524 }
525
526 void dummy(void)
527 {
528         /*
529          * XXX slapi_search_internal() was no getting pulled
530          * in; all manner of linker flags failed to link it.
531          * FIXME
532          */
533         slapi_search_internal( NULL, 0, NULL, NULL, NULL, 0 );
534 }
535 #endif /* LDAP_SLAPI */
536