]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/bind.c
better fix: in case of error during bind, just bail out (very conservative, though)
[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_default_rebind;
38
39 /*
40  * a module could register a replacement for this function
41  */
42 LDAP_REBIND_PROC        *meta_back_rebind_f = meta_back_default_rebind;
43
44 static int
45 meta_back_single_bind(
46         Operation               *op,
47         SlapReply               *rs,
48         metaconn_t              *mc,
49         int                     candidate );
50
51 int
52 meta_back_bind( Operation *op, SlapReply *rs )
53 {
54         metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
55         metaconn_t      *mc = NULL;
56
57         int             rc = LDAP_OTHER,
58                         i,
59                         gotit = 0,
60                         isroot = 0;
61
62         SlapReply       *candidates = meta_back_candidates_get( op );
63
64         rs->sr_err = LDAP_SUCCESS;
65
66         Debug( LDAP_DEBUG_ARGS, "%s meta_back_bind: dn=\"%s\".\n",
67                 op->o_log_prefix, op->o_req_dn.bv_val, 0 );
68
69         /* the test on the bind method should be superfluous */
70         if ( op->orb_method == LDAP_AUTH_SIMPLE
71                 && be_isroot_dn( op->o_bd, &op->o_req_ndn ) )
72         {
73                 if ( !be_isroot_pw( op ) ) {
74                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
75                         rs->sr_text = NULL;
76                         send_ldap_result( op, rs );
77                         return rs->sr_err;
78                 }
79
80                 if ( META_BACK_DEFER_ROOTDN_BIND( mi ) ) {
81                         rs->sr_err = LDAP_SUCCESS;
82                         rs->sr_text = NULL;
83                         /* frontend will return success */
84                         return rs->sr_err;
85                 }
86
87                 isroot = 1;
88         }
89
90         /* we need meta_back_getconn() not send result even on error,
91          * because we want to intercept the error and make it
92          * invalidCredentials */
93         mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_DONTSEND );
94         if ( !mc ) {
95                 char    buf[ SLAP_TEXT_BUFLEN ];
96
97                 snprintf( buf, sizeof( buf ),
98                         "meta_back_bind: no target "
99                         "for dn \"%s\" (%d%s%s).",
100                         op->o_req_dn.bv_val, rs->sr_err,
101                         rs->sr_text ? ". " : "",
102                         rs->sr_text ? rs->sr_text : "" );
103                 Debug( LDAP_DEBUG_ANY,
104                         "%s %s\n",
105                         op->o_log_prefix, buf, 0 );
106
107                 /* FIXME: there might be cases where we don't want
108                  * to map the error onto invalidCredentials */
109                 switch ( rs->sr_err ) {
110                 case LDAP_NO_SUCH_OBJECT:
111                 case LDAP_UNWILLING_TO_PERFORM:
112                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
113                         rs->sr_text = NULL;
114                         break;
115                 }
116                 send_ldap_result( op, rs );
117                 return rs->sr_err;
118         }
119
120         /*
121          * Each target is scanned ...
122          */
123         mc->mc_authz_target = META_BOUND_NONE;
124         for ( i = 0; i < mi->mi_ntargets; i++ ) {
125                 int             lerr;
126                 Operation       op2 = *op;
127
128                 /*
129                  * Skip non-candidates
130                  */
131                 if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
132                         continue;
133                 }
134
135                 if ( gotit == 0 ) {
136                         /* set rc to LDAP_SUCCESS only if at least
137                          * one candidate has been tried */
138                         rc = LDAP_SUCCESS;
139                         gotit = 1;
140
141                 } else if ( isroot == 0 ) {
142                         /*
143                          * A bind operation is expected to have
144                          * ONE CANDIDATE ONLY!
145                          */
146                         Debug( LDAP_DEBUG_ANY,
147                                 "### %s meta_back_bind: more than one"
148                                 " candidate is trying to bind...\n",
149                                 op->o_log_prefix, 0, 0 );
150                 }
151
152                 if ( isroot ) {
153                         if ( BER_BVISNULL( &mi->mi_targets[ i ].mt_pseudorootdn ) )
154                         {
155                                 metasingleconn_t        *msc = &mc->mc_conns[ i ];
156
157                                 /* skip the target if no pseudorootdn is provided */
158                                 if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
159                                         ch_free( msc->msc_bound_ndn.bv_val );
160                                         BER_BVZERO( &msc->msc_bound_ndn );
161                                 }
162
163                                 if ( LDAP_BACK_SAVECRED( mi ) &&
164                                         !BER_BVISNULL( &msc->msc_cred ) )
165                                 {
166                                         /* destroy sensitive data */
167                                         memset( msc->msc_cred.bv_val, 0,
168                                                 msc->msc_cred.bv_len );
169                                         ch_free( msc->msc_cred.bv_val );
170                                         BER_BVZERO( &msc->msc_cred );
171                                 }
172
173                                 continue;
174                         }
175
176                         op2.o_req_dn = mi->mi_targets[ i ].mt_pseudorootdn;
177                         op2.o_req_ndn = mi->mi_targets[ i ].mt_pseudorootdn;
178                         op2.orb_cred = mi->mi_targets[ i ].mt_pseudorootpw;
179                         op2.orb_method = LDAP_AUTH_SIMPLE;
180                 }
181                 
182                 lerr = meta_back_single_bind( &op2, rs, mc, i );
183                 if ( lerr != LDAP_SUCCESS ) {
184                         rs->sr_err = lerr;
185                         candidates[ i ].sr_tag = META_NOT_CANDIDATE;
186
187                         rc = rs->sr_err;
188                         break;
189                 }
190         }
191
192         /* must re-insert if local DN changed as result of bind */
193         if ( rc == LDAP_SUCCESS ) {
194                 if ( isroot ) {
195                         mc->mc_authz_target = META_BOUND_ALL;
196                         ber_dupbv( &op->orb_edn, be_root_dn( op->o_bd ) );
197                 }
198
199                 if ( !dn_match( &op->o_req_ndn, &mc->mc_local_ndn ) ) {
200                         int             lerr;
201
202                         /* wait for all other ops to release the connection */
203 retry_lock:;
204                         ldap_pvt_thread_mutex_lock( &mi->mi_conn_mutex );
205                         if ( mc->mc_refcnt > 1 ) {
206                                 ldap_pvt_thread_mutex_unlock( &mi->mi_conn_mutex );
207                                 ldap_pvt_thread_yield();
208                                 goto retry_lock;
209                         }
210
211                         assert( mc->mc_refcnt == 1 );
212                         mc = avl_delete( &mi->mi_conntree, (caddr_t)mc,
213                                 meta_back_conn_cmp );
214                         assert( mc != NULL );
215
216                         ber_bvreplace( &mc->mc_local_ndn, &op->o_req_ndn );
217                         lerr = avl_insert( &mi->mi_conntree, (caddr_t)mc,
218                                 meta_back_conn_cmp, meta_back_conn_dup );
219                         ldap_pvt_thread_mutex_unlock( &mi->mi_conn_mutex );
220                         if ( lerr == -1 ) {
221                                 for ( i = 0; i < mi->mi_ntargets; ++i ) {
222                                         if ( mc->mc_conns[ i ].msc_ld != NULL ) {
223                                                 meta_clear_one_candidate( &mc->mc_conns[ i ] );
224                                         }
225                                 }
226
227                                 /* we can do this because mc_refcnt == 1 */
228                                 mc->mc_refcnt = 0;
229                                 meta_back_conn_free( mc );
230                                 mc = NULL;
231                         }
232                 }
233         }
234
235         if ( mc != NULL ) {
236                 meta_back_release_conn( op, mc );
237         }
238
239         /*
240          * rc is LDAP_SUCCESS if at least one bind succeeded,
241          * err is the last error that occurred during a bind;
242          * if at least (and at most?) one bind succeeds, fine.
243          */
244         if ( rc != LDAP_SUCCESS ) {
245                 
246                 /*
247                  * deal with bind failure ...
248                  */
249
250                 /*
251                  * no target was found within the naming context, 
252                  * so bind must fail with invalid credentials
253                  */
254                 if ( rs->sr_err == LDAP_SUCCESS && gotit == 0 ) {
255                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
256                 } else {
257                         rs->sr_err = slap_map_api2result( rs );
258                 }
259                 send_ldap_result( op, rs );
260                 return rs->sr_err;
261
262         }
263
264         return LDAP_SUCCESS;
265 }
266
267 /*
268  * meta_back_single_bind
269  *
270  * attempts to perform a bind with creds
271  */
272 static int
273 meta_back_single_bind(
274         Operation               *op,
275         SlapReply               *rs,
276         metaconn_t              *mc,
277         int                     candidate )
278 {
279         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
280         metatarget_t            *mt = &mi->mi_targets[ candidate ];
281         struct berval           mdn = BER_BVNULL;
282         dncookie                dc;
283         metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
284         int                     msgid,
285                                 rebinding = 0;
286
287         
288         if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
289                 ch_free( msc->msc_bound_ndn.bv_val );
290                 BER_BVZERO( &msc->msc_bound_ndn );
291         }
292
293         if ( LDAP_BACK_SAVECRED( mi ) && !BER_BVISNULL( &msc->msc_cred ) ) {
294                 /* destroy sensitive data */
295                 memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
296                 ch_free( msc->msc_cred.bv_val );
297                 BER_BVZERO( &msc->msc_cred );
298         }
299
300         /*
301          * Rewrite the bind dn if needed
302          */
303         dc.target = mt;
304         dc.conn = op->o_conn;
305         dc.rs = rs;
306         dc.ctx = "bindDN";
307
308         if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
309                 send_ldap_result( op, rs );
310                 return -1;
311         }
312
313         /* FIXME: this fixes the bind problem right now; we need
314          * to use the asynchronous version to get the "matched"
315          * and more in case of failure ... */
316         /* FIXME: should we check if at least some of the op->o_ctrls
317          * can/should be passed? */
318 rebind:;
319         rs->sr_err = ldap_sasl_bind( msc->msc_ld, mdn.bv_val,
320                         LDAP_SASL_SIMPLE, &op->orb_cred,
321                         op->o_ctrls, NULL, &msgid );
322         if ( rs->sr_err == LDAP_SUCCESS ) {
323                 LDAPMessage     *res;
324                 struct timeval  tv;
325                 int             rc;
326                 int             nretries = mt->mt_nretries;
327
328                 LDAP_BACK_TV_SET( &tv );
329
330                 /*
331                  * handle response!!!
332                  */
333 retry:;
334                 tv.tv_sec = 0;
335                 tv.tv_usec = META_BIND_TIMEOUT;
336                 switch ( ldap_result( msc->msc_ld, msgid, 0, &tv, &res ) ) {
337                 case 0:
338                         Debug( LDAP_DEBUG_ANY,
339                                 "%s meta_back_single_bind: "
340                                 "ldap_result=0 nretries=%d%s\n",
341                                 op->o_log_prefix, nretries,
342                                 rebinding ? " rebinding" : "" );
343
344                         if ( nretries != META_RETRY_NEVER ) {
345                                 ldap_pvt_thread_yield();
346                                 if ( nretries > 0 ) {
347                                         nretries--;
348                                 }
349                                 LDAP_BACK_TV_SET( &tv );
350                                 goto retry;
351                         }
352                         rs->sr_err = LDAP_BUSY;
353                         if ( rebinding ) {
354                                 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
355                                 break;
356                         }
357
358                         /* FIXME: some times the request times out
359                          * while the other party is not willing to
360                          * send a response any more.  Give it a second
361                          * chance with a freshly bound connection */
362                         rebinding = 1;
363                         nretries = mt->mt_nretries;
364                         /* fallthru */
365
366                 case -1:
367                         ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
368                                         &rs->sr_err );
369
370                         if ( rebinding ) {
371                                 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
372                         }
373
374                         Debug( LDAP_DEBUG_ANY,
375                                 "### %s meta_back_single_bind: "
376                                 "err=%d nretries=%d\n",
377                                 op->o_log_prefix, rs->sr_err, nretries );
378
379                         rc = slap_map_api2result( rs );
380                         if ( rs->sr_err == LDAP_UNAVAILABLE && nretries != META_RETRY_NEVER ) {
381                                 ldap_pvt_thread_mutex_lock( &mi->mi_conn_mutex );
382                                 if ( mc->mc_refcnt == 1 ) {
383                                         ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
384                                         msc->msc_ld = NULL;
385                                         LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
386
387                                         ( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn );
388
389                                         /* mc here must be the regular mc,
390                                          * reset and ready for init */
391                                         rc = meta_back_init_one_conn( op, rs,
392                                                 mt, mc, msc, LDAP_BACK_CONN_ISPRIV( mc ),
393                                                 candidate == mc->mc_authz_target,
394                                                 LDAP_BACK_DONTSEND );
395
396                                 } else {
397                                         /* can't do anything about it */
398                                         rc = 0;
399                                 }
400
401                                 ldap_pvt_thread_mutex_unlock( &mi->mi_conn_mutex );
402
403                                 if ( rc ) {
404                                         if ( nretries > 0 ) {
405                                                 nretries--;
406                                         }
407                                         ldap_pvt_thread_yield();
408                                         goto rebind;
409                                 }
410                         }
411                         break;
412
413                 default:
414                         rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
415                                         NULL, NULL, NULL, NULL, 1 );
416                         if ( rc != LDAP_SUCCESS ) {
417                                 rs->sr_err = rc;
418                         }
419                         break;
420                 }
421         }
422
423         if ( rs->sr_err != LDAP_SUCCESS ) {
424                 rs->sr_err = slap_map_api2result( rs );
425                 goto return_results;
426         }
427
428         ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_dn );
429         LDAP_BACK_CONN_ISBOUND_SET( msc );
430         mc->mc_authz_target = candidate;
431
432         if ( LDAP_BACK_SAVECRED( mi ) ) {
433                 ber_bvreplace( &msc->msc_cred, &op->orb_cred );
434                 ldap_set_rebind_proc( msc->msc_ld, meta_back_rebind_f, msc );
435         }
436
437         if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED
438                         && op->o_req_ndn.bv_len != 0 )
439         {
440                 ( void )meta_dncache_update_entry( &mi->mi_cache,
441                                 &op->o_req_ndn, candidate );
442         }
443
444 return_results:;
445         if ( mdn.bv_val != op->o_req_dn.bv_val ) {
446                 free( mdn.bv_val );
447         }
448
449         return rs->sr_err;
450 }
451
452 /*
453  * meta_back_single_dobind
454  */
455 int
456 meta_back_single_dobind(
457         Operation               *op,
458         SlapReply               *rs,
459         metaconn_t              *mc,
460         int                     candidate,
461         ldap_back_send_t        sendok,
462         int                     nretries,
463         int                     dolock )
464 {
465         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
466         metatarget_t            *mt = &mi->mi_targets[ candidate ];
467         metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
468         int                     rc;
469         static struct berval    cred = BER_BVC( "" );
470         int                     msgid,
471                                 rebinding = 0,
472                                 save_nretries = nretries;
473
474         assert( !LDAP_BACK_CONN_ISBOUND( msc ) );
475
476         /*
477          * Otherwise an anonymous bind is performed
478          * (note: if the target was already bound, the anonymous
479          * bind clears the previous bind).
480          */
481         if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
482                 ber_memfree( msc->msc_bound_ndn.bv_val );
483                 BER_BVZERO( &msc->msc_bound_ndn );
484         }
485                 
486         if ( LDAP_BACK_SAVECRED( mi ) && !BER_BVISNULL( &msc->msc_cred ) ) {
487                 /* destroy sensitive data */
488                 memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
489                 ber_memfree( msc->msc_cred.bv_val );
490                 BER_BVZERO( &msc->msc_cred );
491         }
492
493         /* FIXME: should we check if at least some of the op->o_ctrls
494          * can/should be passed? */
495 rebind:;
496         rc = ldap_sasl_bind( msc->msc_ld, "", LDAP_SASL_SIMPLE, &cred,
497                         NULL, NULL, &msgid );
498         if ( rc == LDAP_SUCCESS ) {
499                 LDAPMessage     *res;
500                 struct timeval  tv;
501
502                 LDAP_BACK_TV_SET( &tv );
503
504                 /*
505                  * handle response!!!
506                  */
507 retry:;
508                 tv.tv_sec = 0;
509                 tv.tv_usec = META_BIND_TIMEOUT;
510                 switch ( ldap_result( msc->msc_ld, msgid, 0, &tv, &res ) ) {
511                 case 0:
512                         Debug( LDAP_DEBUG_ANY,
513                                 "%s meta_back_single_dobind: "
514                                 "ldap_result=0 nretries=%d%s\n",
515                                 op->o_log_prefix, nretries,
516                                 rebinding ? " rebinding" : "" );
517
518                         if ( nretries != META_RETRY_NEVER ) {
519                                 ldap_pvt_thread_yield();
520                                 if ( nretries > 0 ) {
521                                         nretries--;
522                                 }
523                                 LDAP_BACK_TV_SET( &tv );
524                                 goto retry;
525                         }
526
527                         rc = LDAP_BUSY;
528                         if ( rebinding ) {
529                                 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
530                                 break;
531                         }
532
533                         /* FIXME: some times the request times out
534                          * while the other party is not willing to
535                          * send a response any more.  Give it a second
536                          * chance with a freshly bound connection */
537                         rebinding = 1;
538                         nretries = save_nretries;
539                         /* fallthru */
540
541                 case -1:
542                         ldap_get_option( msc->msc_ld,
543                                         LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
544
545                         if ( rebinding ) {
546                                 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
547                         }
548
549                         Debug( LDAP_DEBUG_ANY,
550                                 "### %s meta_back_single_dobind: "
551                                 "err=%d nretries=%d\n",
552                                 op->o_log_prefix, rs->sr_err, nretries );
553
554                         rc = slap_map_api2result( rs );
555                         if ( rc == LDAP_UNAVAILABLE && nretries != META_RETRY_NEVER ) {
556                                 if ( dolock ) {
557                                         ldap_pvt_thread_mutex_lock( &mi->mi_conn_mutex );
558                                 }
559
560                                 if ( mc->mc_refcnt == 1 ) {
561                                         ldap_unbind_ext_s( msc->msc_ld, NULL, NULL );
562                                         msc->msc_ld = NULL;
563                                         LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
564
565                                         ( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn );
566
567                                         /* mc here must be the regular mc,
568                                          * reset and ready for init */
569                                         rc = meta_back_init_one_conn( op, rs,
570                                                 mt, mc, msc,
571                                                 LDAP_BACK_CONN_ISPRIV( mc ),
572                                                 candidate == mc->mc_authz_target,
573                                                 LDAP_BACK_DONTSEND );
574                                 
575
576                                 } else {
577                                         /* can't do anything about it */
578                                         rc = LDAP_UNAVAILABLE;
579                                 }
580
581                                 if ( dolock ) {
582                                         ldap_pvt_thread_mutex_unlock( &mi->mi_conn_mutex );
583                                 }
584
585                                 if ( rc == LDAP_SUCCESS ) {
586                                         ldap_pvt_thread_yield();
587                                         if ( nretries > 0 ) {
588                                                 nretries--;
589                                         }
590                                         goto rebind;
591                                 }
592                         }
593                         break;
594
595                 default:
596                         rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
597                                         NULL, NULL, NULL, NULL, 1 );
598                         if ( rc == LDAP_SUCCESS ) {
599                                 rc = slap_map_api2result( rs );
600                         }
601                         break;
602                 }
603
604         } else {
605                 rs->sr_err = rc;
606                 rc = slap_map_api2result( rs );
607         }
608
609         rs->sr_err = rc;
610         if ( rc != LDAP_SUCCESS && ( sendok & LDAP_BACK_SENDERR ) ) {
611                 send_ldap_result( op, rs );
612         }
613
614         return rc;
615 }
616
617 /*
618  * meta_back_dobind
619  */
620 int
621 meta_back_dobind(
622         Operation               *op,
623         SlapReply               *rs,
624         metaconn_t              *mc,
625         ldap_back_send_t        sendok )
626 {
627         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
628
629         int                     bound = 0,
630                                 i,
631                                 isroot = 0;
632
633         SlapReply               *candidates = meta_back_candidates_get( op );
634
635         if ( be_isroot( op ) ) {
636                 isroot = 1;
637         }
638
639         Debug( LDAP_DEBUG_TRACE,
640                 "%s meta_back_dobind: conn=%ld%s\n",
641                 op->o_log_prefix,
642                 LDAP_BACK_PCONN_ID( mc->mc_conn ),
643                 isroot ? " (isroot)" : "" );
644
645         ldap_pvt_thread_mutex_lock( &mc->mc_mutex );
646
647         /*
648          * all the targets are bound as pseudoroot
649          */
650         if ( mc->mc_authz_target == META_BOUND_ALL ) {
651                 bound = 1;
652                 goto done;
653         }
654
655         for ( i = 0; i < mi->mi_ntargets; i++ ) {
656                 metatarget_t            *mt = &mi->mi_targets[ i ];
657                 metasingleconn_t        *msc = &mc->mc_conns[ i ];
658                 int                     rc;
659                 char                    *rootdn = NULL;
660
661                 /*
662                  * Not a candidate
663                  */
664                 if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
665                         continue;
666                 }
667
668                 assert( msc->msc_ld != NULL );
669
670                 /*
671                  * If the target is already bound it is skipped
672                  */
673                 if ( LDAP_BACK_CONN_ISBOUND( msc ) || LDAP_BACK_CONN_ISANON( msc ) ) {
674                         ++bound;
675                         continue;
676                 }
677
678                 if ( isroot && !BER_BVISNULL( &mi->mi_targets[ i ].mt_pseudorootdn ) )
679                 {
680                         Operation       op2 = *op;
681
682                         op2.o_tag = LDAP_REQ_BIND;
683                         op2.o_req_dn = mi->mi_targets[ i ].mt_pseudorootdn;
684                         op2.o_req_ndn = mi->mi_targets[ i ].mt_pseudorootdn;
685                         op2.orb_cred = mi->mi_targets[ i ].mt_pseudorootpw;
686                         op2.orb_method = LDAP_AUTH_SIMPLE;
687
688                         rootdn = mi->mi_targets[ i ].mt_pseudorootdn.bv_val;
689
690                         rc = meta_back_single_bind( &op2, rs, mc, i );
691
692                 } else {
693                         rc = meta_back_single_dobind( op, rs, mc, i,
694                                 LDAP_BACK_DONTSEND, mt->mt_nretries, 1 );
695                 }
696
697                 if ( rc != LDAP_SUCCESS ) {
698                         char            buf[ SLAP_TEXT_BUFLEN ];
699
700                         snprintf( buf, sizeof( buf ),
701                                 "meta_back_dobind[%d]: (%s) err=%d.",
702                                 i, rootdn ? rootdn : "anonymous", rc );
703                         Debug( LDAP_DEBUG_ANY,
704                                 "%s %s\n",
705                                 op->o_log_prefix, buf, 0 );
706
707                         /*
708                          * null cred bind should always succeed
709                          * as anonymous, so a failure means
710                          * the target is no longer candidate possibly
711                          * due to technical reasons (remote host down?)
712                          * so better clear the handle
713                          */
714                         /* leave the target candidate, but record the error for later use */
715                         candidates[ i ].sr_err = rc;
716                         if ( META_BACK_ONERR_STOP( mi ) ) {
717                                 bound = 0;
718                                 goto done;
719                         }
720                         continue;
721                 } /* else */
722                 
723                 Debug( LDAP_DEBUG_TRACE,
724                         "%s meta_back_dobind[%d]: "
725                         "(%s)\n",
726                         op->o_log_prefix, i,
727                         rootdn ? rootdn : "anonymous" );
728
729                 if ( rootdn ) {
730                         LDAP_BACK_CONN_ISBOUND_SET( msc );
731                 } else {
732                         LDAP_BACK_CONN_ISANON_SET( msc );
733                 }
734                 ++bound;
735         }
736
737 done:;
738         ldap_pvt_thread_mutex_unlock( &mc->mc_mutex );
739
740         Debug( LDAP_DEBUG_TRACE,
741                 "%s meta_back_dobind: conn=%ld bound=%d\n",
742                 op->o_log_prefix, LDAP_BACK_PCONN_ID( mc->mc_conn ), bound );
743
744         if ( bound == 0 ) {
745                 meta_back_release_conn( op, mc );
746
747                 if ( sendok & LDAP_BACK_SENDERR ) {
748                         if ( rs->sr_err == LDAP_SUCCESS ) {
749                                 rs->sr_err = LDAP_BUSY;
750                         }
751                         send_ldap_result( op, rs );
752                 }
753         }
754
755         return( bound > 0 );
756 }
757
758 /*
759  * meta_back_default_rebind
760  *
761  * This is a callback used for chasing referrals using the same
762  * credentials as the original user on this session.
763  */
764 static int 
765 meta_back_default_rebind(
766         LDAP                    *ld,
767         LDAP_CONST char         *url,
768         ber_tag_t               request,
769         ber_int_t               msgid,
770         void                    *params )
771 {
772         metasingleconn_t        *msc = ( metasingleconn_t * )params;
773
774         return ldap_sasl_bind_s( ld, msc->msc_bound_ndn.bv_val,
775                         LDAP_SASL_SIMPLE, &msc->msc_cred,
776                         NULL, NULL, NULL );
777 }
778
779 /*
780  * FIXME: error return must be handled in a cleaner way ...
781  */
782 int
783 meta_back_op_result(
784         metaconn_t      *mc,
785         Operation       *op,
786         SlapReply       *rs,
787         int             candidate )
788 {
789         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
790
791         int                     i,
792                                 rerr = LDAP_SUCCESS;
793         char                    *rmsg = NULL,
794                                 *rmatch = NULL;
795         const char              *save_rmsg = NULL,
796                                 *save_rmatch = NULL;
797         void                    *rmatch_ctx = NULL;
798
799         if ( candidate != META_TARGET_NONE ) {
800                 metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
801
802                 rs->sr_err = LDAP_SUCCESS;
803
804                 ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
805                 if ( rs->sr_err != LDAP_SUCCESS ) {
806                         /*
807                          * better check the type of error. In some cases
808                          * (search ?) it might be better to return a
809                          * success if at least one of the targets gave
810                          * positive result ...
811                          */
812                         ldap_get_option( msc->msc_ld,
813                                         LDAP_OPT_ERROR_STRING, &rmsg );
814                         if ( rmsg != NULL && rmsg[ 0 ] == '\0' ) {
815                                 ldap_memfree( rmsg );
816                                 rmsg = NULL;
817                         }
818
819                         ldap_get_option( msc->msc_ld,
820                                         LDAP_OPT_MATCHED_DN, &rmatch );
821                         if ( rmatch != NULL && rmatch[ 0 ] == '\0' ) {
822                                 ldap_memfree( rmatch );
823                                 rmatch = NULL;
824                         }
825
826                         rerr = rs->sr_err = slap_map_api2result( rs );
827
828                         Debug(LDAP_DEBUG_ANY,
829                                         "==> meta_back_op_result: target"
830                                         " <%d> sending msg \"%s\""
831                                         " (matched \"%s\")\n", 
832                                         candidate, ( rmsg ? rmsg : "" ),
833                                         ( rmatch ? rmatch : "" ) );
834                 }
835
836         } else {
837                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
838                         metasingleconn_t        *msc = &mc->mc_conns[ i ];
839                         char                    *msg = NULL;
840                         char                    *match = NULL;
841
842                         rs->sr_err = LDAP_SUCCESS;
843
844                         ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
845                         if ( rs->sr_err != LDAP_SUCCESS ) {
846                                 /*
847                                  * better check the type of error. In some cases
848                                  * (search ?) it might be better to return a
849                                  * success if at least one of the targets gave
850                                  * positive result ...
851                                  */
852                                 ldap_get_option( msc->msc_ld,
853                                                 LDAP_OPT_ERROR_STRING, &msg );
854                                 if ( msg != NULL && msg[ 0 ] == '\0' ) {
855                                         ldap_memfree( msg );
856                                         msg = NULL;
857                                 }
858
859                                 ldap_get_option( msc->msc_ld,
860                                                 LDAP_OPT_MATCHED_DN, &match );
861                                 if ( match != NULL && match[ 0 ] == '\0' ) {
862                                         ldap_memfree( match );
863                                         match = NULL;
864                                 }
865
866                                 rs->sr_err = slap_map_api2result( rs );
867         
868                                 Debug(LDAP_DEBUG_ANY,
869                                                 "==> meta_back_op_result: target"
870                                                 " <%d> sending msg \"%s\""
871                                                 " (matched \"%s\")\n", 
872                                                 i, ( msg ? msg : "" ),
873                                                 ( match ? match : "" ) );
874         
875                                 /*
876                                  * FIXME: need to rewrite "match" (need rwinfo)
877                                  */
878                                 switch ( rs->sr_err ) {
879                                 default:
880                                         rerr = rs->sr_err;
881                                         if ( msg != NULL ) {
882                                                 if ( rmsg ) {
883                                                         ldap_memfree( rmsg );
884                                                 }
885                                                 rmsg = msg;
886                                                 msg = NULL;
887                                         }
888                                         if ( match != NULL ) {
889                                                 if ( rmatch ) {
890                                                         ldap_memfree( rmatch );
891                                                 }
892                                                 rmatch = match;
893                                                 match = NULL;
894                                         }
895                                         break;
896                                 }
897
898                                 if ( msg ) {
899                                         ldap_memfree( msg );
900                                 }
901         
902                                 if ( match ) {
903                                         ldap_memfree( match );
904                                 }
905                         }
906                 }
907         }
908         
909         rs->sr_err = rerr;
910         if ( rmsg != NULL ) {
911                 save_rmsg = rs->sr_text;
912                 rs->sr_text = rmsg;
913         }
914         if ( rmatch != NULL ) {
915                 struct berval   dn, pdn;
916
917                 ber_str2bv( rmatch, 0, 0, &dn );
918                 if ( dnPretty( NULL, &dn, &pdn, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
919                         ldap_memfree( rmatch );
920                         rmatch_ctx = op->o_tmpmemctx;
921                         rmatch = pdn.bv_val;
922                 }
923                 save_rmatch = rs->sr_matched;
924                 rs->sr_matched = rmatch;
925         }
926         send_ldap_result( op, rs );
927         if ( rmsg != NULL ) {
928                 ber_memfree( rmsg );
929                 rs->sr_text = save_rmsg;
930         }
931         if ( rmatch != NULL ) {
932                 ber_memfree_x( rmatch, rmatch_ctx );
933                 rs->sr_matched = save_rmatch;
934         }
935
936         return ( ( rerr == LDAP_SUCCESS ) ? 0 : -1 );
937 }
938