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