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