]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/bind.c
fix yet another leak
[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, "%s meta_back_single_bind: ldap_result=%d nretries=%d\n",
227                                 op->o_log_prefix, 0, nretries );
228
229                         if ( nretries != META_RETRY_NEVER ) {
230                                 ldap_pvt_thread_yield();
231                                 if ( nretries > 0 ) {
232                                         nretries--;
233                                 }
234                                 goto retry;
235                         }
236                         rs->sr_err = LDAP_BUSY;
237                         if ( rebinding ) {
238                                 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
239                                 break;
240                         }
241
242                         /* FIXME: some times the request times out
243                          * while the other party is not willing to
244                          * send a response any more.  Give it a second
245                          * chance with a freshly bound connection */
246                         rebinding = 1;
247                         nretries = mt->mt_nretries;
248                         /* fallthru */
249
250                 case -1:
251                         ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
252                                         &rs->sr_err );
253
254                         if ( rebinding ) {
255                                 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
256                         }
257
258                         Debug( LDAP_DEBUG_ANY, "### %s meta_back_single_bind: err=%d nretries=%d\n",
259                                 op->o_log_prefix, rs->sr_err, nretries );
260
261                         rc = slap_map_api2result( rs );
262                         if ( rs->sr_err == LDAP_UNAVAILABLE && nretries != META_RETRY_NEVER ) {
263 retry_lock:;
264                                 switch ( ldap_pvt_thread_mutex_trylock( &mi->mi_conn_mutex ) ) {
265                                 case LDAP_PVT_THREAD_EBUSY:
266                                 default:
267                                         ldap_pvt_thread_yield();
268                                         goto retry_lock;
269
270                                 case 0:
271                                         break;
272                                 }
273
274                                 if ( mc->mc_refcnt == 1 ) {
275                                         ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
276                                         msc->msc_ld = NULL;
277                                         msc->msc_bound = 0;
278
279                                         /* mc here must be the regular mc,
280                                          * reset and ready for init */
281                                         rc = meta_back_init_one_conn( op, rs,
282                                                 mt, msc, LDAP_BACK_DONTSEND );
283
284                                 } else {
285                                         /* can't do anything about it */
286                                         rc = 0;
287                                 }
288
289                                 ldap_pvt_thread_mutex_unlock( &mi->mi_conn_mutex );
290
291                                 if ( rc ) {
292                                         if ( nretries > 0 ) {
293                                                 nretries--;
294                                         }
295                                         ldap_pvt_thread_yield();
296                                         goto rebind;
297                                 }
298                         }
299                         break;
300
301                 default:
302                         rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
303                                         NULL, NULL, NULL, NULL, 1 );
304                         if ( rc != LDAP_SUCCESS ) {
305                                 rs->sr_err = rc;
306                         }
307                         break;
308                 }
309         }
310
311         if ( rs->sr_err != LDAP_SUCCESS ) {
312                 rs->sr_err = slap_map_api2result( rs );
313                 goto return_results;
314         }
315
316         ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_dn );
317         msc->msc_bound = META_BOUND;
318         mc->mc_auth_target = candidate;
319
320         if ( LDAP_BACK_SAVECRED( mi ) ) {
321                 ber_bvreplace( &msc->msc_cred, &op->orb_cred );
322                 ldap_set_rebind_proc( msc->msc_ld, meta_back_rebind, msc );
323         }
324
325         if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED
326                         && op->o_req_ndn.bv_len != 0 )
327         {
328                 ( void )meta_dncache_update_entry( &mi->mi_cache,
329                                 &op->o_req_ndn, candidate );
330         }
331
332 return_results:;
333         if ( mdn.bv_val != op->o_req_dn.bv_val ) {
334                 free( mdn.bv_val );
335         }
336
337         return rs->sr_err;
338 }
339
340 /*
341  * meta_back_single_dobind
342  */
343 int
344 meta_back_single_dobind(
345         Operation               *op,
346         SlapReply               *rs,
347         metaconn_t              *mc,
348         int                     candidate,
349         ldap_back_send_t        sendok,
350         int                     nretries,
351         int                     dolock )
352 {
353         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
354         metatarget_t            *mt = &mi->mi_targets[ candidate ];
355         metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
356         int                     rc;
357         struct berval           cred = BER_BVC( "" );
358         int                     msgid,
359                                 rebinding = 0,
360                                 save_nretries = nretries;
361
362         /*
363          * Otherwise an anonymous bind is performed
364          * (note: if the target was already bound, the anonymous
365          * bind clears the previous bind).
366          */
367         if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
368                 ber_memfree( msc->msc_bound_ndn.bv_val );
369                 BER_BVZERO( &msc->msc_bound_ndn );
370         }
371                 
372         if ( !BER_BVISNULL( &msc->msc_cred ) ) {
373                 /* destroy sensitive data */
374                 memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
375                 ber_memfree( msc->msc_cred.bv_val );
376                 BER_BVZERO( &msc->msc_cred );
377         }
378
379         /* FIXME: should we check if at least some of the op->o_ctrls
380          * can/should be passed? */
381 rebind:;
382         rc = ldap_sasl_bind( msc->msc_ld, "", LDAP_SASL_SIMPLE, &cred,
383                         NULL, NULL, &msgid );
384         if ( rc == LDAP_SUCCESS ) {
385                 LDAPMessage     *res;
386                 struct timeval  tv;
387
388                 /*
389                  * handle response!!!
390                  */
391 retry:;
392                 tv.tv_sec = 0;
393                 tv.tv_usec = META_BIND_TIMEOUT;
394                 switch ( ldap_result( msc->msc_ld, msgid, 0, &tv, &res ) ) {
395                 case 0:
396                         Debug( LDAP_DEBUG_ANY, "%s meta_back_single_dobind: ldap_result=%d nretries=%d\n",
397                                 op->o_log_prefix, 0, nretries );
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, "### %s meta_back_single_dobind: err=%d nretries=%d\n",
430                                         op->o_log_prefix, rs->sr_err, nretries );
431
432                         rc = slap_map_api2result( rs );
433                         if ( rc == LDAP_UNAVAILABLE && nretries != META_RETRY_NEVER ) {
434                                 if ( dolock ) {
435 retry_lock:;
436                                         switch ( ldap_pvt_thread_mutex_trylock( &mi->mi_conn_mutex ) ) {
437                                         case LDAP_PVT_THREAD_EBUSY:
438                                         default:
439                                                 ldap_pvt_thread_yield();
440                                                 goto retry_lock;
441         
442                                         case 0:
443                                                 break;
444                                         }
445                                 }
446
447                                 if ( mc->mc_refcnt == 1 ) {
448                                         ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
449                                         msc->msc_ld = NULL;
450                                         msc->msc_bound = 0;
451
452                                         /* mc here must be the regular mc,
453                                          * reset and ready for init */
454                                         rc = meta_back_init_one_conn( op, rs,
455                                                 mt, msc, LDAP_BACK_DONTSEND );
456                                 
457
458                                 } else {
459                                         /* can't do anything about it */
460                                         rc = LDAP_UNAVAILABLE;
461                                 }
462
463                                 if ( dolock ) {
464                                         ldap_pvt_thread_mutex_unlock( &mi->mi_conn_mutex );
465                                 }
466
467                                 if ( rc == LDAP_SUCCESS ) {
468                                         ldap_pvt_thread_yield();
469                                         if ( nretries > 0 ) {
470                                                 nretries--;
471                                         }
472                                         goto rebind;
473                                 }
474                         }
475                         break;
476
477                 default:
478                         rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
479                                         NULL, NULL, NULL, NULL, 1 );
480                         if ( rc == LDAP_SUCCESS ) {
481                                 rc = slap_map_api2result( rs );
482                         }
483                         break;
484                 }
485
486         } else {
487                 rc = slap_map_api2result( rs );
488         }
489
490         rs->sr_err = rc;
491         if ( rc != LDAP_SUCCESS && ( sendok & LDAP_BACK_SENDERR ) ) {
492                 send_ldap_result( op, rs );
493         }
494
495         return rc;
496 }
497
498 /*
499  * meta_back_dobind
500  */
501 int
502 meta_back_dobind(
503         Operation               *op,
504         SlapReply               *rs,
505         metaconn_t              *mc,
506         ldap_back_send_t        sendok )
507 {
508         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
509
510         int                     bound = 0, i;
511
512         SlapReply               *candidates = meta_back_candidates_get( op );
513
514         ldap_pvt_thread_mutex_lock( &mc->mc_mutex );
515
516         Debug( LDAP_DEBUG_TRACE,
517                 "%s meta_back_dobind: conn=%ld\n",
518                 op->o_log_prefix, mc->mc_conn->c_connid, 0 );
519
520         /*
521          * all the targets are bound as pseudoroot
522          */
523         if ( mc->mc_auth_target == META_BOUND_ALL ) {
524                 bound = 1;
525                 goto done;
526         }
527
528         for ( i = 0; i < mi->mi_ntargets; i++ ) {
529                 metatarget_t            *mt = &mi->mi_targets[ i ];
530                 metasingleconn_t        *msc = &mc->mc_conns[ i ];
531                 int                     rc;
532
533                 /*
534                  * Not a candidate
535                  */
536                 if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
537                         continue;
538                 }
539
540                 assert( msc->msc_ld != NULL );
541
542                 /*
543                  * If the target is already bound it is skipped
544                  */
545                 if ( msc->msc_bound == META_BOUND && mc->mc_auth_target == i ) {
546                         ++bound;
547
548                         Debug( LDAP_DEBUG_TRACE, "%s meta_back_dobind[%d]: "
549                                         "authcTarget\n",
550                                         op->o_log_prefix, i, 0 );
551                         continue;
552                 }
553
554                 rc = meta_back_single_dobind( op, rs, mc, i,
555                                 LDAP_BACK_DONTSEND, mt->mt_nretries, 1 );
556                 if ( rc != LDAP_SUCCESS ) {
557                         rs->sr_err = slap_map_api2result( rs );
558
559                         Debug( LDAP_DEBUG_ANY, "%s meta_back_dobind[%d]: "
560                                         "(anonymous) err=%d\n",
561                                         op->o_log_prefix, i, rc );
562
563                         /*
564                          * null cred bind should always succeed
565                          * as anonymous, so a failure means
566                          * the target is no longer candidate possibly
567                          * due to technical reasons (remote host down?)
568                          * so better clear the handle
569                          */
570                         candidates[ i ].sr_tag = META_NOT_CANDIDATE;
571                         continue;
572                 } /* else */
573                 
574                 Debug( LDAP_DEBUG_TRACE, "%s meta_back_dobind[%d]: "
575                                 "(anonymous)\n",
576                                 op->o_log_prefix, i, 0 );
577
578                 msc->msc_bound = META_ANONYMOUS;
579                 ++bound;
580         }
581
582 done:;
583         ldap_pvt_thread_mutex_unlock( &mc->mc_mutex );
584
585         Debug( LDAP_DEBUG_TRACE,
586                 "%s meta_back_dobind: conn=%ld bound=%d\n",
587                 op->o_log_prefix, mc->mc_conn->c_connid, bound );
588
589         if ( bound == 0 ) {
590                 meta_back_release_conn( op, mc );
591
592                 if ( sendok & LDAP_BACK_SENDERR ) {
593                         if ( rs->sr_err == LDAP_SUCCESS ) {
594                                 rs->sr_err = LDAP_BUSY;
595                         }
596                         send_ldap_result( op, rs );
597                 }
598         }
599
600         return( bound > 0 );
601 }
602
603 /*
604  * meta_back_rebind
605  *
606  * This is a callback used for chasing referrals using the same
607  * credentials as the original user on this session.
608  */
609 static int 
610 meta_back_rebind(
611         LDAP                    *ld,
612         LDAP_CONST char         *url,
613         ber_tag_t               request,
614         ber_int_t               msgid,
615         void                    *params )
616 {
617         metasingleconn_t        *msc = ( metasingleconn_t * )params;
618
619         return ldap_sasl_bind_s( ld, msc->msc_bound_ndn.bv_val,
620                         LDAP_SASL_SIMPLE, &msc->msc_cred,
621                         NULL, NULL, NULL );
622 }
623
624 /*
625  * FIXME: error return must be handled in a cleaner way ...
626  */
627 int
628 meta_back_op_result(
629         metaconn_t      *mc,
630         Operation       *op,
631         SlapReply       *rs,
632         int             candidate )
633 {
634         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
635
636         int                     i,
637                                 rerr = LDAP_SUCCESS;
638         char                    *rmsg = NULL;
639         char                    *rmatch = NULL;
640         int                     free_rmsg = 0,
641                                 free_rmatch = 0;
642
643         if ( candidate != META_TARGET_NONE ) {
644                 metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
645
646                 rs->sr_err = LDAP_SUCCESS;
647
648                 ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
649                 if ( rs->sr_err != LDAP_SUCCESS ) {
650                         /*
651                          * better check the type of error. In some cases
652                          * (search ?) it might be better to return a
653                          * success if at least one of the targets gave
654                          * positive result ...
655                          */
656                         ldap_get_option( msc->msc_ld,
657                                         LDAP_OPT_ERROR_STRING, &rmsg );
658                         ldap_get_option( msc->msc_ld,
659                                         LDAP_OPT_MATCHED_DN, &rmatch );
660                         rerr = rs->sr_err = slap_map_api2result( rs );
661
662                         if ( rmsg ) {
663                                 free_rmsg = 1;
664                         }
665                         if ( rmatch ) {
666                                 free_rmatch = 1;
667                         }
668
669                         Debug(LDAP_DEBUG_ANY,
670                                         "==> meta_back_op_result: target"
671                                         " <%d> sending msg \"%s\""
672                                         " (matched \"%s\")\n", 
673                                         candidate, ( rmsg ? rmsg : "" ),
674                                         ( rmatch ? rmatch : "" ) );
675                 }
676
677         } else {
678                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
679                         metasingleconn_t        *msc = &mc->mc_conns[ i ];
680                         char                    *msg = NULL;
681                         char                    *match = NULL;
682
683                         rs->sr_err = LDAP_SUCCESS;
684
685                         ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
686                         if ( rs->sr_err != LDAP_SUCCESS ) {
687                                 /*
688                                  * better check the type of error. In some cases
689                                  * (search ?) it might be better to return a
690                                  * success if at least one of the targets gave
691                                  * positive result ...
692                                  */
693                                 ldap_get_option( msc->msc_ld,
694                                                 LDAP_OPT_ERROR_STRING, &msg );
695                                 ldap_get_option( msc->msc_ld,
696                                                 LDAP_OPT_MATCHED_DN, &match );
697                                 rs->sr_err = slap_map_api2result( rs );
698         
699                                 Debug(LDAP_DEBUG_ANY,
700                                                 "==> meta_back_op_result: target"
701                                                 " <%d> sending msg \"%s\""
702                                                 " (matched \"%s\")\n", 
703                                                 i, ( msg ? msg : "" ),
704                                                 ( match ? match : "" ) );
705         
706                                 /*
707                                  * FIXME: need to rewrite "match" (need rwinfo)
708                                  */
709                                 switch ( rs->sr_err ) {
710                                 default:
711                                         rerr = rs->sr_err;
712                                         if ( rmsg ) {
713                                                 ber_memfree( rmsg );
714                                         }
715                                         rmsg = msg;
716                                         free_rmsg = 1;
717                                         msg = NULL;
718                                         if ( rmatch ) {
719                                                 ber_memfree( rmatch );
720                                         }
721                                         rmatch = match;
722                                         free_rmatch = 1;
723                                         match = NULL;
724                                         break;
725                                 }
726         
727                                 /* better test the pointers before freeing? */
728                                 if ( match ) {
729                                         free( match );
730                                 }
731                                 if ( msg ) {
732                                         free( msg );
733                                 }
734                         }
735                 }
736         }
737         
738         rs->sr_err = rerr;
739         rs->sr_text = rmsg;
740         rs->sr_matched = rmatch;
741         send_ldap_result( op, rs );
742         if ( free_rmsg ) {
743                 ber_memfree( rmsg );
744         }
745         if ( free_rmatch ) {
746                 ber_memfree( rmatch );
747         }
748         rs->sr_text = NULL;
749         rs->sr_matched = NULL;
750
751         return ( ( rerr == LDAP_SUCCESS ) ? 0 : -1 );
752 }
753