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