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