]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/bind.c
ACIs almost entirely factored out of slapd
[openldap] / servers / slapd / back-meta / bind.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/errno.h>
28 #include <ac/socket.h>
29 #include <ac/string.h>
30
31
32 #define AVL_INTERNAL
33 #include "slap.h"
34 #include "../back-ldap/back-ldap.h"
35 #include "back-meta.h"
36
37 static LDAP_REBIND_PROC meta_back_rebind;
38
39 static int
40 meta_back_single_bind(
41         Operation               *op,
42         SlapReply               *rs,
43         metaconn_t              *mc,
44         int                     candidate );
45
46 int
47 meta_back_bind( Operation *op, SlapReply *rs )
48 {
49         metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
50         metaconn_t      *mc = NULL;
51
52         int             rc = LDAP_OTHER,
53                         i, gotit = 0, isroot = 0;
54
55         SlapReply       *candidates = meta_back_candidates_get( op );
56
57         rs->sr_err = LDAP_SUCCESS;
58
59         Debug( LDAP_DEBUG_ARGS, "meta_back_bind: dn: %s.\n%s%s",
60                         op->o_req_dn.bv_val, "", "" );
61
62         if ( op->orb_method == LDAP_AUTH_SIMPLE && be_isroot_pw( op ) ) {
63                 isroot = 1;
64                 ber_dupbv( &op->orb_edn, be_root_dn( op->o_bd ) );
65         }
66
67         /* we need meta_back_getconn() not send result even on error,
68          * because we want to intercept the error and make it
69          * invalidCredentials */
70         mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_DONTSEND );
71         if ( !mc ) {
72                 Debug( LDAP_DEBUG_ANY,
73                                 "meta_back_bind: no target "
74                                 "for dn \"%s\" (%d: %s).\n",
75                                 op->o_req_dn.bv_val, rs->sr_err,
76                                 rs->sr_text ? rs->sr_text : "" );
77                 /* FIXME: there might be cases where we don't want
78                  * to map the error onto invalidCredentials */
79                 switch ( rs->sr_err ) {
80                 case LDAP_NO_SUCH_OBJECT:
81                 case LDAP_UNWILLING_TO_PERFORM:
82                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
83                         rs->sr_text = NULL;
84                         break;
85                 }
86                 send_ldap_result( op, rs );
87                 return rs->sr_err;
88         }
89
90         /*
91          * Each target is scanned ...
92          */
93         mc->mc_auth_target = META_BOUND_NONE;
94         for ( i = 0; i < mi->mi_ntargets; i++ ) {
95                 int             lerr;
96                 Operation       op2 = *op;
97
98                 /*
99                  * Skip non-candidates
100                  */
101                 if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
102                         continue;
103                 }
104
105                 if ( gotit == 0 ) {
106                         gotit = 1;
107
108                 } else if ( isroot == 0 ) {
109                         /*
110                          * A bind operation is expected to have
111                          * ONE CANDIDATE ONLY!
112                          */
113                         Debug( LDAP_DEBUG_ANY,
114                                         "==>meta_back_bind: more than one"
115                                         " candidate is trying to bind...\n",
116                                         0, 0, 0 );
117                 }
118
119                 if ( isroot && !BER_BVISNULL( &mi->mi_targets[ i ].mt_pseudorootdn ) )
120                 {
121                         op2.o_req_dn = mi->mi_targets[ i ].mt_pseudorootdn;
122                         op2.o_req_ndn = mi->mi_targets[ i ].mt_pseudorootdn;
123                         op2.orb_cred = mi->mi_targets[ i ].mt_pseudorootpw;
124                         op2.orb_method = LDAP_AUTH_SIMPLE;
125                 }
126                 
127                 lerr = meta_back_single_bind( &op2, rs, mc, i );
128                 if ( lerr != LDAP_SUCCESS ) {
129                         rs->sr_err = lerr;
130                         candidates[ i ].sr_tag = META_NOT_CANDIDATE;
131
132                 } else {
133                         rc = LDAP_SUCCESS;
134                 }
135         }
136
137         if ( isroot ) {
138                 mc->mc_auth_target = META_BOUND_ALL;
139         }
140
141         meta_back_release_conn( op, mc );
142
143         /*
144          * rc is LDAP_SUCCESS if at least one bind succeeded,
145          * err is the last error that occurred during a bind;
146          * if at least (and at most?) one bind succeedes, fine.
147          */
148         if ( rc != LDAP_SUCCESS ) {
149                 
150                 /*
151                  * deal with bind failure ...
152                  */
153
154                 /*
155                  * no target was found within the naming context, 
156                  * so bind must fail with invalid credentials
157                  */
158                 if ( rs->sr_err == LDAP_SUCCESS && gotit == 0 ) {
159                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
160                 }
161
162                 rs->sr_err = slap_map_api2result( rs );
163                 send_ldap_result( op, rs );
164                 return rs->sr_err;
165         }
166
167         return LDAP_SUCCESS;
168 }
169
170 /*
171  * meta_back_single_bind
172  *
173  * attempts to perform a bind with creds
174  */
175 static int
176 meta_back_single_bind(
177         Operation               *op,
178         SlapReply               *rs,
179         metaconn_t              *mc,
180         int                     candidate )
181 {
182         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
183         metatarget_t            *mt = &mi->mi_targets[ candidate ];
184         struct berval           mdn = BER_BVNULL;
185         dncookie                dc;
186         metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
187         int                     msgid,
188                                 rebinding = 0;
189         
190         /*
191          * Rewrite the bind dn if needed
192          */
193         dc.target = &mi->mi_targets[ candidate ];
194         dc.conn = op->o_conn;
195         dc.rs = rs;
196         dc.ctx = "bindDN";
197
198         if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
199                 send_ldap_result( op, rs );
200                 return -1;
201         }
202
203         /* FIXME: this fixes the bind problem right now; we need
204          * to use the asynchronous version to get the "matched"
205          * and more in case of failure ... */
206         /* FIXME: should be check if at least some of the op->o_ctrls
207          * can/should be passed? */
208 rebind:;
209         rs->sr_err = ldap_sasl_bind( msc->msc_ld, mdn.bv_val,
210                         LDAP_SASL_SIMPLE, &op->orb_cred,
211                         op->o_ctrls, NULL, &msgid );
212         if ( rs->sr_err == LDAP_SUCCESS ) {
213                 LDAPMessage     *res;
214                 struct timeval  tv;
215                 int             rc;
216                 int             nretries = mt->mt_nretries;
217
218                 /*
219                  * handle response!!!
220                  */
221 retry:;
222                 tv.tv_sec = 0;
223                 tv.tv_usec = META_BIND_TIMEOUT;
224                 switch ( ldap_result( msc->msc_ld, msgid, 0, &tv, &res ) ) {
225                 case 0:
226                         Debug( LDAP_DEBUG_ANY,
227                                 "%s meta_back_single_bind: "
228                                 "ldap_result=0 nretries=%d%s\n",
229                                 op->o_log_prefix, nretries,
230                                 rebinding ? " rebinding" : "" );
231
232                         if ( nretries != META_RETRY_NEVER ) {
233                                 ldap_pvt_thread_yield();
234                                 if ( nretries > 0 ) {
235                                         nretries--;
236                                 }
237                                 goto retry;
238                         }
239                         rs->sr_err = LDAP_BUSY;
240                         if ( rebinding ) {
241                                 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
242                                 break;
243                         }
244
245                         /* FIXME: some times the request times out
246                          * while the other party is not willing to
247                          * send a response any more.  Give it a second
248                          * chance with a freshly bound connection */
249                         rebinding = 1;
250                         nretries = mt->mt_nretries;
251                         /* fallthru */
252
253                 case -1:
254                         ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
255                                         &rs->sr_err );
256
257                         if ( rebinding ) {
258                                 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
259                         }
260
261                         Debug( LDAP_DEBUG_ANY,
262                                 "### %s meta_back_single_bind: "
263                                 "err=%d nretries=%d\n",
264                                 op->o_log_prefix, rs->sr_err, nretries );
265
266                         rc = slap_map_api2result( rs );
267                         if ( rs->sr_err == LDAP_UNAVAILABLE && nretries != META_RETRY_NEVER ) {
268                                 ldap_pvt_thread_mutex_lock( &mi->mi_conn_mutex );
269                                 if ( mc->mc_refcnt == 1 ) {
270                                         ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
271                                         msc->msc_ld = NULL;
272                                         msc->msc_bound = 0;
273
274                                         ( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn );
275
276                                         /* mc here must be the regular mc,
277                                          * reset and ready for init */
278                                         rc = meta_back_init_one_conn( op, rs,
279                                                 mt, msc, LDAP_BACK_DONTSEND );
280
281                                 } else {
282                                         /* can't do anything about it */
283                                         rc = 0;
284                                 }
285
286                                 ldap_pvt_thread_mutex_unlock( &mi->mi_conn_mutex );
287
288                                 if ( rc ) {
289                                         if ( nretries > 0 ) {
290                                                 nretries--;
291                                         }
292                                         ldap_pvt_thread_yield();
293                                         goto rebind;
294                                 }
295                         }
296                         break;
297
298                 default:
299                         rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
300                                         NULL, NULL, NULL, NULL, 1 );
301                         if ( rc != LDAP_SUCCESS ) {
302                                 rs->sr_err = rc;
303                         }
304                         break;
305                 }
306         }
307
308         if ( rs->sr_err != LDAP_SUCCESS ) {
309                 rs->sr_err = slap_map_api2result( rs );
310                 goto return_results;
311         }
312
313         ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_dn );
314         msc->msc_bound = META_BOUND;
315         mc->mc_auth_target = candidate;
316
317         if ( LDAP_BACK_SAVECRED( mi ) ) {
318                 ber_bvreplace( &msc->msc_cred, &op->orb_cred );
319                 ldap_set_rebind_proc( msc->msc_ld, meta_back_rebind, msc );
320         }
321
322         if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED
323                         && op->o_req_ndn.bv_len != 0 )
324         {
325                 ( void )meta_dncache_update_entry( &mi->mi_cache,
326                                 &op->o_req_ndn, candidate );
327         }
328
329 return_results:;
330         if ( mdn.bv_val != op->o_req_dn.bv_val ) {
331                 free( mdn.bv_val );
332         }
333
334         return rs->sr_err;
335 }
336
337 /*
338  * meta_back_single_dobind
339  */
340 int
341 meta_back_single_dobind(
342         Operation               *op,
343         SlapReply               *rs,
344         metaconn_t              *mc,
345         int                     candidate,
346         ldap_back_send_t        sendok,
347         int                     nretries,
348         int                     dolock )
349 {
350         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
351         metatarget_t            *mt = &mi->mi_targets[ candidate ];
352         metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
353         int                     rc;
354         struct berval           cred = BER_BVC( "" );
355         int                     msgid,
356                                 rebinding = 0,
357                                 save_nretries = nretries;
358
359         /*
360          * Otherwise an anonymous bind is performed
361          * (note: if the target was already bound, the anonymous
362          * bind clears the previous bind).
363          */
364         if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
365                 ber_memfree( msc->msc_bound_ndn.bv_val );
366                 BER_BVZERO( &msc->msc_bound_ndn );
367         }
368                 
369         if ( !BER_BVISNULL( &msc->msc_cred ) ) {
370                 /* destroy sensitive data */
371                 memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
372                 ber_memfree( msc->msc_cred.bv_val );
373                 BER_BVZERO( &msc->msc_cred );
374         }
375
376         /* FIXME: should we check if at least some of the op->o_ctrls
377          * can/should be passed? */
378 rebind:;
379         rc = ldap_sasl_bind( msc->msc_ld, "", LDAP_SASL_SIMPLE, &cred,
380                         NULL, NULL, &msgid );
381         if ( rc == LDAP_SUCCESS ) {
382                 LDAPMessage     *res;
383                 struct timeval  tv;
384
385                 /*
386                  * handle response!!!
387                  */
388 retry:;
389                 tv.tv_sec = 0;
390                 tv.tv_usec = META_BIND_TIMEOUT;
391                 switch ( ldap_result( msc->msc_ld, msgid, 0, &tv, &res ) ) {
392                 case 0:
393                         Debug( LDAP_DEBUG_ANY,
394                                 "%s meta_back_single_dobind: "
395                                 "ldap_result=0 nretries=%d%s\n",
396                                 op->o_log_prefix, nretries,
397                                 rebinding ? " rebinding" : "" );
398
399                         if ( nretries != META_RETRY_NEVER ) {
400                                 ldap_pvt_thread_yield();
401                                 if ( nretries > 0 ) {
402                                         nretries--;
403                                 }
404                                 goto retry;
405                         }
406
407                         rc = LDAP_BUSY;
408                         if ( rebinding ) {
409                                 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
410                                 break;
411                         }
412
413                         /* FIXME: some times the request times out
414                          * while the other party is not willing to
415                          * send a response any more.  Give it a second
416                          * chance with a freshly bound connection */
417                         rebinding = 1;
418                         nretries = save_nretries;
419                         /* fallthru */
420
421                 case -1:
422                         ldap_get_option( msc->msc_ld,
423                                         LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
424
425                         if ( rebinding ) {
426                                 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
427                         }
428
429                         Debug( LDAP_DEBUG_ANY,
430                                 "### %s meta_back_single_dobind: "
431                                 "err=%d nretries=%d\n",
432                                 op->o_log_prefix, rs->sr_err, nretries );
433
434                         rc = slap_map_api2result( rs );
435                         if ( rc == LDAP_UNAVAILABLE && nretries != META_RETRY_NEVER ) {
436                                 if ( dolock ) {
437                                         ldap_pvt_thread_mutex_lock( &mi->mi_conn_mutex );
438                                 }
439
440                                 if ( mc->mc_refcnt == 1 ) {
441                                         ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
442                                         msc->msc_ld = NULL;
443                                         msc->msc_bound = 0;
444
445                                         ( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn );
446
447                                         /* mc here must be the regular mc,
448                                          * reset and ready for init */
449                                         rc = meta_back_init_one_conn( op, rs,
450                                                 mt, msc, LDAP_BACK_DONTSEND );
451                                 
452
453                                 } else {
454                                         /* can't do anything about it */
455                                         rc = LDAP_UNAVAILABLE;
456                                 }
457
458                                 if ( dolock ) {
459                                         ldap_pvt_thread_mutex_unlock( &mi->mi_conn_mutex );
460                                 }
461
462                                 if ( rc == LDAP_SUCCESS ) {
463                                         ldap_pvt_thread_yield();
464                                         if ( nretries > 0 ) {
465                                                 nretries--;
466                                         }
467                                         goto rebind;
468                                 }
469                         }
470                         break;
471
472                 default:
473                         rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
474                                         NULL, NULL, NULL, NULL, 1 );
475                         if ( rc == LDAP_SUCCESS ) {
476                                 rc = slap_map_api2result( rs );
477                         }
478                         break;
479                 }
480
481         } else {
482                 rc = slap_map_api2result( rs );
483         }
484
485         rs->sr_err = rc;
486         if ( rc != LDAP_SUCCESS && ( sendok & LDAP_BACK_SENDERR ) ) {
487                 send_ldap_result( op, rs );
488         }
489
490         return rc;
491 }
492
493 /*
494  * meta_back_dobind
495  */
496 int
497 meta_back_dobind(
498         Operation               *op,
499         SlapReply               *rs,
500         metaconn_t              *mc,
501         ldap_back_send_t        sendok )
502 {
503         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
504
505         int                     bound = 0,
506                                 i,
507                                 isroot = 0;
508
509         SlapReply               *candidates = meta_back_candidates_get( op );
510
511         if ( be_isroot( op ) ) {
512                 isroot = 1;
513         }
514
515         Debug( LDAP_DEBUG_TRACE,
516                 "%s meta_back_dobind: conn=%ld%s\n",
517                 op->o_log_prefix, mc->mc_conn->c_connid,
518                 isroot ? " (isroot)" : "" );
519
520         ldap_pvt_thread_mutex_lock( &mc->mc_mutex );
521
522         /*
523          * all the targets are bound as pseudoroot
524          */
525         if ( mc->mc_auth_target == META_BOUND_ALL ) {
526                 bound = 1;
527                 goto done;
528         }
529
530         for ( i = 0; i < mi->mi_ntargets; i++ ) {
531                 metatarget_t            *mt = &mi->mi_targets[ i ];
532                 metasingleconn_t        *msc = &mc->mc_conns[ i ];
533                 int                     rc;
534
535                 /*
536                  * Not a candidate
537                  */
538                 if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
539                         continue;
540                 }
541
542                 assert( msc->msc_ld != NULL );
543
544                 /*
545                  * If the target is already bound it is skipped
546                  */
547                 if ( msc->msc_bound == META_BOUND && mc->mc_auth_target == i ) {
548                         ++bound;
549
550                         Debug( LDAP_DEBUG_TRACE, "%s meta_back_dobind[%d]: "
551                                         "authcTarget\n",
552                                         op->o_log_prefix, i, 0 );
553                         continue;
554                 }
555
556                 if ( isroot && !BER_BVISNULL( &mi->mi_targets[ i ].mt_pseudorootdn ) )
557                 {
558                         Operation       op2 = *op;
559
560                         op2.o_tag = LDAP_REQ_BIND;
561                         op2.o_req_dn = mi->mi_targets[ i ].mt_pseudorootdn;
562                         op2.o_req_ndn = mi->mi_targets[ i ].mt_pseudorootdn;
563                         op2.orb_cred = mi->mi_targets[ i ].mt_pseudorootpw;
564                         op2.orb_method = LDAP_AUTH_SIMPLE;
565
566                         rc = meta_back_single_bind( &op2, rs, mc, i );
567
568                 } else {
569                         rc = meta_back_single_dobind( op, rs, mc, i,
570                                 LDAP_BACK_DONTSEND, mt->mt_nretries, 1 );
571                 }
572
573                 if ( rc != LDAP_SUCCESS ) {
574                         rs->sr_err = slap_map_api2result( rs );
575
576                         Debug( LDAP_DEBUG_ANY, "%s meta_back_dobind[%d]: "
577                                         "(anonymous) err=%d\n",
578                                         op->o_log_prefix, i, rc );
579
580                         /*
581                          * null cred bind should always succeed
582                          * as anonymous, so a failure means
583                          * the target is no longer candidate possibly
584                          * due to technical reasons (remote host down?)
585                          * so better clear the handle
586                          */
587                         candidates[ i ].sr_tag = META_NOT_CANDIDATE;
588                         continue;
589                 } /* else */
590                 
591                 Debug( LDAP_DEBUG_TRACE, "%s meta_back_dobind[%d]: "
592                                 "(anonymous)\n",
593                                 op->o_log_prefix, i, 0 );
594
595                 msc->msc_bound = META_ANONYMOUS;
596                 ++bound;
597         }
598
599 done:;
600         ldap_pvt_thread_mutex_unlock( &mc->mc_mutex );
601
602         Debug( LDAP_DEBUG_TRACE,
603                 "%s meta_back_dobind: conn=%ld bound=%d\n",
604                 op->o_log_prefix, mc->mc_conn->c_connid, bound );
605
606         if ( bound == 0 ) {
607                 meta_back_release_conn( op, mc );
608
609                 if ( sendok & LDAP_BACK_SENDERR ) {
610                         if ( rs->sr_err == LDAP_SUCCESS ) {
611                                 rs->sr_err = LDAP_BUSY;
612                         }
613                         send_ldap_result( op, rs );
614                 }
615         }
616
617         return( bound > 0 );
618 }
619
620 /*
621  * meta_back_rebind
622  *
623  * This is a callback used for chasing referrals using the same
624  * credentials as the original user on this session.
625  */
626 static int 
627 meta_back_rebind(
628         LDAP                    *ld,
629         LDAP_CONST char         *url,
630         ber_tag_t               request,
631         ber_int_t               msgid,
632         void                    *params )
633 {
634         metasingleconn_t        *msc = ( metasingleconn_t * )params;
635
636         return ldap_sasl_bind_s( ld, msc->msc_bound_ndn.bv_val,
637                         LDAP_SASL_SIMPLE, &msc->msc_cred,
638                         NULL, NULL, NULL );
639 }
640
641 /*
642  * FIXME: error return must be handled in a cleaner way ...
643  */
644 int
645 meta_back_op_result(
646         metaconn_t      *mc,
647         Operation       *op,
648         SlapReply       *rs,
649         int             candidate )
650 {
651         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
652
653         int                     i,
654                                 rerr = LDAP_SUCCESS;
655         char                    *rmsg = NULL;
656         char                    *rmatch = NULL;
657         int                     free_rmsg = 0,
658                                 free_rmatch = 0;
659
660         if ( candidate != META_TARGET_NONE ) {
661                 metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
662
663                 rs->sr_err = LDAP_SUCCESS;
664
665                 ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
666                 if ( rs->sr_err != LDAP_SUCCESS ) {
667                         /*
668                          * better check the type of error. In some cases
669                          * (search ?) it might be better to return a
670                          * success if at least one of the targets gave
671                          * positive result ...
672                          */
673                         ldap_get_option( msc->msc_ld,
674                                         LDAP_OPT_ERROR_STRING, &rmsg );
675                         ldap_get_option( msc->msc_ld,
676                                         LDAP_OPT_MATCHED_DN, &rmatch );
677                         rerr = rs->sr_err = slap_map_api2result( rs );
678
679                         if ( rmsg ) {
680                                 free_rmsg = 1;
681                         }
682                         if ( rmatch ) {
683                                 free_rmatch = 1;
684                         }
685
686                         Debug(LDAP_DEBUG_ANY,
687                                         "==> meta_back_op_result: target"
688                                         " <%d> sending msg \"%s\""
689                                         " (matched \"%s\")\n", 
690                                         candidate, ( rmsg ? rmsg : "" ),
691                                         ( rmatch ? rmatch : "" ) );
692                 }
693
694         } else {
695                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
696                         metasingleconn_t        *msc = &mc->mc_conns[ i ];
697                         char                    *msg = NULL;
698                         char                    *match = NULL;
699
700                         rs->sr_err = LDAP_SUCCESS;
701
702                         ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
703                         if ( rs->sr_err != LDAP_SUCCESS ) {
704                                 /*
705                                  * better check the type of error. In some cases
706                                  * (search ?) it might be better to return a
707                                  * success if at least one of the targets gave
708                                  * positive result ...
709                                  */
710                                 ldap_get_option( msc->msc_ld,
711                                                 LDAP_OPT_ERROR_STRING, &msg );
712                                 ldap_get_option( msc->msc_ld,
713                                                 LDAP_OPT_MATCHED_DN, &match );
714                                 rs->sr_err = slap_map_api2result( rs );
715         
716                                 Debug(LDAP_DEBUG_ANY,
717                                                 "==> meta_back_op_result: target"
718                                                 " <%d> sending msg \"%s\""
719                                                 " (matched \"%s\")\n", 
720                                                 i, ( msg ? msg : "" ),
721                                                 ( match ? match : "" ) );
722         
723                                 /*
724                                  * FIXME: need to rewrite "match" (need rwinfo)
725                                  */
726                                 switch ( rs->sr_err ) {
727                                 default:
728                                         rerr = rs->sr_err;
729                                         if ( rmsg ) {
730                                                 ber_memfree( rmsg );
731                                         }
732                                         rmsg = msg;
733                                         free_rmsg = 1;
734                                         msg = NULL;
735                                         if ( rmatch ) {
736                                                 ber_memfree( rmatch );
737                                         }
738                                         rmatch = match;
739                                         free_rmatch = 1;
740                                         match = NULL;
741                                         break;
742                                 }
743         
744                                 /* better test the pointers before freeing? */
745                                 if ( match ) {
746                                         free( match );
747                                 }
748                                 if ( msg ) {
749                                         free( msg );
750                                 }
751                         }
752                 }
753         }
754         
755         rs->sr_err = rerr;
756         rs->sr_text = rmsg;
757         rs->sr_matched = rmatch;
758         send_ldap_result( op, rs );
759         if ( free_rmsg ) {
760                 ber_memfree( rmsg );
761         }
762         if ( free_rmatch ) {
763                 ber_memfree( rmatch );
764         }
765         rs->sr_text = NULL;
766         rs->sr_matched = NULL;
767
768         return ( ( rerr == LDAP_SUCCESS ) ? 0 : -1 );
769 }
770