]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/bind.c
various cleanup
[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                                         meta_clear_one_candidate( msc );
384                                         LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
385
386                                         ( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn );
387
388                                         /* mc here must be the regular mc,
389                                          * reset and ready for init */
390                                         rc = meta_back_init_one_conn( op, rs,
391                                                 mt, mc, msc, LDAP_BACK_CONN_ISPRIV( mc ),
392                                                 candidate == mc->mc_authz_target,
393                                                 LDAP_BACK_DONTSEND );
394
395                                 } else {
396                                         /* can't do anything about it */
397                                         rc = 0;
398                                 }
399
400                                 ldap_pvt_thread_mutex_unlock( &mi->mi_conn_mutex );
401
402                                 if ( rc ) {
403                                         if ( nretries > 0 ) {
404                                                 nretries--;
405                                         }
406                                         ldap_pvt_thread_yield();
407                                         goto rebind;
408                                 }
409                         }
410                         break;
411
412                 default:
413                         rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
414                                         NULL, NULL, NULL, NULL, 1 );
415                         if ( rc != LDAP_SUCCESS ) {
416                                 rs->sr_err = rc;
417                         }
418                         break;
419                 }
420         }
421
422         if ( rs->sr_err != LDAP_SUCCESS ) {
423                 rs->sr_err = slap_map_api2result( rs );
424                 goto return_results;
425         }
426
427         ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_dn );
428         LDAP_BACK_CONN_ISBOUND_SET( msc );
429         mc->mc_authz_target = candidate;
430
431         if ( LDAP_BACK_SAVECRED( mi ) ) {
432                 ber_bvreplace( &msc->msc_cred, &op->orb_cred );
433                 ldap_set_rebind_proc( msc->msc_ld, meta_back_rebind_f, msc );
434         }
435
436         if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED
437                         && op->o_req_ndn.bv_len != 0 )
438         {
439                 ( void )meta_dncache_update_entry( &mi->mi_cache,
440                                 &op->o_req_ndn, candidate );
441         }
442
443 return_results:;
444         if ( mdn.bv_val != op->o_req_dn.bv_val ) {
445                 free( mdn.bv_val );
446         }
447
448         return rs->sr_err;
449 }
450
451 /*
452  * meta_back_single_dobind
453  */
454 int
455 meta_back_single_dobind(
456         Operation               *op,
457         SlapReply               *rs,
458         metaconn_t              *mc,
459         int                     candidate,
460         ldap_back_send_t        sendok,
461         int                     nretries,
462         int                     dolock )
463 {
464         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
465         metatarget_t            *mt = &mi->mi_targets[ candidate ];
466         metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
467         int                     rc;
468         static struct berval    cred = BER_BVC( "" );
469         int                     msgid,
470                                 rebinding = 0,
471                                 save_nretries = nretries;
472
473         assert( !LDAP_BACK_CONN_ISBOUND( msc ) );
474
475         /*
476          * Otherwise an anonymous bind is performed
477          * (note: if the target was already bound, the anonymous
478          * bind clears the previous bind).
479          */
480         if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
481                 ber_memfree( msc->msc_bound_ndn.bv_val );
482                 BER_BVZERO( &msc->msc_bound_ndn );
483         }
484                 
485         if ( LDAP_BACK_SAVECRED( mi ) && !BER_BVISNULL( &msc->msc_cred ) ) {
486                 /* destroy sensitive data */
487                 memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
488                 ber_memfree( msc->msc_cred.bv_val );
489                 BER_BVZERO( &msc->msc_cred );
490         }
491
492         /* FIXME: should we check if at least some of the op->o_ctrls
493          * can/should be passed? */
494 rebind:;
495         rc = ldap_sasl_bind( msc->msc_ld, "", LDAP_SASL_SIMPLE, &cred,
496                         NULL, NULL, &msgid );
497         if ( rc == LDAP_SUCCESS ) {
498                 LDAPMessage     *res;
499                 struct timeval  tv;
500
501                 LDAP_BACK_TV_SET( &tv );
502
503                 /*
504                  * handle response!!!
505                  */
506 retry:;
507                 tv.tv_sec = 0;
508                 tv.tv_usec = META_BIND_TIMEOUT;
509                 switch ( ldap_result( msc->msc_ld, msgid, 0, &tv, &res ) ) {
510                 case 0:
511                         Debug( LDAP_DEBUG_ANY,
512                                 "%s meta_back_single_dobind: "
513                                 "ldap_result=0 nretries=%d%s\n",
514                                 op->o_log_prefix, nretries,
515                                 rebinding ? " rebinding" : "" );
516
517                         if ( nretries != META_RETRY_NEVER ) {
518                                 ldap_pvt_thread_yield();
519                                 if ( nretries > 0 ) {
520                                         nretries--;
521                                 }
522                                 LDAP_BACK_TV_SET( &tv );
523                                 goto retry;
524                         }
525
526                         rc = LDAP_BUSY;
527                         if ( rebinding ) {
528                                 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
529                                 break;
530                         }
531
532                         /* FIXME: some times the request times out
533                          * while the other party is not willing to
534                          * send a response any more.  Give it a second
535                          * chance with a freshly bound connection */
536                         rebinding = 1;
537                         nretries = save_nretries;
538                         /* fallthru */
539
540                 case -1:
541                         ldap_get_option( msc->msc_ld,
542                                         LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
543
544                         if ( rebinding ) {
545                                 ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
546                         }
547
548                         Debug( LDAP_DEBUG_ANY,
549                                 "### %s meta_back_single_dobind: "
550                                 "err=%d nretries=%d\n",
551                                 op->o_log_prefix, rs->sr_err, nretries );
552
553                         rc = slap_map_api2result( rs );
554                         if ( rc == LDAP_UNAVAILABLE && nretries != META_RETRY_NEVER ) {
555                                 if ( dolock ) {
556                                         ldap_pvt_thread_mutex_lock( &mi->mi_conn_mutex );
557                                 }
558
559                                 if ( mc->mc_refcnt == 1 ) {
560                                         meta_clear_one_candidate( msc );
561                                         LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
562
563                                         ( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn );
564
565                                         /* mc here must be the regular mc,
566                                          * reset and ready for init */
567                                         rc = meta_back_init_one_conn( op, rs,
568                                                 mt, mc, msc,
569                                                 LDAP_BACK_CONN_ISPRIV( mc ),
570                                                 candidate == mc->mc_authz_target,
571                                                 LDAP_BACK_DONTSEND );
572                                 
573
574                                 } else {
575                                         /* can't do anything about it */
576                                         rc = LDAP_UNAVAILABLE;
577                                 }
578
579                                 if ( dolock ) {
580                                         ldap_pvt_thread_mutex_unlock( &mi->mi_conn_mutex );
581                                 }
582
583                                 if ( rc == LDAP_SUCCESS ) {
584                                         ldap_pvt_thread_yield();
585                                         if ( nretries > 0 ) {
586                                                 nretries--;
587                                         }
588                                         goto rebind;
589                                 }
590                         }
591                         break;
592
593                 default:
594                         rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
595                                         NULL, NULL, NULL, NULL, 1 );
596                         if ( rc == LDAP_SUCCESS ) {
597                                 rc = slap_map_api2result( rs );
598                         }
599                         break;
600                 }
601
602         } else {
603                 rs->sr_err = rc;
604                 rc = slap_map_api2result( rs );
605         }
606
607         rs->sr_err = rc;
608         if ( rc != LDAP_SUCCESS && ( sendok & LDAP_BACK_SENDERR ) ) {
609                 send_ldap_result( op, rs );
610         }
611
612         return rc;
613 }
614
615 /*
616  * meta_back_dobind
617  */
618 int
619 meta_back_dobind(
620         Operation               *op,
621         SlapReply               *rs,
622         metaconn_t              *mc,
623         ldap_back_send_t        sendok )
624 {
625         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
626
627         int                     bound = 0,
628                                 i,
629                                 isroot = 0;
630
631         SlapReply               *candidates = meta_back_candidates_get( op );
632
633         if ( be_isroot( op ) ) {
634                 isroot = 1;
635         }
636
637         Debug( LDAP_DEBUG_TRACE,
638                 "%s meta_back_dobind: conn=%ld%s\n",
639                 op->o_log_prefix,
640                 LDAP_BACK_PCONN_ID( mc->mc_conn ),
641                 isroot ? " (isroot)" : "" );
642
643         ldap_pvt_thread_mutex_lock( &mc->mc_mutex );
644
645         /*
646          * all the targets are bound as pseudoroot
647          */
648         if ( mc->mc_authz_target == META_BOUND_ALL ) {
649                 bound = 1;
650                 goto done;
651         }
652
653         for ( i = 0; i < mi->mi_ntargets; i++ ) {
654                 metatarget_t            *mt = &mi->mi_targets[ i ];
655                 metasingleconn_t        *msc = &mc->mc_conns[ i ];
656                 int                     rc;
657                 char                    *rootdn = NULL;
658
659                 /*
660                  * Not a candidate
661                  */
662                 if ( candidates[ i ].sr_tag != META_CANDIDATE ) {
663                         continue;
664                 }
665
666                 assert( msc->msc_ld != NULL );
667
668                 /*
669                  * If the target is already bound it is skipped
670                  */
671                 if ( LDAP_BACK_CONN_ISBOUND( msc ) || LDAP_BACK_CONN_ISANON( msc ) ) {
672                         ++bound;
673                         continue;
674                 }
675
676                 if ( isroot && !BER_BVISNULL( &mi->mi_targets[ i ].mt_pseudorootdn ) )
677                 {
678                         Operation       op2 = *op;
679
680                         op2.o_tag = LDAP_REQ_BIND;
681                         op2.o_req_dn = mi->mi_targets[ i ].mt_pseudorootdn;
682                         op2.o_req_ndn = mi->mi_targets[ i ].mt_pseudorootdn;
683                         op2.orb_cred = mi->mi_targets[ i ].mt_pseudorootpw;
684                         op2.orb_method = LDAP_AUTH_SIMPLE;
685
686                         rootdn = mi->mi_targets[ i ].mt_pseudorootdn.bv_val;
687
688                         rc = meta_back_single_bind( &op2, rs, mc, i );
689
690                 } else {
691                         rc = meta_back_single_dobind( op, rs, mc, i,
692                                 LDAP_BACK_DONTSEND, mt->mt_nretries, 1 );
693                 }
694
695                 if ( rc != LDAP_SUCCESS ) {
696                         char            buf[ SLAP_TEXT_BUFLEN ];
697
698                         snprintf( buf, sizeof( buf ),
699                                 "meta_back_dobind[%d]: (%s) err=%d.",
700                                 i, rootdn ? rootdn : "anonymous", rc );
701                         Debug( LDAP_DEBUG_ANY,
702                                 "%s %s\n",
703                                 op->o_log_prefix, buf, 0 );
704
705                         /*
706                          * null cred bind should always succeed
707                          * as anonymous, so a failure means
708                          * the target is no longer candidate possibly
709                          * due to technical reasons (remote host down?)
710                          * so better clear the handle
711                          */
712                         /* leave the target candidate, but record the error for later use */
713                         candidates[ i ].sr_err = rc;
714                         if ( META_BACK_ONERR_STOP( mi ) ) {
715                                 bound = 0;
716                                 goto done;
717                         }
718                         continue;
719                 } /* else */
720                 
721                 Debug( LDAP_DEBUG_TRACE,
722                         "%s meta_back_dobind[%d]: "
723                         "(%s)\n",
724                         op->o_log_prefix, i,
725                         rootdn ? rootdn : "anonymous" );
726
727                 if ( rootdn ) {
728                         LDAP_BACK_CONN_ISBOUND_SET( msc );
729                 } else {
730                         LDAP_BACK_CONN_ISANON_SET( msc );
731                 }
732                 ++bound;
733         }
734
735 done:;
736         ldap_pvt_thread_mutex_unlock( &mc->mc_mutex );
737
738         Debug( LDAP_DEBUG_TRACE,
739                 "%s meta_back_dobind: conn=%ld bound=%d\n",
740                 op->o_log_prefix, LDAP_BACK_PCONN_ID( mc->mc_conn ), bound );
741
742         if ( bound == 0 ) {
743                 meta_back_release_conn( op, mc );
744
745                 if ( sendok & LDAP_BACK_SENDERR ) {
746                         if ( rs->sr_err == LDAP_SUCCESS ) {
747                                 rs->sr_err = LDAP_BUSY;
748                         }
749                         send_ldap_result( op, rs );
750                 }
751         }
752
753         return( bound > 0 );
754 }
755
756 /*
757  * meta_back_default_rebind
758  *
759  * This is a callback used for chasing referrals using the same
760  * credentials as the original user on this session.
761  */
762 static int 
763 meta_back_default_rebind(
764         LDAP                    *ld,
765         LDAP_CONST char         *url,
766         ber_tag_t               request,
767         ber_int_t               msgid,
768         void                    *params )
769 {
770         metasingleconn_t        *msc = ( metasingleconn_t * )params;
771
772         return ldap_sasl_bind_s( ld, msc->msc_bound_ndn.bv_val,
773                         LDAP_SASL_SIMPLE, &msc->msc_cred,
774                         NULL, NULL, NULL );
775 }
776
777 /*
778  * FIXME: error return must be handled in a cleaner way ...
779  */
780 int
781 meta_back_op_result(
782         metaconn_t      *mc,
783         Operation       *op,
784         SlapReply       *rs,
785         int             candidate )
786 {
787         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
788
789         int                     i,
790                                 rerr = LDAP_SUCCESS;
791         char                    *rmsg = NULL,
792                                 *rmatch = NULL;
793         const char              *save_rmsg = NULL,
794                                 *save_rmatch = NULL;
795         void                    *rmatch_ctx = NULL;
796
797         if ( candidate != META_TARGET_NONE ) {
798                 metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
799
800                 rs->sr_err = LDAP_SUCCESS;
801
802                 ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
803                 if ( rs->sr_err != LDAP_SUCCESS ) {
804                         /*
805                          * better check the type of error. In some cases
806                          * (search ?) it might be better to return a
807                          * success if at least one of the targets gave
808                          * positive result ...
809                          */
810                         ldap_get_option( msc->msc_ld,
811                                         LDAP_OPT_ERROR_STRING, &rmsg );
812                         if ( rmsg != NULL && rmsg[ 0 ] == '\0' ) {
813                                 ldap_memfree( rmsg );
814                                 rmsg = NULL;
815                         }
816
817                         ldap_get_option( msc->msc_ld,
818                                         LDAP_OPT_MATCHED_DN, &rmatch );
819                         if ( rmatch != NULL && rmatch[ 0 ] == '\0' ) {
820                                 ldap_memfree( rmatch );
821                                 rmatch = NULL;
822                         }
823
824                         rerr = rs->sr_err = slap_map_api2result( rs );
825
826                         Debug(LDAP_DEBUG_ANY,
827                                         "==> meta_back_op_result: target"
828                                         " <%d> sending msg \"%s\""
829                                         " (matched \"%s\")\n", 
830                                         candidate, ( rmsg ? rmsg : "" ),
831                                         ( rmatch ? rmatch : "" ) );
832                 }
833
834         } else {
835                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
836                         metasingleconn_t        *msc = &mc->mc_conns[ i ];
837                         char                    *msg = NULL;
838                         char                    *match = NULL;
839
840                         rs->sr_err = LDAP_SUCCESS;
841
842                         ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
843                         if ( rs->sr_err != LDAP_SUCCESS ) {
844                                 /*
845                                  * better check the type of error. In some cases
846                                  * (search ?) it might be better to return a
847                                  * success if at least one of the targets gave
848                                  * positive result ...
849                                  */
850                                 ldap_get_option( msc->msc_ld,
851                                                 LDAP_OPT_ERROR_STRING, &msg );
852                                 if ( msg != NULL && msg[ 0 ] == '\0' ) {
853                                         ldap_memfree( msg );
854                                         msg = NULL;
855                                 }
856
857                                 ldap_get_option( msc->msc_ld,
858                                                 LDAP_OPT_MATCHED_DN, &match );
859                                 if ( match != NULL && match[ 0 ] == '\0' ) {
860                                         ldap_memfree( match );
861                                         match = NULL;
862                                 }
863
864                                 rs->sr_err = slap_map_api2result( rs );
865         
866                                 Debug(LDAP_DEBUG_ANY,
867                                                 "==> meta_back_op_result: target"
868                                                 " <%d> sending msg \"%s\""
869                                                 " (matched \"%s\")\n", 
870                                                 i, ( msg ? msg : "" ),
871                                                 ( match ? match : "" ) );
872         
873                                 /*
874                                  * FIXME: need to rewrite "match" (need rwinfo)
875                                  */
876                                 switch ( rs->sr_err ) {
877                                 default:
878                                         rerr = rs->sr_err;
879                                         if ( msg != NULL ) {
880                                                 if ( rmsg ) {
881                                                         ldap_memfree( rmsg );
882                                                 }
883                                                 rmsg = msg;
884                                                 msg = NULL;
885                                         }
886                                         if ( match != NULL ) {
887                                                 if ( rmatch ) {
888                                                         ldap_memfree( rmatch );
889                                                 }
890                                                 rmatch = match;
891                                                 match = NULL;
892                                         }
893                                         break;
894                                 }
895
896                                 if ( msg ) {
897                                         ldap_memfree( msg );
898                                 }
899         
900                                 if ( match ) {
901                                         ldap_memfree( match );
902                                 }
903                         }
904                 }
905         }
906         
907         rs->sr_err = rerr;
908         if ( rmsg != NULL ) {
909                 save_rmsg = rs->sr_text;
910                 rs->sr_text = rmsg;
911         }
912         if ( rmatch != NULL ) {
913                 struct berval   dn, pdn;
914
915                 ber_str2bv( rmatch, 0, 0, &dn );
916                 if ( dnPretty( NULL, &dn, &pdn, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
917                         ldap_memfree( rmatch );
918                         rmatch_ctx = op->o_tmpmemctx;
919                         rmatch = pdn.bv_val;
920                 }
921                 save_rmatch = rs->sr_matched;
922                 rs->sr_matched = rmatch;
923         }
924         send_ldap_result( op, rs );
925         if ( rmsg != NULL ) {
926                 ber_memfree( rmsg );
927                 rs->sr_text = save_rmsg;
928         }
929         if ( rmatch != NULL ) {
930                 ber_memfree_x( rmatch, rmatch_ctx );
931                 rs->sr_matched = save_rmatch;
932         }
933
934         return ( ( rerr == LDAP_SUCCESS ) ? 0 : -1 );
935 }
936