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