]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/search.c
don't lookup rootdn if the password is incorrect (ITS#4004)
[openldap] / servers / slapd / back-meta / search.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2005 The OpenLDAP Foundation.
5  * Portions Copyright 2001-2003 Pierangelo Masarati.
6  * Portions Copyright 1999-2003 Howard Chu.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by the Howard Chu for inclusion
19  * in OpenLDAP Software and subsequently enhanced by Pierangelo
20  * Masarati.
21  */
22
23 #include "portable.h"
24
25 #include <stdio.h>
26
27 #include <ac/socket.h>
28 #include <ac/string.h>
29 #include <ac/time.h>
30
31 #include "slap.h"
32 #include "../back-ldap/back-ldap.h"
33 #include "back-meta.h"
34 #undef ldap_debug       /* silence a warning in ldap-int.h */
35 #include "ldap_log.h"
36 #include "../../../libraries/libldap/ldap-int.h"
37
38 static int
39 meta_send_entry(
40         Operation       *op,
41         SlapReply       *rs,
42         metaconn_t      *mc,
43         int             i,
44         LDAPMessage     *e );
45
46 static int
47 meta_back_search_start(
48         Operation               *op,
49         SlapReply               *rs,
50         dncookie                *dc,
51         metasingleconn_t        *msc,
52         int                     candidate,
53         SlapReply               *candidates
54 )
55 {
56         metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
57         struct berval   realbase = op->o_req_dn;
58         int             realscope = op->ors_scope;
59         ber_len_t       suffixlen = 0;
60         struct berval   mbase = BER_BVNULL; 
61         struct berval   mfilter = BER_BVNULL;
62         char            **mapped_attrs = NULL;
63         int             rc;
64
65         /* should we check return values? */
66         if ( op->ors_deref != -1 ) {
67                 ldap_set_option( msc->msc_ld, LDAP_OPT_DEREF,
68                                 ( void * )&op->ors_deref);
69         }
70         if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
71                 ldap_set_option( msc->msc_ld, LDAP_OPT_TIMELIMIT,
72                                 ( void * )&op->ors_tlimit);
73         }
74         if ( op->ors_slimit != SLAP_NO_LIMIT ) {
75                 ldap_set_option( msc->msc_ld, LDAP_OPT_SIZELIMIT,
76                                 ( void * )&op->ors_slimit);
77         }
78
79         dc->target = &mi->mi_targets[ candidate ];
80
81         /*
82          * modifies the base according to the scope, if required
83          */
84         suffixlen = mi->mi_targets[ candidate ].mt_nsuffix.bv_len;
85         if ( suffixlen > op->o_req_ndn.bv_len ) {
86                 switch ( op->ors_scope ) {
87                 case LDAP_SCOPE_SUBTREE:
88                         /*
89                          * make the target suffix the new base
90                          * FIXME: this is very forgiving, because
91                          * "illegal" searchBases may be turned
92                          * into the suffix of the target; however,
93                          * the requested searchBase already passed
94                          * thru the candidate analyzer...
95                          */
96                         if ( dnIsSuffix( &mi->mi_targets[ candidate ].mt_nsuffix,
97                                         &op->o_req_ndn ) )
98                         {
99                                 realbase = mi->mi_targets[ candidate ].mt_nsuffix;
100 #ifdef LDAP_SCOPE_SUBORDINATE
101                                 if ( mi->mi_targets[ candidate ].mt_scope == LDAP_SCOPE_SUBORDINATE ) {
102                                         realscope = LDAP_SCOPE_SUBORDINATE;
103                                 }
104 #endif /* LDAP_SCOPE_SUBORDINATE */
105
106                         } else {
107                                 /*
108                                  * this target is no longer candidate
109                                  */
110                                 return 0;
111                         }
112                         break;
113
114 #ifdef LDAP_SCOPE_SUBORDINATE
115                 case LDAP_SCOPE_SUBORDINATE:
116 #endif /* LDAP_SCOPE_SUBORDINATE */
117                 case LDAP_SCOPE_ONELEVEL:
118                 {
119                         struct berval   rdn = mi->mi_targets[ candidate ].mt_nsuffix;
120                         rdn.bv_len -= op->o_req_ndn.bv_len + STRLENOF( "," );
121                         if ( dnIsOneLevelRDN( &rdn )
122                                         && dnIsSuffix( &mi->mi_targets[ candidate ].mt_nsuffix, &op->o_req_ndn ) )
123                         {
124                                 /*
125                                  * if there is exactly one level,
126                                  * make the target suffix the new
127                                  * base, and make scope "base"
128                                  */
129                                 realbase = mi->mi_targets[ candidate ].mt_nsuffix;
130 #ifdef LDAP_SCOPE_SUBORDINATE
131                                 if ( op->ors_scope == LDAP_SCOPE_SUBORDINATE ) {
132                                         if ( mi->mi_targets[ candidate ].mt_scope == LDAP_SCOPE_SUBORDINATE ) {
133                                                 realscope = LDAP_SCOPE_SUBORDINATE;
134                                         } else {
135                                                 realscope = LDAP_SCOPE_SUBTREE;
136                                         }
137                                 } else
138 #endif /* LDAP_SCOPE_SUBORDINATE */
139                                 {
140                                         realscope = LDAP_SCOPE_BASE;
141                                 }
142                                 break;
143                         } /* else continue with the next case */
144                 }
145
146                 case LDAP_SCOPE_BASE:
147                         /*
148                          * this target is no longer candidate
149                          */
150                         return 0;
151                 }
152         }
153
154         /*
155          * Rewrite the search base, if required
156          */
157         dc->ctx = "searchBase";
158         switch ( ldap_back_dn_massage( dc, &realbase, &mbase ) ) {
159         default:
160                 break;
161
162         case REWRITE_REGEXEC_UNWILLING:
163                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
164                 rs->sr_text = "Operation not allowed";
165                 send_ldap_result( op, rs );
166                 return -1;
167
168         case REWRITE_REGEXEC_ERR:
169
170                 /*
171                  * this target is no longer candidate
172                  */
173                 return 0;
174         }
175
176         /*
177          * Maps filter
178          */
179         rc = ldap_back_filter_map_rewrite( dc, op->ors_filter,
180                         &mfilter, BACKLDAP_MAP );
181         switch ( rc ) {
182         case LDAP_SUCCESS:
183                 break;
184
185         case LDAP_COMPARE_FALSE:
186         default:
187                 /*
188                  * this target is no longer candidate
189                  */
190                 rc = 0;
191                 goto done;
192         }
193
194         /*
195          * Maps required attributes
196          */
197         rc = ldap_back_map_attrs( &mi->mi_targets[ candidate ].mt_rwmap.rwm_at,
198                         op->ors_attrs, BACKLDAP_MAP, &mapped_attrs );
199         if ( rc != LDAP_SUCCESS ) {
200                 /*
201                  * this target is no longer candidate
202                  */
203                 rc = 0;
204                 goto done;
205         }
206
207         /*
208          * Starts the search
209          */
210         rc = ldap_search_ext( msc->msc_ld,
211                         mbase.bv_val, realscope, mfilter.bv_val,
212                         mapped_attrs, op->ors_attrsonly,
213                         op->o_ctrls, NULL, NULL, op->ors_slimit,
214                         &candidates[ candidate ].sr_msgid ); 
215         if ( rc == LDAP_SUCCESS ) {
216                 rc = 1;
217
218         } else {
219                 candidates[ candidate ].sr_msgid = -1;
220                 rc = 0;
221         }
222
223 done:;
224         if ( mapped_attrs ) {
225                 free( mapped_attrs );
226         }
227         if ( mfilter.bv_val != op->ors_filterstr.bv_val ) {
228                 free( mfilter.bv_val );
229         }
230         if ( mbase.bv_val != realbase.bv_val ) {
231                 free( mbase.bv_val );
232         }
233
234         return rc;
235 }
236
237 int
238 meta_back_search( Operation *op, SlapReply *rs )
239 {
240         metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
241         metaconn_t      *mc;
242         struct timeval  tv = { 0, 0 };
243         time_t          stoptime;
244         LDAPMessage     *res = NULL, *e;
245         int             rc = 0, sres = LDAP_SUCCESS;
246         char            *matched = NULL;
247         int             i, last = 0, ncandidates = 0,
248                         initial_candidates = 0, candidate_match = 0;
249         dncookie        dc;
250         int             is_ok = 0;
251         void            *savepriv;
252         SlapReply       *candidates = meta_back_candidates_get( op );
253
254         /*
255          * controls are set in ldap_back_dobind()
256          * 
257          * FIXME: in case of values return filter, we might want
258          * to map attrs and maybe rewrite value
259          */
260         mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_SENDERR );
261         if ( !mc || !meta_back_dobind( op, rs, mc, LDAP_BACK_SENDERR ) ) {
262                 return rs->sr_err;
263         }
264
265         dc.conn = op->o_conn;
266         dc.rs = rs;
267
268         /*
269          * Inits searches
270          */
271         for ( i = 0; i < mi->mi_ntargets; i++ ) {
272                 metasingleconn_t        *msc = &mc->mc_conns[ i ];
273
274                 candidates[ i ].sr_msgid = -1;
275                 candidates[ i ].sr_matched = NULL;
276                 candidates[ i ].sr_text = NULL;
277                 candidates[ i ].sr_ref = NULL;
278                 candidates[ i ].sr_ctrls = NULL;
279
280                 if ( candidates[ i ].sr_tag != META_CANDIDATE
281                         || candidates[ i ].sr_err != LDAP_SUCCESS )
282                 {
283                         continue;
284                 }
285
286                 switch ( meta_back_search_start( op, rs, &dc, msc, i, candidates ) )
287                 {
288                 case 0:
289                         break;
290
291                 case 1:
292                         ++ncandidates;
293                         break;
294
295                 case -1:
296                         rc = -1;
297                         goto finish;
298                 }
299         }
300
301         initial_candidates = ncandidates;
302
303 #if 0
304         {
305                 char    cnd[BUFSIZ];
306                 int     i;
307
308                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
309                         if ( candidates[ i ].sr_tag == META_CANDIDATE ) {
310                                 cnd[ i ] = '*';
311                         } else {
312                                 cnd[ i ] = ' ';
313                         }
314                 }
315                 cnd[ i ] = '\0';
316
317                 Debug( LDAP_DEBUG_ANY, "%s meta_back_search: ncandidates=%d "
318                         "cnd=\"%s\"\n", op->o_log_prefix, ncandidates, cnd );
319         }
320 #endif
321
322         if ( initial_candidates == 0 ) {
323                 /* NOTE: here we are not sending any matchedDN;
324                  * this is intended, because if the back-meta
325                  * is serving this search request, but no valid
326                  * candidate could be looked up, it means that
327                  * there is a hole in the mapping of the targets
328                  * and thus no knowledge of any remote superior
329                  * is available */
330                 Debug( LDAP_DEBUG_ANY, "%s meta_back_search: "
331                         "base=\"%s\" scope=%d: "
332                         "no candidate could be selected\n",
333                         op->o_log_prefix, op->o_req_dn.bv_val,
334                         op->ors_scope );
335
336                 /* FIXME: we're sending the first error we encounter;
337                  * maybe we should pick the worst... */
338                 rc = LDAP_NO_SUCH_OBJECT;
339                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
340                         if ( candidates[ i ].sr_tag == META_CANDIDATE
341                                 && candidates[ i ].sr_err != LDAP_SUCCESS )
342                         {
343                                 rc = candidates[ i ].sr_err;
344                                 break;
345                         }
346                 }
347
348                 send_ldap_error( op, rs, rc, NULL );
349
350                 goto finish;
351         }
352
353         /* We pull apart the ber result, stuff it into a slapd entry, and
354          * let send_search_entry stuff it back into ber format. Slow & ugly,
355          * but this is necessary for version matching, and for ACL processing.
356          */
357
358         if ( op->ors_tlimit != SLAP_NO_LIMIT ) {
359                 stoptime = op->o_time + op->ors_tlimit;
360                 tv.tv_sec = 0;
361         }
362
363         /*
364          * In case there are no candidates, no cycle takes place...
365          *
366          * FIXME: we might use a queue, to better balance the load 
367          * among the candidates
368          */
369         for ( rc = 0; ncandidates > 0; ) {
370                 int     gotit = 0, doabandon = 0;
371
372                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
373                         metasingleconn_t        *msc = &mc->mc_conns[ i ];
374
375                         if ( candidates[ i ].sr_msgid == -1 ) {
376                                 continue;
377                         }
378
379                         /* check for abandon */
380                         if ( op->o_abandon ) {
381                                 break;
382                         }
383                         
384                         /*
385                          * FIXME: handle time limit as well?
386                          * Note that target servers are likely 
387                          * to handle it, so at some time we'll
388                          * get a LDAP_TIMELIMIT_EXCEEDED from
389                          * one of them ...
390                          */
391                         rc = ldap_result( msc->msc_ld, candidates[ i ].sr_msgid,
392                                         0, &tv, &res );
393
394                         if ( rc == 0 ) {
395                                 /* FIXME: res should not need to be freed */
396                                 assert( res == NULL );
397
398                                 /* check time limit */
399                                 if ( op->ors_tlimit != SLAP_NO_LIMIT
400                                                 && slap_get_time() > stoptime )
401                                 {
402                                         doabandon = 1;
403                                         rc = rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
404                                         savepriv = op->o_private;
405                                         op->o_private = (void *)i;
406                                         send_ldap_result( op, rs );
407                                         op->o_private = savepriv;
408                                         goto finish;
409                                 }
410
411                                 continue;
412
413                         } else if ( rc == -1 ) {
414 really_bad:;
415                                 /* something REALLY bad happened! */
416                                 ( void )meta_clear_unused_candidates( op, -1 );
417                                 rs->sr_err = LDAP_OTHER;
418                                 savepriv = op->o_private;
419                                 op->o_private = (void *)i;
420                                 send_ldap_result( op, rs );
421                                 op->o_private = savepriv;
422                                 
423                                 /* anything else needs be done? */
424
425                                 /* FIXME: res should not need to be freed */
426                                 assert( res == NULL );
427
428                                 goto finish;
429
430                         } else if ( rc == LDAP_RES_SEARCH_ENTRY ) {
431                                 if ( --op->ors_slimit == -1 ) {
432                                         ldap_msgfree( res );
433                                         res = NULL;
434
435                                         rs->sr_err = LDAP_SIZELIMIT_EXCEEDED;
436                                         savepriv = op->o_private;
437                                         op->o_private = (void *)i;
438                                         send_ldap_result( op, rs );
439                                         op->o_private = savepriv;
440                                         goto finish;
441                                 }
442
443                                 is_ok++;
444
445                                 e = ldap_first_entry( msc->msc_ld, res );
446                                 savepriv = op->o_private;
447                                 op->o_private = (void *)i;
448                                 meta_send_entry( op, rs, mc, i, e );
449                                 op->o_private = savepriv;
450
451                                 ldap_msgfree( res );
452                                 res = NULL;
453
454                                 gotit = 1;
455
456 #if 0
457                                 /*
458                                  * If scope is BASE, we need to jump out
459                                  * as soon as one entry is found; if
460                                  * the target pool is properly crafted,
461                                  * this should correspond to the sole
462                                  * entry that has the base DN
463                                  */
464                                 /* FIXME: this defeats the purpose of
465                                  * doing a search with scope == base and
466                                  * sizelimit = 1 to determine if a
467                                  * candidate is actually unique */
468                                 if ( op->ors_scope == LDAP_SCOPE_BASE
469                                                 && rs->sr_nentries > 0 )
470                                 {
471                                         doabandon = 1;
472                                         ncandidates = 0;
473                                         sres = LDAP_SUCCESS;
474                                         break;
475                                 }
476 #endif
477
478                         } else if ( rc == LDAP_RES_SEARCH_REFERENCE ) {
479                                 char            **references = NULL;
480                                 int             cnt;
481
482                                 is_ok++;
483
484                                 rc = ldap_parse_reference( msc->msc_ld, res,
485                                                 &references, &rs->sr_ctrls, 1 );
486                                 res = NULL;
487
488                                 if ( rc != LDAP_SUCCESS ) {
489                                         continue;
490                                 }
491
492                                 if ( references == NULL ) {
493                                         continue;
494                                 }
495
496 #ifdef ENABLE_REWRITE
497                                 dc.ctx = "referralDN";
498 #else /* ! ENABLE_REWRITE */
499                                 dc.tofrom = 0;
500                                 dc.normalized = 0;
501 #endif /* ! ENABLE_REWRITE */
502
503                                 /* FIXME: merge all and return at the end */
504
505                                 for ( cnt = 0; references[ cnt ]; cnt++ )
506                                         ;
507
508                                 rs->sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 );
509
510                                 for ( cnt = 0; references[ cnt ]; cnt++ ) {
511                                         ber_str2bv( references[ cnt ], 0, 1, &rs->sr_ref[ cnt ] );
512                                 }
513                                 BER_BVZERO( &rs->sr_ref[ cnt ] );
514
515                                 ( void )ldap_back_referral_result_rewrite( &dc, rs->sr_ref );
516
517                                 if ( rs->sr_ref != NULL && !BER_BVISNULL( &rs->sr_ref[ 0 ] ) ) {
518                                         /* ignore return value by now */
519                                         savepriv = op->o_private;
520                                         op->o_private = (void *)i;
521                                         ( void )send_search_reference( op, rs );
522                                         op->o_private = savepriv;
523
524                                         ber_bvarray_free( rs->sr_ref );
525                                         rs->sr_ref = NULL;
526                                 }
527
528                                 /* cleanup */
529                                 if ( references ) {
530                                         ldap_value_free( references );
531                                 }
532
533                                 if ( rs->sr_ctrls ) {
534                                         ldap_controls_free( rs->sr_ctrls );
535                                         rs->sr_ctrls = NULL;
536                                 }
537
538                         } else if ( rc == LDAP_RES_SEARCH_RESULT ) {
539                                 char            buf[ SLAP_TEXT_BUFLEN ];
540                                 char            **references = NULL;
541
542                                 if ( ldap_parse_result( msc->msc_ld,
543                                                         res,
544                                                         &candidates[ i ].sr_err,
545                                                         (char **)&candidates[ i ].sr_matched,
546                                                         NULL /* (char **)&candidates[ i ].sr_text */ ,
547                                                         &references,
548                                                         &candidates[ i ].sr_ctrls, 1 ) != LDAP_SUCCESS )
549                                 {
550                                         res = NULL;
551                                         ldap_get_option( msc->msc_ld,
552                                                         LDAP_OPT_ERROR_NUMBER,
553                                                         &rs->sr_err );
554                                         sres = slap_map_api2result( rs );
555                                         goto really_bad;
556                                 }
557
558                                 rs->sr_err = candidates[ i ].sr_err;
559                                 sres = slap_map_api2result( rs );
560                                 res = NULL;
561
562                                 /* massage matchedDN if need be */
563                                 if ( candidates[ i ].sr_matched != NULL ) {
564                                         if ( candidates[ i ].sr_matched[ 0 ] == '\0' ) {
565                                                 ldap_memfree( (char *)candidates[ i ].sr_matched );
566                                                 candidates[ i ].sr_matched = NULL;
567
568                                         } else {
569                                                 struct berval   match, mmatch;
570
571                                                 ber_str2bv( candidates[ i ].sr_matched,
572                                                         0, 0, &match );
573
574                                                 dc.ctx = "matchedDN";
575                                                 dc.target = &mi->mi_targets[ i ];
576
577                                                 if ( !ldap_back_dn_massage( &dc, &match, &mmatch ) ) {
578                                                         if ( mmatch.bv_val == match.bv_val ) {
579                                                                 candidates[ i ].sr_matched = ch_strdup( mmatch.bv_val );
580
581                                                         } else {
582                                                                 candidates[ i ].sr_matched = mmatch.bv_val;
583                                                         }
584
585                                                         candidate_match++;
586                                                 } 
587                                                 ldap_memfree( match.bv_val );
588                                         }
589                                 }
590
591                                 /* just get rid of the error message, if any */
592                                 if ( candidates[ i ].sr_text && candidates[ i ].sr_text[ 0 ] == '\0' )
593                                 {
594                                         ldap_memfree( (char *)candidates[ i ].sr_text );
595                                         candidates[ i ].sr_text = NULL;
596                                 }
597
598                                 /* add references to array */
599                                 if ( references ) {
600                                         BerVarray       sr_ref;
601                                         int             cnt;
602
603                                         for ( cnt = 0; references[ cnt ]; cnt++ )
604                                                 ;
605
606                                         sr_ref = ch_calloc( sizeof( struct berval ), cnt + 1 );
607
608                                         for ( cnt = 0; references[ cnt ]; cnt++ ) {
609                                                 ber_str2bv( references[ cnt ], 0, 1, &sr_ref[ cnt ] );
610                                         }
611                                         BER_BVZERO( &sr_ref[ cnt ] );
612
613                                         ( void )ldap_back_referral_result_rewrite( &dc, sr_ref );
614                                 
615                                         /* cleanup */
616                                         ldap_value_free( references );
617
618                                         if ( rs->sr_v2ref == NULL ) {
619                                                 rs->sr_v2ref = sr_ref;
620
621                                         } else {
622                                                 for ( cnt = 0; !BER_BVISNULL( &sr_ref[ cnt ] ); cnt++ ) {
623                                                         ber_bvarray_add( &rs->sr_v2ref, &sr_ref[ cnt ] );
624                                                 }
625                                                 ber_memfree( sr_ref );
626                                         }
627                                 }
628
629                                 rs->sr_err = candidates[ i ].sr_err;
630                                 sres = slap_map_api2result( rs );
631
632                                 snprintf( buf, sizeof( buf ),
633                                         "%s meta_back_search[%d] "
634                                         "match=\"%s\" err=%d\n",
635                                         op->o_log_prefix, i,
636                                         candidates[ i ].sr_matched ? candidates[ i ].sr_matched : "",
637                                         candidates[ i ].sr_err );
638                                 Debug( LDAP_DEBUG_ANY, "%s", buf, 0, 0 );
639
640                                 switch ( sres ) {
641                                 case LDAP_NO_SUCH_OBJECT:
642                                         /* is_ok is touched any time a valid
643                                          * (even intermediate) result is
644                                          * returned; as a consequence, if
645                                          * a candidate returns noSuchObject
646                                          * it is ignored and the candidate
647                                          * is simply demoted. */
648                                         if ( is_ok ) {
649                                                 sres = LDAP_SUCCESS;
650                                         }
651                                         break;
652
653                                 case LDAP_SUCCESS:
654                                 case LDAP_REFERRAL:
655                                         is_ok++;
656                                         break;
657
658                                 default:
659                                         if ( META_BACK_ONERR_STOP( mi ) ) {
660                                                 savepriv = op->o_private;
661                                                 op->o_private = (void *)i;
662                                                 send_ldap_result( op, rs );
663                                                 op->o_private = savepriv;
664                                                 goto finish;
665                                         }
666                                         break;
667                                 }
668
669                                 last = i;
670                                 rc = 0;
671
672                                 /*
673                                  * When no candidates are left,
674                                  * the outer cycle finishes
675                                  */
676                                 candidates[ i ].sr_msgid = -1;
677                                 --ncandidates;
678
679                         } else {
680                                 assert( 0 );
681                                 goto really_bad;
682                         }
683                 }
684
685                 /* check for abandon */
686                 if ( op->o_abandon || doabandon ) {
687                         for ( i = 0; i < mi->mi_ntargets; i++ ) {
688                                 metasingleconn_t        *msc = &mc->mc_conns[ i ];
689
690                                 if ( candidates[ i ].sr_msgid != -1 ) {
691                                         ldap_abandon_ext( msc->msc_ld,
692                                                 candidates[ i ].sr_msgid,
693                                                 NULL, NULL );
694                                         candidates[ i ].sr_msgid = -1;
695                                 }
696                         }
697
698                         if ( op->o_abandon ) {
699                                 rc = SLAPD_ABANDON;
700                                 goto finish;
701                         }
702                 }
703
704                 if ( gotit == 0 ) {
705                         tv.tv_sec = 0;
706                         tv.tv_usec = 100000;    /* 0.1 s */
707                         ldap_pvt_thread_yield();
708
709                 } else {
710                         tv.tv_sec = 0;
711                         tv.tv_usec = 0;
712                 }
713         }
714
715         if ( rc == -1 ) {
716                 /*
717                  * FIXME: need a better strategy to handle errors
718                  */
719                 rc = meta_back_op_result( mc, op, rs, META_TARGET_NONE );
720                 goto finish;
721         }
722
723         /*
724          * Rewrite the matched portion of the search base, if required
725          * 
726          * FIXME: only the last one gets caught!
727          */
728         savepriv = op->o_private;
729         op->o_private = (void *)mi->mi_ntargets;
730         if ( candidate_match > 0 ) {
731                 struct berval   pmatched = BER_BVNULL;
732
733                 /* we use the first one */
734                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
735                         if ( candidates[ i ].sr_tag == META_CANDIDATE
736                                         && candidates[ i ].sr_matched )
737                         {
738                                 struct berval   bv, pbv;
739                                 int             rc;
740
741                                 ber_str2bv( candidates[ i ].sr_matched, 0, 0, &bv );
742                                 rc = dnPretty( NULL, &bv, &pbv, op->o_tmpmemctx );
743
744                                 if ( rc == LDAP_SUCCESS ) {
745
746                                         /* NOTE: if they all are superiors
747                                          * of the baseDN, the shorter is also 
748                                          * superior of the longer... */
749                                         if ( pbv.bv_len > pmatched.bv_len ) {
750                                                 if ( !BER_BVISNULL( &pmatched ) ) {
751                                                         op->o_tmpfree( pmatched.bv_val, op->o_tmpmemctx );
752                                                 }
753                                                 pmatched = pbv;
754                                                 op->o_private = (void *)i;
755
756                                         } else {
757                                                 op->o_tmpfree( pbv.bv_val, op->o_tmpmemctx );
758                                         }
759                                 }
760
761                                 if ( candidates[ i ].sr_matched != NULL ) {
762                                         free( (char *)candidates[ i ].sr_matched );
763                                         candidates[ i ].sr_matched = NULL;
764                                 }
765                         }
766                 }
767
768                 if ( !BER_BVISNULL( &pmatched ) ) {
769                         matched = pmatched.bv_val;
770                 }
771
772         } else if ( sres == LDAP_NO_SUCH_OBJECT ) {
773                 matched = op->o_bd->be_suffix[ 0 ].bv_val;
774         }
775
776 #if 0
777         {
778                 char    buf[BUFSIZ];
779                 char    cnd[BUFSIZ];
780                 int     i;
781
782                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
783                         if ( candidates[ i ].sr_tag == META_CANDIDATE ) {
784                                 cnd[ i ] = '*';
785                         } else {
786                                 cnd[ i ] = ' ';
787                         }
788                 }
789                 cnd[ i ] = '\0';
790
791                 snprintf( buf, sizeof( buf ), "%s meta_back_search: is_scope=%d is_ok=%d cnd=\"%s\"\n",
792                         op->o_log_prefix, initial_candidates, is_ok, cnd );
793
794                 Debug( LDAP_DEBUG_ANY, "%s", buf, 0, 0 );
795         }
796 #endif
797
798         /*
799          * In case we returned at least one entry, we return LDAP_SUCCESS
800          * otherwise, the latter error code we got
801          *
802          * FIXME: we should handle error codes and return the more 
803          * important/reasonable
804          */
805
806         if ( sres == LDAP_SUCCESS && rs->sr_v2ref ) {
807                 sres = LDAP_REFERRAL;
808         }
809         rs->sr_err = sres;
810         rs->sr_matched = matched;
811         rs->sr_ref = ( sres == LDAP_REFERRAL ? rs->sr_v2ref : NULL );
812         send_ldap_result( op, rs );
813         op->o_private = savepriv;
814         rs->sr_matched = NULL;
815         rs->sr_ref = NULL;
816
817 finish:;
818         if ( matched && matched != op->o_bd->be_suffix[ 0 ].bv_val ) {
819                 op->o_tmpfree( matched, op->o_tmpmemctx );
820         }
821
822         if ( rs->sr_v2ref ) {
823                 ber_bvarray_free( rs->sr_v2ref );
824         }
825
826         for ( i = 0; i < mi->mi_ntargets; i++ ) {
827                 if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
828                         continue;
829                 }
830
831                 if ( candidates[ i ].sr_matched ) {
832                         free( (char *)candidates[ i ].sr_matched );
833                         candidates[ i ].sr_matched = NULL;
834                 }
835
836                 if ( candidates[ i ].sr_text ) {
837                         ldap_memfree( (char *)candidates[ i ].sr_text );
838                         candidates[ i ].sr_text = NULL;
839                 }
840
841                 if ( candidates[ i ].sr_ref ) {
842                         ber_bvarray_free( candidates[ i ].sr_ref );
843                         candidates[ i ].sr_ref = NULL;
844                 }
845
846                 if ( candidates[ i ].sr_ctrls ) {
847                         ldap_controls_free( candidates[ i ].sr_ctrls );
848                         candidates[ i ].sr_ctrls = NULL;
849                 }
850         }
851
852         meta_back_release_conn( op, mc );
853
854         return rs->sr_err;
855 }
856
857 static int
858 meta_send_entry(
859         Operation       *op,
860         SlapReply       *rs,
861         metaconn_t      *mc,
862         int             target,
863         LDAPMessage     *e )
864 {
865         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
866         struct berval           a, mapped;
867         Entry                   ent = { 0 };
868         BerElement              ber = *e->lm_ber;
869         Attribute               *attr, **attrp;
870         struct berval           *bv, bdn;
871         const char              *text;
872         dncookie                dc;
873
874         if ( ber_scanf( &ber, "{m{", &bdn ) == LBER_ERROR ) {
875                 return LDAP_DECODING_ERROR;
876         }
877
878         /*
879          * Rewrite the dn of the result, if needed
880          */
881         dc.target = &mi->mi_targets[ target ];
882         dc.conn = op->o_conn;
883         dc.rs = rs;
884         dc.ctx = "searchResult";
885
886         rs->sr_err = ldap_back_dn_massage( &dc, &bdn, &ent.e_name );
887         if ( rs->sr_err != LDAP_SUCCESS) {
888                 return rs->sr_err;
889         }
890
891         /*
892          * Note: this may fail if the target host(s) schema differs
893          * from the one known to the meta, and a DN with unknown
894          * attributes is returned.
895          * 
896          * FIXME: should we log anything, or delegate to dnNormalize?
897          */
898         if ( dnNormalize( 0, NULL, NULL, &ent.e_name, &ent.e_nname,
899                 op->o_tmpmemctx ) != LDAP_SUCCESS )
900         {
901                 return LDAP_INVALID_DN_SYNTAX;
902         }
903
904         /*
905          * cache dn
906          */
907         if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED ) {
908                 ( void )meta_dncache_update_entry( &mi->mi_cache,
909                                 &ent.e_nname, target );
910         }
911
912         attrp = &ent.e_attrs;
913
914         dc.ctx = "searchAttrDN";
915         while ( ber_scanf( &ber, "{m", &a ) != LBER_ERROR ) {
916                 int                             last = 0;
917                 slap_syntax_validate_func       *validate;
918                 slap_syntax_transform_func      *pretty;
919
920                 ldap_back_map( &mi->mi_targets[ target ].mt_rwmap.rwm_at, 
921                                 &a, &mapped, BACKLDAP_REMAP );
922                 if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0' ) {
923                         continue;
924                 }
925                 attr = ( Attribute * )ch_malloc( sizeof( Attribute ) );
926                 if ( attr == NULL ) {
927                         continue;
928                 }
929                 attr->a_flags = 0;
930                 attr->a_next = 0;
931                 attr->a_desc = NULL;
932                 if ( slap_bv2ad( &mapped, &attr->a_desc, &text )
933                                 != LDAP_SUCCESS) {
934                         if ( slap_bv2undef_ad( &mapped, &attr->a_desc, &text,
935                                 SLAP_AD_PROXIED ) != LDAP_SUCCESS )
936                         {
937                                 char    buf[ SLAP_TEXT_BUFLEN ];
938
939                                 snprintf( buf, sizeof( buf ),
940                                         "%s meta_send_entry(\"%s\"): "
941                                         "slap_bv2undef_ad(%s): %s\n",
942                                         op->o_log_prefix, ent.e_name.bv_val,
943                                         mapped.bv_val, text );
944
945                                 Debug( LDAP_DEBUG_ANY, "%s", buf, 0, 0 );
946                                 ch_free( attr );
947                                 continue;
948                         }
949                 }
950
951                 /* no subschemaSubentry */
952                 if ( attr->a_desc == slap_schema.si_ad_subschemaSubentry
953                         || attr->a_desc == slap_schema.si_ad_entryDN )
954                 {
955
956                         /* 
957                          * We eat target's subschemaSubentry because
958                          * a search for this value is likely not
959                          * to resolve to the appropriate backend;
960                          * later, the local subschemaSubentry is
961                          * added.
962                          *
963                          * We also eat entryDN because the frontend
964                          * will reattach it without checking if already
965                          * present...
966                          */
967                         ( void )ber_scanf( &ber, "x" /* [W] */ );
968
969                         ch_free(attr);
970                         continue;
971                 }
972
973                 if ( ber_scanf( &ber, "[W]", &attr->a_vals ) == LBER_ERROR 
974                                 || attr->a_vals == NULL )
975                 {
976                         attr->a_vals = (struct berval *)&slap_dummy_bv;
977
978                 } else {
979                         for ( last = 0; !BER_BVISNULL( &attr->a_vals[ last ] ); ++last )
980                                 ;
981                 }
982
983                 validate = attr->a_desc->ad_type->sat_syntax->ssyn_validate;
984                 pretty = attr->a_desc->ad_type->sat_syntax->ssyn_pretty;
985
986                 if ( !validate && !pretty ) {
987                         attr->a_nvals = NULL;
988                         attr_free( attr );
989                         goto next_attr;
990                 }
991
992                 if ( attr->a_desc == slap_schema.si_ad_objectClass
993                                 || attr->a_desc == slap_schema.si_ad_structuralObjectClass )
994                 {
995                         for ( bv = attr->a_vals; !BER_BVISNULL( bv ); bv++ ) {
996                                 ldap_back_map( &mi->mi_targets[ target ].mt_rwmap.rwm_oc,
997                                                 bv, &mapped, BACKLDAP_REMAP );
998                                 if ( BER_BVISNULL( &mapped ) || mapped.bv_val[0] == '\0') {
999                                         free( bv->bv_val );
1000                                         BER_BVZERO( bv );
1001                                         if ( --last < 0 ) {
1002                                                 break;
1003                                         }
1004                                         *bv = attr->a_vals[ last ];
1005                                         BER_BVZERO( &attr->a_vals[ last ] );
1006                                         bv--;
1007
1008                                 } else if ( mapped.bv_val != bv->bv_val ) {
1009                                         free( bv->bv_val );
1010                                         ber_dupbv( bv, &mapped );
1011                                 }
1012                         }
1013                 /*
1014                  * It is necessary to try to rewrite attributes with
1015                  * dn syntax because they might be used in ACLs as
1016                  * members of groups; since ACLs are applied to the
1017                  * rewritten stuff, no dn-based subecj clause could
1018                  * be used at the ldap backend side (see
1019                  * http://www.OpenLDAP.org/faq/data/cache/452.html)
1020                  * The problem can be overcome by moving the dn-based
1021                  * ACLs to the target directory server, and letting
1022                  * everything pass thru the ldap backend.
1023                  */
1024                 } else if ( attr->a_desc->ad_type->sat_syntax ==
1025                                 slap_schema.si_syn_distinguishedName )
1026                 {
1027                         ldap_dnattr_result_rewrite( &dc, attr->a_vals );
1028
1029                 } else if ( attr->a_desc == slap_schema.si_ad_ref ) {
1030                         ldap_back_referral_result_rewrite( &dc, attr->a_vals );
1031
1032                 } else {
1033                         int     i;
1034
1035                         for ( i = 0; i < last; i++ ) {
1036                                 struct berval   pval;
1037                                 int             rc;
1038
1039                                 if ( pretty ) {
1040                                         rc = pretty( attr->a_desc->ad_type->sat_syntax,
1041                                                 &attr->a_vals[i], &pval, NULL );
1042
1043                                 } else {
1044                                         rc = validate( attr->a_desc->ad_type->sat_syntax,
1045                                                 &attr->a_vals[i] );
1046                                 }
1047
1048                                 if ( pretty ) {
1049                                         LBER_FREE( attr->a_vals[i].bv_val );
1050                                         attr->a_vals[i] = pval;
1051                                 }
1052                         }
1053                 }
1054
1055                 if ( last && attr->a_desc->ad_type->sat_equality &&
1056                         attr->a_desc->ad_type->sat_equality->smr_normalize )
1057                 {
1058                         int i;
1059
1060                         attr->a_nvals = ch_malloc( ( last + 1 ) * sizeof( struct berval ) );
1061                         for ( i = 0; i<last; i++ ) {
1062                                 attr->a_desc->ad_type->sat_equality->smr_normalize(
1063                                         SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
1064                                         attr->a_desc->ad_type->sat_syntax,
1065                                         attr->a_desc->ad_type->sat_equality,
1066                                         &attr->a_vals[i], &attr->a_nvals[i],
1067                                         NULL );
1068                         }
1069                         BER_BVZERO( &attr->a_nvals[i] );
1070
1071                 } else {
1072                         attr->a_nvals = attr->a_vals;
1073                 }
1074
1075                 *attrp = attr;
1076                 attrp = &attr->a_next;
1077 next_attr:;
1078         }
1079         rs->sr_entry = &ent;
1080         rs->sr_attrs = op->ors_attrs;
1081         rs->sr_flags = 0;
1082         send_search_entry( op, rs );
1083         rs->sr_entry = NULL;
1084         rs->sr_attrs = NULL;
1085         
1086         if ( !BER_BVISNULL( &ent.e_name ) ) {
1087                 if ( ent.e_name.bv_val != bdn.bv_val ) {
1088                         free( ent.e_name.bv_val );
1089                 }
1090                 BER_BVZERO( &ent.e_name );
1091         }
1092         if ( !BER_BVISNULL( &ent.e_nname ) ) {
1093                 free( ent.e_nname.bv_val );
1094                 BER_BVZERO( &ent.e_nname );
1095         }
1096         entry_clean( &ent );
1097
1098         return LDAP_SUCCESS;
1099 }
1100