]> git.sur5r.net Git - openldap/blob - servers/slapd/back-asyncmeta/bind.c
ITS#8303 Asynchronous meta back-end for OpenLDAP
[openldap] / servers / slapd / back-asyncmeta / bind.c
1 /* bind.c - bind request handler functions for binding
2  * to remote targets for back-asyncmeta */
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 2016 The OpenLDAP Foundation.
7  * Portions Copyright 2016 Symas Corporation.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18
19 /* ACKNOWLEDGEMENTS:
20  * This work was developed by Symas Corporation
21  * based on back-meta module for inclusion in OpenLDAP Software.
22  * This work was sponsored by Ericsson. */
23
24 #include "portable.h"
25
26 #include <stdio.h>
27
28 #include <ac/errno.h>
29 #include <ac/socket.h>
30 #include <ac/string.h>
31
32
33 #define AVL_INTERNAL
34 #include "slap.h"
35 #include "../back-ldap/back-ldap.h"
36 #include "back-asyncmeta.h"
37
38 #include "lutil_ldap.h"
39
40 static int
41 asyncmeta_proxy_authz_bind(
42         a_metaconn_t            *mc,
43         int                     candidate,
44         Operation               *op,
45         SlapReply               *rs,
46         ldap_back_send_t        sendok,
47         int                     dolock );
48
49 static int
50 asyncmeta_single_bind(
51         Operation               *op,
52         SlapReply               *rs,
53         a_metaconn_t            *mc,
54         int                     candidate );
55
56 int
57 asyncmeta_back_bind( Operation *op, SlapReply *rs )
58 {
59         a_metainfo_t    *mi = ( a_metainfo_t * )op->o_bd->be_private;
60         a_metaconn_t    *mc = NULL;
61
62         int             rc = LDAP_OTHER,
63                         i,
64                         gotit = 0,
65                         isroot = 0;
66
67         SlapReply       *candidates;
68
69         candidates = op->o_tmpcalloc(mi->mi_ntargets, sizeof(SlapReply),op->o_tmpmemctx);
70         rs->sr_err = LDAP_SUCCESS;
71
72         Debug( LDAP_DEBUG_ARGS, "%s asyncmeta_back_bind: dn=\"%s\".\n",
73                 op->o_log_prefix, op->o_req_dn.bv_val, 0 );
74
75         /* the test on the bind method should be superfluous */
76         switch ( be_rootdn_bind( op, rs ) ) {
77         case LDAP_SUCCESS:
78                 if ( META_BACK_DEFER_ROOTDN_BIND( mi ) ) {
79                         /* frontend will return success */
80                         return rs->sr_err;
81                 }
82
83                 isroot = 1;
84                 /* fallthru */
85
86         case SLAP_CB_CONTINUE:
87                 break;
88
89         default:
90                 /* be_rootdn_bind() sent result */
91                 return rs->sr_err;
92         }
93
94         /* we need asyncmeta_getconn() not send result even on error,
95          * because we want to intercept the error and make it
96          * invalidCredentials */
97         mc = asyncmeta_getconn( op, rs, candidates, NULL, LDAP_BACK_BIND_DONTSEND, 1 );
98         if ( !mc ) {
99                 if ( LogTest( LDAP_DEBUG_ANY ) ) {
100                         char    buf[ SLAP_TEXT_BUFLEN ];
101
102                         snprintf( buf, sizeof( buf ),
103                                 "asyncmeta_back_bind: no target "
104                                 "for dn \"%s\" (%d%s%s).",
105                                 op->o_req_dn.bv_val, rs->sr_err,
106                                 rs->sr_text ? ". " : "",
107                                 rs->sr_text ? rs->sr_text : "" );
108                         Debug( LDAP_DEBUG_ANY,
109                                 "%s %s\n",
110                                 op->o_log_prefix, buf, 0 );
111                 }
112
113                 /* FIXME: there might be cases where we don't want
114                  * to map the error onto invalidCredentials */
115                 switch ( rs->sr_err ) {
116                 case LDAP_NO_SUCH_OBJECT:
117                 case LDAP_UNWILLING_TO_PERFORM:
118                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
119                         rs->sr_text = NULL;
120                         break;
121                 }
122                 send_ldap_result( op, rs );
123                 return rs->sr_err;
124         }
125
126         /*
127          * Each target is scanned ...
128          */
129         mc->mc_authz_target = META_BOUND_NONE;
130         for ( i = 0; i < mi->mi_ntargets; i++ ) {
131                 a_metatarget_t  *mt = mi->mi_targets[ i ];
132                 int             lerr;
133
134                 /*
135                  * Skip non-candidates
136                  */
137                 if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
138                         continue;
139                 }
140
141                 if ( gotit == 0 ) {
142                         /* set rc to LDAP_SUCCESS only if at least
143                          * one candidate has been tried */
144                         rc = LDAP_SUCCESS;
145                         gotit = 1;
146
147                 } else if ( !isroot ) {
148                         /*
149                          * A bind operation is expected to have
150                          * ONE CANDIDATE ONLY!
151                          */
152                         Debug( LDAP_DEBUG_ANY,
153                                 "### %s asyncmeta_back_bind: more than one"
154                                 " candidate selected...\n",
155                                 op->o_log_prefix, 0, 0 );
156                 }
157
158                 if ( isroot ) {
159                         if ( mt->mt_idassert_authmethod == LDAP_AUTH_NONE
160                                 || BER_BVISNULL( &mt->mt_idassert_authcDN ) )
161                         {
162                                 a_metasingleconn_t      *msc = &mc->mc_conns[ i ];
163
164                                 if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
165                                         ch_free( msc->msc_bound_ndn.bv_val );
166                                         BER_BVZERO( &msc->msc_bound_ndn );
167                                 }
168
169                                 if ( !BER_BVISNULL( &msc->msc_cred ) ) {
170                                         /* destroy sensitive data */
171                                         memset( msc->msc_cred.bv_val, 0,
172                                                 msc->msc_cred.bv_len );
173                                         ch_free( msc->msc_cred.bv_val );
174                                         BER_BVZERO( &msc->msc_cred );
175                                 }
176
177                                 continue;
178                         }
179
180
181                         (void)asyncmeta_proxy_authz_bind( mc, i, op, rs, LDAP_BACK_DONTSEND, 1 );
182                         lerr = rs->sr_err;
183
184                 } else {
185                         lerr = asyncmeta_single_bind( op, rs, mc, i );
186                 }
187
188                 if ( lerr != LDAP_SUCCESS ) {
189                         rc = rs->sr_err = lerr;
190
191                         /* FIXME: in some cases (e.g. unavailable)
192                          * do not assume it's not candidate; rather
193                          * mark this as an error to be eventually
194                          * reported to client */
195                         META_CANDIDATE_CLEAR( &candidates[ i ] );
196                         break;
197                 }
198         }
199
200         if ( mc != NULL ) {
201                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
202                         a_metasingleconn_t      *msc = &mc->mc_conns[ i ];
203                         if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
204                                 ch_free( msc->msc_bound_ndn.bv_val );
205                         }
206
207                         if ( !BER_BVISNULL( &msc->msc_cred ) ) {
208                                 /* destroy sensitive data */
209                                 memset( msc->msc_cred.bv_val, 0,
210                                         msc->msc_cred.bv_len );
211                                 ch_free( msc->msc_cred.bv_val );
212                         }
213                 }
214                 asyncmeta_back_conn_free( mc );
215         }
216
217         /*
218          * rc is LDAP_SUCCESS if at least one bind succeeded,
219          * err is the last error that occurred during a bind;
220          * if at least (and at most?) one bind succeeds, fine.
221          */
222         if ( rc != LDAP_SUCCESS ) {
223
224                 /*
225                  * deal with bind failure ...
226                  */
227
228                 /*
229                  * no target was found within the naming context,
230                  * so bind must fail with invalid credentials
231                  */
232                 if ( rs->sr_err == LDAP_SUCCESS && gotit == 0 ) {
233                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
234                 } else {
235                         rs->sr_err = slap_map_api2result( rs );
236                 }
237                 send_ldap_result( op, rs );
238                 return rs->sr_err;
239
240         }
241         return LDAP_SUCCESS;
242 }
243
244 static int
245 asyncmeta_bind_op_result(
246         Operation               *op,
247         SlapReply               *rs,
248         a_metaconn_t            *mc,
249         int                     candidate,
250         int                     msgid,
251         ldap_back_send_t        sendok,
252         int                     dolock )
253 {
254         a_metainfo_t            *mi = mc->mc_info;
255         a_metatarget_t          *mt = mi->mi_targets[ candidate ];
256         a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
257         LDAPMessage             *res;
258         struct timeval          tv;
259         int                     rc;
260         int                     nretries = mt->mt_nretries;
261         char                    buf[ SLAP_TEXT_BUFLEN ];
262
263         Debug( LDAP_DEBUG_TRACE,
264                 ">>> %s asyncmeta_bind_op_result[%d]\n",
265                 op->o_log_prefix, candidate, 0 );
266
267         /* make sure this is clean */
268         assert( rs->sr_ctrls == NULL );
269
270         if ( rs->sr_err == LDAP_SUCCESS ) {
271                 time_t          stoptime = (time_t)(-1),
272                                 timeout;
273                 int             timeout_err = op->o_protocol >= LDAP_VERSION3 ?
274                                 LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
275                 const char      *timeout_text = "Operation timed out";
276                 slap_op_t       opidx = slap_req2op( op->o_tag );
277
278                 /* since timeout is not specified, compute and use
279                  * the one specific to the ongoing operation */
280                 if ( opidx == LDAP_REQ_SEARCH ) {
281                         if ( op->ors_tlimit <= 0 ) {
282                                 timeout = 0;
283
284                         } else {
285                                 timeout = op->ors_tlimit;
286                                 timeout_err = LDAP_TIMELIMIT_EXCEEDED;
287                                 timeout_text = NULL;
288                         }
289
290                 } else {
291                         timeout = mt->mt_timeout[ opidx ];
292                 }
293
294                 /* better than nothing :) */
295                 if ( timeout == 0 ) {
296                         if ( mi->mi_idle_timeout ) {
297                                 timeout = mi->mi_idle_timeout;
298
299                         }
300                 }
301
302                 if ( timeout ) {
303                         stoptime = op->o_time + timeout;
304                 }
305
306                 LDAP_BACK_TV_SET( &tv );
307
308                 /*
309                  * handle response!!!
310                  */
311 retry:;
312                 rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
313                 switch ( rc ) {
314                 case 0:
315                         if ( nretries != META_RETRY_NEVER
316                                 || ( timeout && slap_get_time() <= stoptime ) )
317                         {
318                                 ldap_pvt_thread_yield();
319                                 if ( nretries > 0 ) {
320                                         nretries--;
321                                 }
322                                 tv = mt->mt_bind_timeout;
323                                 goto retry;
324                         }
325
326                         /* don't let anyone else use this handler,
327                          * because there's a pending bind that will not
328                          * be acknowledged */
329                         assert( LDAP_BACK_CONN_BINDING( msc ) );
330
331 #ifdef DEBUG_205
332                         Debug( LDAP_DEBUG_ANY, "### %s asyncmeta_bind_op_result ldap_unbind_ext[%d] ld=%p\n",
333                                 op->o_log_prefix, candidate, (void *)msc->msc_ld );
334 #endif /* DEBUG_205 */
335
336                         rs->sr_err = timeout_err;
337                         rs->sr_text = timeout_text;
338                         break;
339
340                 case -1:
341                         ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
342                                 &rs->sr_err );
343
344                         snprintf( buf, sizeof( buf ),
345                                 "err=%d (%s) nretries=%d",
346                                 rs->sr_err, ldap_err2string( rs->sr_err ), nretries );
347                         Debug( LDAP_DEBUG_ANY,
348                                 "### %s asyncmeta_bind_op_result[%d]: %s.\n",
349                                 op->o_log_prefix, candidate, buf );
350                         break;
351
352                 default:
353                         /* only touch when activity actually took place... */
354                         if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
355                                 msc->msc_time = op->o_time;
356                         }
357
358                         /* FIXME: matched? referrals? response controls? */
359                         rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
360                                         NULL, NULL, NULL, NULL, 1 );
361                         if ( rc != LDAP_SUCCESS ) {
362                                 rs->sr_err = rc;
363                         }
364                         rs->sr_err = slap_map_api2result( rs );
365                         break;
366                 }
367         }
368
369         rs->sr_err = slap_map_api2result( rs );
370         Debug( LDAP_DEBUG_TRACE,
371                 "<<< %s asyncmeta_bind_op_result[%d] err=%d\n",
372                 op->o_log_prefix, candidate, rs->sr_err );
373
374         return rs->sr_err;
375 }
376
377 /*
378  * asyncmeta_single_bind
379  *
380  * attempts to perform a bind with creds
381  */
382 static int
383 asyncmeta_single_bind(
384         Operation               *op,
385         SlapReply               *rs,
386         a_metaconn_t            *mc,
387         int                     candidate )
388 {
389         a_metainfo_t            *mi = mc->mc_info;
390         a_metatarget_t          *mt = mi->mi_targets[ candidate ];
391         struct berval           mdn = BER_BVNULL;
392         a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
393         int                     msgid;
394         a_dncookie              dc;
395         struct berval           save_o_dn;
396         int                     save_o_do_not_cache;
397         LDAPControl             **ctrls = NULL;
398
399         if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
400                 ch_free( msc->msc_bound_ndn.bv_val );
401                 BER_BVZERO( &msc->msc_bound_ndn );
402         }
403
404         if ( !BER_BVISNULL( &msc->msc_cred ) ) {
405                 /* destroy sensitive data */
406                 memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
407                 ch_free( msc->msc_cred.bv_val );
408                 BER_BVZERO( &msc->msc_cred );
409         }
410
411         /*
412          * Rewrite the bind dn if needed
413          */
414         dc.target = mt;
415         dc.conn = op->o_conn;
416         dc.rs = rs;
417         dc.ctx = "bindDN";
418
419         if ( asyncmeta_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
420                 rs->sr_text = "DN rewrite error";
421                 rs->sr_err = LDAP_OTHER;
422                 return rs->sr_err;
423         }
424
425         /* don't add proxyAuthz; set the bindDN */
426         save_o_dn = op->o_dn;
427         save_o_do_not_cache = op->o_do_not_cache;
428         op->o_do_not_cache = 1;
429         op->o_dn = op->o_req_dn;
430
431         ctrls = op->o_ctrls;
432         rs->sr_err = asyncmeta_controls_add( op, rs, mc, candidate, &ctrls );
433         op->o_dn = save_o_dn;
434         op->o_do_not_cache = save_o_do_not_cache;
435         if ( rs->sr_err != LDAP_SUCCESS ) {
436                 goto return_results;
437         }
438
439         /* FIXME: this fixes the bind problem right now; we need
440          * to use the asynchronous version to get the "matched"
441          * and more in case of failure ... */
442         /* FIXME: should we check if at least some of the op->o_ctrls
443          * can/should be passed? */
444         for (;;) {
445                 rs->sr_err = ldap_sasl_bind( msc->msc_ld, mdn.bv_val,
446                         LDAP_SASL_SIMPLE, &op->orb_cred,
447                         ctrls, NULL, &msgid );
448                 if ( rs->sr_err != LDAP_X_CONNECTING ) {
449                         break;
450                 }
451                 ldap_pvt_thread_yield();
452         }
453
454         mi->mi_ldap_extra->controls_free( op, rs, &ctrls );
455
456         asyncmeta_bind_op_result( op, rs, mc, candidate, msgid, LDAP_BACK_DONTSEND, 1 );
457         if ( rs->sr_err != LDAP_SUCCESS ) {
458                 goto return_results;
459         }
460
461         /* If defined, proxyAuthz will be used also when
462          * back-ldap is the authorizing backend; for this
463          * purpose, a successful bind is followed by a
464          * bind with the configured identity assertion */
465         /* NOTE: use with care */
466         if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
467                 asyncmeta_proxy_authz_bind( mc, candidate, op, rs, LDAP_BACK_SENDERR, 1 );
468                 if ( !LDAP_BACK_CONN_ISBOUND( msc ) ) {
469                         goto return_results;
470                 }
471                 goto cache_refresh;
472         }
473
474         ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_ndn );
475         LDAP_BACK_CONN_ISBOUND_SET( msc );
476         mc->mc_authz_target = candidate;
477
478         if ( META_BACK_TGT_SAVECRED( mt ) ) {
479                 if ( !BER_BVISNULL( &msc->msc_cred ) ) {
480                         memset( msc->msc_cred.bv_val, 0,
481                                 msc->msc_cred.bv_len );
482                 }
483                 ber_bvreplace( &msc->msc_cred, &op->orb_cred );
484                 ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
485         }
486
487 cache_refresh:;
488         if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED
489                         && !BER_BVISEMPTY( &op->o_req_ndn ) )
490         {
491                 ( void )asyncmeta_dncache_update_entry( &mi->mi_cache,
492                                 &op->o_req_ndn, candidate );
493         }
494
495 return_results:;
496         if ( mdn.bv_val != op->o_req_dn.bv_val ) {
497                 free( mdn.bv_val );
498         }
499
500         if ( META_BACK_TGT_QUARANTINE( mt ) ) {
501                 asyncmeta_quarantine( op, mi, rs, candidate );
502         }
503         ldap_unbind_ext( msc->msc_ld, NULL, NULL );
504         msc->msc_ld = NULL;
505         ldap_ld_free( msc->msc_ldr, 0, NULL, NULL );
506         msc->msc_ldr = NULL;
507         return rs->sr_err;
508 }
509
510 /*
511  * asyncmeta_back_single_dobind
512  */
513 int
514 asyncmeta_back_single_dobind(
515         Operation               *op,
516         SlapReply               *rs,
517         a_metaconn_t            **mcp,
518         int                     candidate,
519         ldap_back_send_t        sendok,
520         int                     nretries,
521         int                     dolock )
522 {
523         a_metaconn_t            *mc = *mcp;
524         a_metainfo_t            *mi = mc->mc_info;
525         a_metatarget_t          *mt = mi->mi_targets[ candidate ];
526         a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
527         int                     msgid;
528
529         assert( !LDAP_BACK_CONN_ISBOUND( msc ) );
530
531         if ( op->o_conn != NULL &&
532                 !op->o_do_not_cache &&
533                 ( BER_BVISNULL( &msc->msc_bound_ndn ) ||
534                         BER_BVISEMPTY( &msc->msc_bound_ndn ) ||
535                         ( LDAP_BACK_CONN_ISPRIV( mc ) && dn_match( &msc->msc_bound_ndn, &mt->mt_idassert_authcDN ) ) ||
536                         ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
537         {
538                 (void)asyncmeta_proxy_authz_bind( mc, candidate, op, rs, sendok, dolock );
539
540         } else {
541                 char *binddn = "";
542                 struct berval cred = BER_BVC( "" );
543
544                 /* use credentials if available */
545                 if ( !BER_BVISNULL( &msc->msc_bound_ndn )
546                         && !BER_BVISNULL( &msc->msc_cred ) )
547                 {
548                         binddn = msc->msc_bound_ndn.bv_val;
549                         cred = msc->msc_cred;
550                 }
551
552                 for (;;) {
553                         rs->sr_err = ldap_sasl_bind( msc->msc_ld,
554                                 binddn, LDAP_SASL_SIMPLE, &cred,
555                                 NULL, NULL, &msgid );
556                         if ( rs->sr_err != LDAP_X_CONNECTING ) {
557                                 break;
558                         }
559                         ldap_pvt_thread_yield();
560                 }
561
562                 rs->sr_err = asyncmeta_bind_op_result( op, rs, mc, candidate, msgid, sendok, dolock );
563
564                 /* if bind succeeded, but anonymous, clear msc_bound_ndn */
565                 if ( rs->sr_err != LDAP_SUCCESS || binddn[0] == '\0' ) {
566                         if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
567                                 ber_memfree( msc->msc_bound_ndn.bv_val );
568                                 BER_BVZERO( &msc->msc_bound_ndn );
569                         }
570
571                         if ( !BER_BVISNULL( &msc->msc_cred ) ) {
572                                 memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
573                                 ber_memfree( msc->msc_cred.bv_val );
574                                 BER_BVZERO( &msc->msc_cred );
575                         }
576                 }
577         }
578
579         if ( META_BACK_TGT_QUARANTINE( mt ) ) {
580                 asyncmeta_quarantine( op, mi, rs, candidate );
581         }
582
583         return rs->sr_err;
584 }
585
586 /*
587  * asyncmeta_back_default_rebind
588  *
589  * This is a callback used for chasing referrals using the same
590  * credentials as the original user on this session.
591  */
592 int
593 asyncmeta_back_default_rebind(
594         LDAP                    *ld,
595         LDAP_CONST char         *url,
596         ber_tag_t               request,
597         ber_int_t               msgid,
598         void                    *params )
599 {
600         a_metasingleconn_t      *msc = ( a_metasingleconn_t * )params;
601
602         return ldap_sasl_bind_s( ld, msc->msc_bound_ndn.bv_val,
603                         LDAP_SASL_SIMPLE, &msc->msc_cred,
604                         NULL, NULL, NULL );
605 }
606
607 /*
608  * meta_back_default_urllist
609  *
610  * This is a callback used for mucking with the urllist
611  */
612 int
613 asyncmeta_back_default_urllist(
614         LDAP            *ld,
615         LDAPURLDesc     **urllist,
616         LDAPURLDesc     **url,
617         void            *params )
618 {
619         a_metatarget_t  *mt = (a_metatarget_t *)params;
620         LDAPURLDesc     **urltail;
621
622         if ( urllist == url ) {
623                 return LDAP_SUCCESS;
624         }
625
626         for ( urltail = &(*url)->lud_next; *urltail; urltail = &(*urltail)->lud_next )
627                 /* count */ ;
628
629         *urltail = *urllist;
630         *urllist = *url;
631         *url = NULL;
632
633         ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
634         if ( mt->mt_uri ) {
635                 ch_free( mt->mt_uri );
636         }
637
638         ldap_get_option( ld, LDAP_OPT_URI, (void *)&mt->mt_uri );
639         ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
640
641         return LDAP_SUCCESS;
642 }
643
644 int
645 asyncmeta_back_cancel(
646         a_metaconn_t            *mc,
647         Operation               *op,
648         ber_int_t               msgid,
649         int                     candidate )
650 {
651
652         a_metainfo_t            *mi = mc->mc_info;
653         a_metatarget_t          *mt = mi->mi_targets[ candidate ];
654         a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
655
656         int                     rc = LDAP_OTHER;
657
658         Debug( LDAP_DEBUG_TRACE, ">>> %s asyncmeta_back_cancel[%d] msgid=%d\n",
659                 op->o_log_prefix, candidate, msgid );
660
661         if (msc->msc_ld == NULL) {
662                 Debug( LDAP_DEBUG_TRACE, ">>> %s asyncmeta_back_cancel[%d] msgid=%d\n already reset",
663                 op->o_log_prefix, candidate, msgid );
664                 return LDAP_SUCCESS;
665         }
666
667         /* default behavior */
668         if ( META_BACK_TGT_ABANDON( mt ) ) {
669                 rc = ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
670
671         } else if ( META_BACK_TGT_IGNORE( mt ) ) {
672                 rc = ldap_pvt_discard( msc->msc_ld, msgid );
673
674         } else if ( META_BACK_TGT_CANCEL( mt ) ) {
675                 rc = ldap_cancel_s( msc->msc_ld, msgid, NULL, NULL );
676
677         } else {
678                 assert( 0 );
679         }
680
681         Debug( LDAP_DEBUG_TRACE, "<<< %s asyncmeta_back_cancel[%d] err=%d\n",
682                 op->o_log_prefix, candidate, rc );
683
684         return rc;
685 }
686
687
688 int
689 asyncmeta_back_abandon_candidate(
690         a_metaconn_t            *mc,
691         Operation               *op,
692         ber_int_t               msgid,
693         int                     candidate )
694 {
695
696         a_metainfo_t            *mi = mc->mc_info;
697         a_metatarget_t          *mt = mi->mi_targets[ candidate ];
698         a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
699
700         int                     rc = LDAP_OTHER;
701
702         Debug( LDAP_DEBUG_TRACE, ">>> %s asyncmeta_back_abandon[%d] msgid=%d\n",
703                 op->o_log_prefix, candidate, msgid );
704
705         rc = ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
706
707         Debug( LDAP_DEBUG_TRACE, "<<< %s asyncmeta_back_abandon[%d] err=%d\n",
708                 op->o_log_prefix, candidate, rc );
709
710         return rc;
711 }
712
713 int
714 asyncmeta_back_cancel_msc(
715         Operation               *op,
716         SlapReply               *rs,
717         ber_int_t               msgid,
718         a_metasingleconn_t      *msc,
719         int                     candidate,
720         ldap_back_send_t        sendok )
721 {
722         a_metainfo_t            *mi = (a_metainfo_t *)op->o_bd->be_private;
723
724         a_metatarget_t          *mt = mi->mi_targets[ candidate ];
725
726         int                     rc = LDAP_OTHER;
727
728         Debug( LDAP_DEBUG_TRACE, ">>> %s asyncmeta_back_cancel_msc[%d] msgid=%d\n",
729                 op->o_log_prefix, candidate, msgid );
730
731         /* default behavior */
732         if ( META_BACK_TGT_ABANDON( mt ) ) {
733                 rc = ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
734
735         } else if ( META_BACK_TGT_IGNORE( mt ) ) {
736                 rc = ldap_pvt_discard( msc->msc_ld, msgid );
737
738         } else if ( META_BACK_TGT_CANCEL( mt ) ) {
739                 rc = ldap_cancel_s( msc->msc_ld, msgid, NULL, NULL );
740
741         } else {
742                 assert( 0 );
743         }
744
745         Debug( LDAP_DEBUG_TRACE, "<<< %s asyncmeta_back_cancel_msc[%d] err=%d\n",
746                 op->o_log_prefix, candidate, rc );
747
748         return rc;
749 }
750
751 /*
752  * FIXME: error return must be handled in a cleaner way ...
753  */
754 int
755 asyncmeta_back_op_result(
756         a_metaconn_t            *mc,
757         Operation               *op,
758         SlapReply               *rs,
759         int                     candidate,
760         ber_int_t               msgid,
761         time_t                  timeout,
762         ldap_back_send_t        sendok )
763 {
764         a_metainfo_t    *mi = mc->mc_info;
765
766         const char      *save_text = rs->sr_text,
767                         *save_matched = rs->sr_matched;
768         BerVarray       save_ref = rs->sr_ref;
769         LDAPControl     **save_ctrls = rs->sr_ctrls;
770         void            *matched_ctx = NULL;
771
772         char            *matched = NULL;
773         char            *text = NULL;
774         char            **refs = NULL;
775         LDAPControl     **ctrls = NULL;
776
777         assert( mc != NULL );
778
779         rs->sr_text = NULL;
780         rs->sr_matched = NULL;
781         rs->sr_ref = NULL;
782         rs->sr_ctrls = NULL;
783
784         if ( candidate != META_TARGET_NONE ) {
785                 a_metatarget_t          *mt = mi->mi_targets[ candidate ];
786                 a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
787
788                 if ( LDAP_ERR_OK( rs->sr_err ) ) {
789                         int             rc;
790                         struct timeval  tv;
791                         LDAPMessage     *res = NULL;
792                         time_t          stoptime = (time_t)(-1);
793                         int             timeout_err = op->o_protocol >= LDAP_VERSION3 ?
794                                                 LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
795                         const char      *timeout_text = "Operation timed out";
796
797                         /* if timeout is not specified, compute and use
798                          * the one specific to the ongoing operation */
799                         if ( timeout == (time_t)(-1) ) {
800                                 slap_op_t       opidx = slap_req2op( op->o_tag );
801
802                                 if ( opidx == SLAP_OP_SEARCH ) {
803                                         if ( op->ors_tlimit <= 0 ) {
804                                                 timeout = 0;
805
806                                         } else {
807                                                 timeout = op->ors_tlimit;
808                                                 timeout_err = LDAP_TIMELIMIT_EXCEEDED;
809                                                 timeout_text = NULL;
810                                         }
811
812                                 } else {
813                                         timeout = mt->mt_timeout[ opidx ];
814                                 }
815                         }
816
817                         /* better than nothing :) */
818                         if ( timeout == 0 ) {
819                                 if ( mi->mi_idle_timeout ) {
820                                         timeout = mi->mi_idle_timeout;
821
822                                 }
823                         }
824
825                         if ( timeout ) {
826                                 stoptime = op->o_time + timeout;
827                         }
828
829                         LDAP_BACK_TV_SET( &tv );
830
831 retry:;
832                         rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
833                         switch ( rc ) {
834                         case 0:
835                                 if ( timeout && slap_get_time() > stoptime ) {
836                                         (void)asyncmeta_back_cancel( mc, op, msgid, candidate );
837                                         rs->sr_err = timeout_err;
838                                         rs->sr_text = timeout_text;
839                                         break;
840                                 }
841
842                                 LDAP_BACK_TV_SET( &tv );
843                                 ldap_pvt_thread_yield();
844                                 goto retry;
845
846                         case -1:
847                                 ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE,
848                                                 &rs->sr_err );
849                                 break;
850
851
852                         /* otherwise get the result; if it is not
853                          * LDAP_SUCCESS, record it in the reply
854                          * structure (this includes
855                          * LDAP_COMPARE_{TRUE|FALSE}) */
856                         default:
857                                 /* only touch when activity actually took place... */
858                                 if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
859                                         msc->msc_time = op->o_time;
860                                 }
861
862                                 rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
863                                                 &matched, &text, &refs, &ctrls, 1 );
864                                 res = NULL;
865                                 if ( rc == LDAP_SUCCESS ) {
866                                         rs->sr_text = text;
867                                 } else {
868                                         rs->sr_err = rc;
869                                 }
870                                 rs->sr_err = slap_map_api2result( rs );
871
872                                 /* RFC 4511: referrals can only appear
873                                  * if result code is LDAP_REFERRAL */
874                                 if ( refs != NULL
875                                         && refs[ 0 ] != NULL
876                                         && refs[ 0 ][ 0 ] != '\0' )
877                                 {
878                                         if ( rs->sr_err != LDAP_REFERRAL ) {
879                                                 Debug( LDAP_DEBUG_ANY,
880                                                         "%s asyncmeta_back_op_result[%d]: "
881                                                         "got referrals with err=%d\n",
882                                                         op->o_log_prefix,
883                                                         candidate, rs->sr_err );
884
885                                         } else {
886                                                 int     i;
887
888                                                 for ( i = 0; refs[ i ] != NULL; i++ )
889                                                         /* count */ ;
890                                                 rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( i + 1 ),
891                                                         op->o_tmpmemctx );
892                                                 for ( i = 0; refs[ i ] != NULL; i++ ) {
893                                                         ber_str2bv( refs[ i ], 0, 0, &rs->sr_ref[ i ] );
894                                                 }
895                                                 BER_BVZERO( &rs->sr_ref[ i ] );
896                                         }
897
898                                 } else if ( rs->sr_err == LDAP_REFERRAL ) {
899                                         Debug( LDAP_DEBUG_ANY,
900                                                 "%s asyncmeta_back_op_result[%d]: "
901                                                 "got err=%d with null "
902                                                 "or empty referrals\n",
903                                                 op->o_log_prefix,
904                                                 candidate, rs->sr_err );
905
906                                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
907                                 }
908
909                                 if ( ctrls != NULL ) {
910                                         rs->sr_ctrls = ctrls;
911                                 }
912                         }
913
914                         assert( res == NULL );
915                 }
916
917                 /* if the error in the reply structure is not
918                  * LDAP_SUCCESS, try to map it from client
919                  * to server error */
920                 if ( !LDAP_ERR_OK( rs->sr_err ) ) {
921                         rs->sr_err = slap_map_api2result( rs );
922
923                         /* internal ops ( op->o_conn == NULL )
924                          * must not reply to client */
925                         if ( op->o_conn && !op->o_do_not_cache && matched ) {
926
927                                 /* record the (massaged) matched
928                                  * DN into the reply structure */
929                                 rs->sr_matched = matched;
930                         }
931                 }
932
933                 if ( META_BACK_TGT_QUARANTINE( mt ) ) {
934                         asyncmeta_quarantine( op, mi, rs, candidate );
935                 }
936
937         } else {
938                 int     i,
939                         err = rs->sr_err;
940
941                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
942                         a_metasingleconn_t      *msc = &mc->mc_conns[ i ];
943                         char                    *xtext = NULL;
944                         char                    *xmatched = NULL;
945
946                         if ( msc->msc_ld == NULL ) {
947                                 continue;
948                         }
949
950                         rs->sr_err = LDAP_SUCCESS;
951
952                         ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE, &rs->sr_err );
953                         if ( rs->sr_err != LDAP_SUCCESS ) {
954                                 /*
955                                  * better check the type of error. In some cases
956                                  * (search ?) it might be better to return a
957                                  * success if at least one of the targets gave
958                                  * positive result ...
959                                  */
960                                 ldap_get_option( msc->msc_ld,
961                                                 LDAP_OPT_DIAGNOSTIC_MESSAGE, &xtext );
962                                 if ( xtext != NULL && xtext [ 0 ] == '\0' ) {
963                                         ldap_memfree( xtext );
964                                         xtext = NULL;
965                                 }
966
967                                 ldap_get_option( msc->msc_ld,
968                                                 LDAP_OPT_MATCHED_DN, &xmatched );
969                                 if ( xmatched != NULL && xmatched[ 0 ] == '\0' ) {
970                                         ldap_memfree( xmatched );
971                                         xmatched = NULL;
972                                 }
973
974                                 rs->sr_err = slap_map_api2result( rs );
975
976                                 if ( LogTest( LDAP_DEBUG_ANY ) ) {
977                                         char    buf[ SLAP_TEXT_BUFLEN ];
978
979                                         snprintf( buf, sizeof( buf ),
980                                                 "asyncmeta_back_op_result[%d] "
981                                                 "err=%d text=\"%s\" matched=\"%s\"",
982                                                 i, rs->sr_err,
983                                                 ( xtext ? xtext : "" ),
984                                                 ( xmatched ? xmatched : "" ) );
985                                         Debug( LDAP_DEBUG_ANY, "%s %s.\n",
986                                                 op->o_log_prefix, buf, 0 );
987                                 }
988
989                                 /*
990                                  * FIXME: need to rewrite "match" (need rwinfo)
991                                  */
992                                 switch ( rs->sr_err ) {
993                                 default:
994                                         err = rs->sr_err;
995                                         if ( xtext != NULL ) {
996                                                 if ( text ) {
997                                                         ldap_memfree( text );
998                                                 }
999                                                 text = xtext;
1000                                                 xtext = NULL;
1001                                         }
1002                                         if ( xmatched != NULL ) {
1003                                                 if ( matched ) {
1004                                                         ldap_memfree( matched );
1005                                                 }
1006                                                 matched = xmatched;
1007                                                 xmatched = NULL;
1008                                         }
1009                                         break;
1010                                 }
1011
1012                                 if ( xtext ) {
1013                                         ldap_memfree( xtext );
1014                                 }
1015
1016                                 if ( xmatched ) {
1017                                         ldap_memfree( xmatched );
1018                                 }
1019                         }
1020
1021                         if ( META_BACK_TGT_QUARANTINE( mi->mi_targets[ i ] ) ) {
1022                                 asyncmeta_quarantine( op, mi, rs, i );
1023                         }
1024                 }
1025
1026                 if ( err != LDAP_SUCCESS ) {
1027                         rs->sr_err = err;
1028                 }
1029         }
1030
1031         if ( matched != NULL ) {
1032                 struct berval   dn, pdn;
1033
1034                 ber_str2bv( matched, 0, 0, &dn );
1035                 if ( dnPretty( NULL, &dn, &pdn, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
1036                         ldap_memfree( matched );
1037                         matched_ctx = op->o_tmpmemctx;
1038                         matched = pdn.bv_val;
1039                 }
1040                 rs->sr_matched = matched;
1041         }
1042
1043         if ( rs->sr_err == LDAP_UNAVAILABLE ) {
1044                 if ( !( sendok & LDAP_BACK_RETRYING ) ) {
1045                         if ( op->o_conn && ( sendok & LDAP_BACK_SENDERR ) ) {
1046                                 if ( rs->sr_text == NULL ) rs->sr_text = "Proxy operation retry failed";
1047                                 send_ldap_result( op, rs );
1048                         }
1049                 }
1050
1051         } else if ( op->o_conn &&
1052                 ( ( ( sendok & LDAP_BACK_SENDOK ) && LDAP_ERR_OK( rs->sr_err ) )
1053                         || ( ( sendok & LDAP_BACK_SENDERR ) && !LDAP_ERR_OK( rs->sr_err ) ) ) )
1054         {
1055                 send_ldap_result( op, rs );
1056         }
1057         if ( matched ) {
1058                 op->o_tmpfree( (char *)rs->sr_matched, matched_ctx );
1059         }
1060         if ( text ) {
1061                 ldap_memfree( text );
1062         }
1063         if ( rs->sr_ref ) {
1064                 op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
1065                 rs->sr_ref = NULL;
1066         }
1067         if ( refs ) {
1068                 ber_memvfree( (void **)refs );
1069         }
1070         if ( ctrls ) {
1071                 assert( rs->sr_ctrls != NULL );
1072                 ldap_controls_free( ctrls );
1073         }
1074
1075         rs->sr_text = save_text;
1076         rs->sr_matched = save_matched;
1077         rs->sr_ref = save_ref;
1078         rs->sr_ctrls = save_ctrls;
1079
1080         return( LDAP_ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err );
1081 }
1082
1083 /*
1084  * meta_back_proxy_authz_cred()
1085  *
1086  * prepares credentials & method for meta_back_proxy_authz_bind();
1087  * or, if method is SASL, performs the SASL bind directly.
1088  */
1089 int
1090 asyncmeta_back_proxy_authz_cred(
1091         a_metaconn_t            *mc,
1092         int                     candidate,
1093         Operation               *op,
1094         SlapReply               *rs,
1095         ldap_back_send_t        sendok,
1096         struct berval           *binddn,
1097         struct berval           *bindcred,
1098         int                     *method )
1099 {
1100         a_metainfo_t            *mi = mc->mc_info;
1101         a_metatarget_t          *mt = mi->mi_targets[ candidate ];
1102         a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
1103         struct berval           ndn;
1104         int                     dobind = 0;
1105
1106         /* don't proxyAuthz if protocol is not LDAPv3 */
1107         switch ( mt->mt_version ) {
1108         case LDAP_VERSION3:
1109                 break;
1110
1111         case 0:
1112                 if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
1113                         break;
1114                 }
1115                 /* fall thru */
1116
1117         default:
1118                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1119                 if ( sendok & LDAP_BACK_SENDERR ) {
1120                         send_ldap_result( op, rs );
1121                 }
1122                 LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
1123                 goto done;
1124         }
1125
1126         if ( op->o_tag == LDAP_REQ_BIND ) {
1127                 ndn = op->o_req_ndn;
1128
1129         } else if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
1130                 ndn = op->o_conn->c_ndn;
1131
1132         } else {
1133                 ndn = op->o_ndn;
1134         }
1135         rs->sr_err = LDAP_SUCCESS;
1136
1137         /*
1138          * FIXME: we need to let clients use proxyAuthz
1139          * otherwise we cannot do symmetric pools of servers;
1140          * we have to live with the fact that a user can
1141          * authorize itself as any ID that is allowed
1142          * by the authzTo directive of the "proxyauthzdn".
1143          */
1144         /*
1145          * NOTE: current Proxy Authorization specification
1146          * and implementation do not allow proxy authorization
1147          * control to be provided with Bind requests
1148          */
1149         /*
1150          * if no bind took place yet, but the connection is bound
1151          * and the "proxyauthzdn" is set, then bind as
1152          * "proxyauthzdn" and explicitly add the proxyAuthz
1153          * control to every operation with the dn bound
1154          * to the connection as control value.
1155          */
1156
1157         /* bind as proxyauthzdn only if no idassert mode
1158          * is requested, or if the client's identity
1159          * is authorized */
1160         switch ( mt->mt_idassert_mode ) {
1161         case LDAP_BACK_IDASSERT_LEGACY:
1162                 if ( !BER_BVISNULL( &ndn ) && !BER_BVISEMPTY( &ndn ) ) {
1163                         if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) && !BER_BVISEMPTY( &mt->mt_idassert_authcDN ) )
1164                         {
1165                                 *binddn = mt->mt_idassert_authcDN;
1166                                 *bindcred = mt->mt_idassert_passwd;
1167                                 dobind = 1;
1168                         }
1169                 }
1170                 break;
1171
1172         default:
1173                 /* NOTE: rootdn can always idassert */
1174                 if ( BER_BVISNULL( &ndn )
1175                         && mt->mt_idassert_authz == NULL
1176                         && !( mt->mt_idassert_flags & LDAP_BACK_AUTH_AUTHZ_ALL ) )
1177                 {
1178                         if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
1179                                 rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
1180                                 if ( sendok & LDAP_BACK_SENDERR ) {
1181                                         send_ldap_result( op, rs );
1182                                 }
1183                                 LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
1184                                 goto done;
1185
1186                         }
1187
1188                         rs->sr_err = LDAP_SUCCESS;
1189                         *binddn = slap_empty_bv;
1190                         *bindcred = slap_empty_bv;
1191                         break;
1192
1193                 } else if ( mt->mt_idassert_authz && !be_isroot( op ) ) {
1194                         struct berval authcDN;
1195
1196                         if ( BER_BVISNULL( &ndn ) ) {
1197                                 authcDN = slap_empty_bv;
1198
1199                         } else {
1200                                 authcDN = ndn;
1201                         }
1202                         rs->sr_err = slap_sasl_matches( op, mt->mt_idassert_authz,
1203                                         &authcDN, &authcDN );
1204                         if ( rs->sr_err != LDAP_SUCCESS ) {
1205                                 if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
1206                                         if ( sendok & LDAP_BACK_SENDERR ) {
1207                                                 send_ldap_result( op, rs );
1208                                         }
1209                                         LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
1210                                         goto done;
1211                                 }
1212
1213                                 rs->sr_err = LDAP_SUCCESS;
1214                                 *binddn = slap_empty_bv;
1215                                 *bindcred = slap_empty_bv;
1216                                 break;
1217                         }
1218                 }
1219
1220                 *binddn = mt->mt_idassert_authcDN;
1221                 *bindcred = mt->mt_idassert_passwd;
1222                 dobind = 1;
1223                 break;
1224         }
1225
1226         if ( dobind && mt->mt_idassert_authmethod == LDAP_AUTH_SASL ) {
1227 #ifdef HAVE_CYRUS_SASL
1228                 void            *defaults = NULL;
1229                 struct berval   authzID = BER_BVNULL;
1230                 int             freeauthz = 0;
1231
1232                 /* if SASL supports native authz, prepare for it */
1233                 if ( ( !op->o_do_not_cache || !op->o_is_auth_check ) &&
1234                                 ( mt->mt_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
1235                 {
1236                         switch ( mt->mt_idassert_mode ) {
1237                         case LDAP_BACK_IDASSERT_OTHERID:
1238                         case LDAP_BACK_IDASSERT_OTHERDN:
1239                                 authzID = mt->mt_idassert_authzID;
1240                                 break;
1241
1242                         case LDAP_BACK_IDASSERT_ANONYMOUS:
1243                                 BER_BVSTR( &authzID, "dn:" );
1244                                 break;
1245
1246                         case LDAP_BACK_IDASSERT_SELF:
1247                                 if ( BER_BVISNULL( &ndn ) ) {
1248                                         /* connection is not authc'd, so don't idassert */
1249                                         BER_BVSTR( &authzID, "dn:" );
1250                                         break;
1251                                 }
1252                                 authzID.bv_len = STRLENOF( "dn:" ) + ndn.bv_len;
1253                                 authzID.bv_val = slap_sl_malloc( authzID.bv_len + 1, op->o_tmpmemctx );
1254                                 AC_MEMCPY( authzID.bv_val, "dn:", STRLENOF( "dn:" ) );
1255                                 AC_MEMCPY( authzID.bv_val + STRLENOF( "dn:" ),
1256                                                 ndn.bv_val, ndn.bv_len + 1 );
1257                                 freeauthz = 1;
1258                                 break;
1259
1260                         default:
1261                                 break;
1262                         }
1263                 }
1264
1265                 if ( mt->mt_idassert_secprops != NULL ) {
1266                         rs->sr_err = ldap_set_option( msc->msc_ld,
1267                                 LDAP_OPT_X_SASL_SECPROPS,
1268                                 (void *)mt->mt_idassert_secprops );
1269
1270                         if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
1271                                 rs->sr_err = LDAP_OTHER;
1272                                 if ( sendok & LDAP_BACK_SENDERR ) {
1273                                         send_ldap_result( op, rs );
1274                                 }
1275                                 LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
1276                                 goto done;
1277                         }
1278                 }
1279
1280                 defaults = lutil_sasl_defaults( msc->msc_ld,
1281                                 mt->mt_idassert_sasl_mech.bv_val,
1282                                 mt->mt_idassert_sasl_realm.bv_val,
1283                                 mt->mt_idassert_authcID.bv_val,
1284                                 mt->mt_idassert_passwd.bv_val,
1285                                 authzID.bv_val );
1286                 if ( defaults == NULL ) {
1287                         rs->sr_err = LDAP_OTHER;
1288                         LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
1289                         if ( sendok & LDAP_BACK_SENDERR ) {
1290                                 send_ldap_result( op, rs );
1291                         }
1292                         goto done;
1293                 }
1294
1295                 rs->sr_err = ldap_sasl_interactive_bind_s( msc->msc_ld, binddn->bv_val,
1296                                 mt->mt_idassert_sasl_mech.bv_val, NULL, NULL,
1297                                 LDAP_SASL_QUIET, lutil_sasl_interact,
1298                                 defaults );
1299
1300                 rs->sr_err = slap_map_api2result( rs );
1301                 if ( rs->sr_err != LDAP_SUCCESS ) {
1302                         LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
1303                         if ( sendok & LDAP_BACK_SENDERR ) {
1304                                 send_ldap_result( op, rs );
1305                         }
1306
1307                 } else {
1308                         LDAP_BACK_CONN_ISBOUND_SET( msc );
1309                 }
1310
1311                 lutil_sasl_freedefs( defaults );
1312                 if ( freeauthz ) {
1313                         slap_sl_free( authzID.bv_val, op->o_tmpmemctx );
1314                 }
1315
1316                 goto done;
1317 #endif /* HAVE_CYRUS_SASL */
1318         }
1319
1320         *method = mt->mt_idassert_authmethod;
1321         switch ( mt->mt_idassert_authmethod ) {
1322         case LDAP_AUTH_NONE:
1323                 BER_BVSTR( binddn, "" );
1324                 BER_BVSTR( bindcred, "" );
1325                 /* fallthru */
1326
1327         case LDAP_AUTH_SIMPLE:
1328                 break;
1329
1330         default:
1331                 /* unsupported! */
1332                 LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
1333                 rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
1334                 if ( sendok & LDAP_BACK_SENDERR ) {
1335                         send_ldap_result( op, rs );
1336                 }
1337                 break;
1338         }
1339
1340 done:;
1341
1342         if ( !BER_BVISEMPTY( binddn ) ) {
1343                 LDAP_BACK_CONN_ISIDASSERT_SET( msc );
1344         }
1345
1346         return rs->sr_err;
1347 }
1348
1349 static int
1350 asyncmeta_proxy_authz_bind(
1351         a_metaconn_t *mc,
1352         int candidate,
1353         Operation *op,
1354         SlapReply *rs,
1355         ldap_back_send_t sendok,
1356         int dolock )
1357 {
1358         a_metainfo_t            *mi = mc->mc_info;
1359         a_metatarget_t          *mt = mi->mi_targets[ candidate ];
1360         a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
1361         struct berval           binddn = BER_BVC( "" ),
1362                                 cred = BER_BVC( "" );
1363         int                     method = LDAP_AUTH_NONE,
1364                                 rc;
1365
1366         rc = asyncmeta_back_proxy_authz_cred( mc, candidate, op, rs, sendok, &binddn, &cred, &method );
1367         if ( rc == LDAP_SUCCESS && !LDAP_BACK_CONN_ISBOUND( msc ) ) {
1368                 int     msgid;
1369
1370                 switch ( method ) {
1371                 case LDAP_AUTH_NONE:
1372                 case LDAP_AUTH_SIMPLE:
1373                         for (;;) {
1374                                 rs->sr_err = ldap_sasl_bind( msc->msc_ld,
1375                                         binddn.bv_val, LDAP_SASL_SIMPLE,
1376                                         &cred, NULL, NULL, &msgid );
1377                                 if ( rs->sr_err != LDAP_X_CONNECTING ) {
1378                                         break;
1379                                 }
1380                                 ldap_pvt_thread_yield();
1381                         }
1382
1383                         rc = asyncmeta_bind_op_result( op, rs, mc, candidate, msgid, sendok, dolock );
1384                         if ( rc == LDAP_SUCCESS ) {
1385                                 /* set rebind stuff in case of successful proxyAuthz bind,
1386                                  * so that referral chasing is attempted using the right
1387                                  * identity */
1388                                 LDAP_BACK_CONN_ISBOUND_SET( msc );
1389                                 ber_bvreplace( &msc->msc_bound_ndn, &binddn );
1390
1391                                 if ( META_BACK_TGT_SAVECRED( mt ) ) {
1392                                         if ( !BER_BVISNULL( &msc->msc_cred ) ) {
1393                                                 memset( msc->msc_cred.bv_val, 0,
1394                                                         msc->msc_cred.bv_len );
1395                                         }
1396                                         ber_bvreplace( &msc->msc_cred, &cred );
1397                                         ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
1398                                 }
1399                         }
1400                         break;
1401
1402                 default:
1403                         assert( 0 );
1404                         break;
1405                 }
1406         }
1407
1408         return LDAP_BACK_CONN_ISBOUND( msc );
1409 }
1410
1411 /*
1412  * Add controls;
1413  *
1414  * if any needs to be added, it is prepended to existing ones,
1415  * in a newly allocated array.  The companion function
1416  * mi->mi_ldap_extra->controls_free() must be used to restore the original
1417  * status of op->o_ctrls.
1418  */
1419 int
1420 asyncmeta_controls_add(
1421                 Operation       *op,
1422                 SlapReply       *rs,
1423                 a_metaconn_t    *mc,
1424                 int             candidate,
1425                 LDAPControl     ***pctrls )
1426 {
1427         a_metainfo_t            *mi = mc->mc_info;
1428         a_metatarget_t          *mt = mi->mi_targets[ candidate ];
1429         a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
1430
1431         LDAPControl             **ctrls = NULL;
1432         /* set to the maximum number of controls this backend can add */
1433         LDAPControl             c[ 2 ] = {{ 0 }};
1434         int                     n = 0, i, j1 = 0, j2 = 0;
1435
1436         *pctrls = NULL;
1437
1438         rs->sr_err = LDAP_SUCCESS;
1439
1440         /* don't add controls if protocol is not LDAPv3 */
1441         switch ( mt->mt_version ) {
1442         case LDAP_VERSION3:
1443                 break;
1444
1445         case 0:
1446                 if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
1447                         break;
1448                 }
1449                 /* fall thru */
1450
1451         default:
1452                 goto done;
1453         }
1454
1455         /* put controls that go __before__ existing ones here */
1456
1457         /* proxyAuthz for identity assertion */
1458         switch ( mi->mi_ldap_extra->proxy_authz_ctrl( op, rs, &msc->msc_bound_ndn,
1459                 mt->mt_version, &mt->mt_idassert, &c[ j1 ] ) )
1460         {
1461         case SLAP_CB_CONTINUE:
1462                 break;
1463
1464         case LDAP_SUCCESS:
1465                 j1++;
1466                 break;
1467
1468         default:
1469                 goto done;
1470         }
1471
1472         /* put controls that go __after__ existing ones here */
1473
1474 #ifdef SLAP_CONTROL_X_SESSION_TRACKING
1475         /* session tracking */
1476         if ( META_BACK_TGT_ST_REQUEST( mt ) ) {
1477                 switch ( slap_ctrl_session_tracking_request_add( op, rs, &c[ j1 + j2 ] ) ) {
1478                 case SLAP_CB_CONTINUE:
1479                         break;
1480
1481                 case LDAP_SUCCESS:
1482                         j2++;
1483                         break;
1484
1485                 default:
1486                         goto done;
1487                 }
1488         }
1489 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
1490
1491         if ( rs->sr_err == SLAP_CB_CONTINUE ) {
1492                 rs->sr_err = LDAP_SUCCESS;
1493         }
1494
1495         /* if nothing to do, just bail out */
1496         if ( j1 == 0 && j2 == 0 ) {
1497                 goto done;
1498         }
1499
1500         assert( j1 + j2 <= (int) (sizeof( c )/sizeof( c[0] )) );
1501
1502         if ( op->o_ctrls ) {
1503                 for ( n = 0; op->o_ctrls[ n ]; n++ )
1504                         /* just count ctrls */ ;
1505         }
1506
1507         ctrls = op->o_tmpalloc( (n + j1 + j2 + 1) * sizeof( LDAPControl * ) + ( j1 + j2 ) * sizeof( LDAPControl ),
1508                         op->o_tmpmemctx );
1509         if ( j1 ) {
1510                 ctrls[ 0 ] = (LDAPControl *)&ctrls[ n + j1 + j2 + 1 ];
1511                 *ctrls[ 0 ] = c[ 0 ];
1512                 for ( i = 1; i < j1; i++ ) {
1513                         ctrls[ i ] = &ctrls[ 0 ][ i ];
1514                         *ctrls[ i ] = c[ i ];
1515                 }
1516         }
1517
1518         i = 0;
1519         if ( op->o_ctrls ) {
1520                 for ( i = 0; op->o_ctrls[ i ]; i++ ) {
1521                         ctrls[ i + j1 ] = op->o_ctrls[ i ];
1522                 }
1523         }
1524
1525         n += j1;
1526         if ( j2 ) {
1527                 ctrls[ n ] = (LDAPControl *)&ctrls[ n + j2 + 1 ] + j1;
1528                 *ctrls[ n ] = c[ j1 ];
1529                 for ( i = 1; i < j2; i++ ) {
1530                         ctrls[ n + i ] = &ctrls[ n ][ i ];
1531                         *ctrls[ n + i ] = c[ i ];
1532                 }
1533         }
1534
1535         ctrls[ n + j2 ] = NULL;
1536
1537 done:;
1538         if ( ctrls == NULL ) {
1539                 ctrls = op->o_ctrls;
1540         }
1541
1542         *pctrls = ctrls;
1543
1544         return rs->sr_err;
1545 }
1546
1547 #if 0
1548 /*
1549  * Add controls;
1550  *
1551  * same as asyncmeta_controls_add, but creates a new controls array
1552  * to be used by the operation copy
1553  */
1554 int
1555 asyncmeta_controls_add_copy(
1556                 Operation       *op,
1557                 SlapReply       *rs,
1558                 a_metaconn_t    *mc,
1559                 int             candidate,
1560                 LDAPControl     ***pctrls )
1561 {
1562         a_metainfo_t            *mi = (a_metainfo_t *)op->o_bd->be_private;
1563         a_metatarget_t          *mt = mi->mi_targets[ candidate ];
1564         a_metasingleconn_t      *msc = mc->mc_conns[ candidate ];
1565
1566         LDAPControl             **ctrls = NULL;
1567         /* set to the maximum number of controls this backend can add */
1568         LDAPControl             c[ 2 ] = {{ 0 }};
1569         int                     n = 0, i, j1 = 0, j2 = 0;
1570
1571         *pctrls = NULL;
1572
1573         rs->sr_err = LDAP_SUCCESS;
1574
1575         /* don't add controls if protocol is not LDAPv3 */
1576         switch ( mt->mt_version ) {
1577         case LDAP_VERSION3:
1578                 break;
1579
1580         case 0:
1581                 if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
1582                         break;
1583                 }
1584                 /* fall thru */
1585
1586         default:
1587                 goto done;
1588         }
1589
1590         /* put controls that go __before__ existing ones here */
1591
1592         /* proxyAuthz for identity assertion */
1593         switch ( mi->mi_ldap_extra->proxy_authz_ctrl( op, rs, &msc->msc_bound_ndn,
1594                 mt->mt_version, &mt->mt_idassert, &c[ j1 ] ) )
1595         {
1596         case SLAP_CB_CONTINUE:
1597                 break;
1598
1599         case LDAP_SUCCESS:
1600                 j1++;
1601                 break;
1602
1603         default:
1604                 goto done;
1605         }
1606
1607         /* put controls that go __after__ existing ones here */
1608
1609 #ifdef SLAP_CONTROL_X_SESSION_TRACKING
1610         /* session tracking */
1611         if ( META_BACK_TGT_ST_REQUEST( mt ) ) {
1612                 switch ( slap_ctrl_session_tracking_request_add( op, rs, &c[ j1 + j2 ] ) ) {
1613                 case SLAP_CB_CONTINUE:
1614                         break;
1615
1616                 case LDAP_SUCCESS:
1617                         j2++;
1618                         break;
1619
1620                 default:
1621                         goto copy;
1622                 }
1623         }
1624 #endif /* SLAP_CONTROL_X_SESSION_TRACKING */
1625
1626         if ( rs->sr_err == SLAP_CB_CONTINUE ) {
1627                 rs->sr_err = LDAP_SUCCESS;
1628         }
1629
1630 copy:
1631         if ( op->o_ctrls ) {
1632                 for ( n = 0; op->o_ctrls[ n ]; n++ )
1633                         /* just count ctrls */ ;
1634         }
1635
1636         ctrls = ch_calloc( (n + j1 + j2 + 1) * sizeof( LDAPControl * ) + ( j1 + j2 ) * sizeof( LDAPControl ));
1637         if ( j1 ) {
1638                 ctrls[ 0 ] = (LDAPControl *)&ctrls[ n + j1 + j2 + 1 ];
1639                 *ctrls[ 0 ] = c[ 0 ];
1640                 for ( i = 1; i < j1; i++ ) {
1641                         ctrls[ i ] = &ctrls[ 0 ][ i ];
1642                         *ctrls[ i ] = c[ i ];
1643                 }
1644         }
1645
1646         i = 0;
1647         if ( op->o_ctrls ) {
1648                 for ( i = 0; op->o_ctrls[ i ]; i++ ) {
1649                         ctrls[ i + j1 ] = op->o_ctrls[ i ];
1650                 }
1651         }
1652
1653         n += j1;
1654         if ( j2 ) {
1655                 ctrls[ n ] = (LDAPControl *)&ctrls[ n + j2 + 1 ] + j1;
1656                 *ctrls[ n ] = c[ j1 ];
1657                 for ( i = 1; i < j2; i++ ) {
1658                         ctrls[ n + i ] = &ctrls[ n ][ i ];
1659                         *ctrls[ n + i ] = c[ i ];
1660                 }
1661         }
1662
1663         ctrls[ n + j2 ] = NULL;
1664
1665 done:;
1666
1667         op->o_ctrls = ctrls;
1668
1669         return rs->sr_err;
1670 }
1671 #endif
1672
1673 /*
1674  * asyncmeta_dobind_init()
1675  *
1676  * initiates bind for a candidate target
1677  */
1678 meta_search_candidate_t
1679 asyncmeta_dobind_init(Operation *op, SlapReply *rs, bm_context_t *bc, a_metaconn_t *mc, int candidate)
1680 {
1681         SlapReply               *candidates = bc->candidates;
1682         a_metainfo_t            *mi = ( a_metainfo_t * )mc->mc_info;
1683         a_metatarget_t          *mt = mi->mi_targets[ candidate ];
1684         a_metasingleconn_t      *msc = &mc->mc_conns[ candidate ];
1685         ber_socket_t s;
1686         struct berval           binddn = msc->msc_bound_ndn,
1687                                 cred = msc->msc_cred;
1688         int                     method;
1689
1690         int                     rc;
1691         ber_int_t       msgid;
1692
1693         meta_search_candidate_t retcode;
1694
1695         Debug( LDAP_DEBUG_TRACE, "%s >>> asyncmeta_search_dobind_init[%d]\n",
1696                 op->o_log_prefix, candidate, 0 );
1697
1698         if ( mc->mc_authz_target == META_BOUND_ALL ) {
1699                 return META_SEARCH_CANDIDATE;
1700         }
1701
1702         retcode = META_SEARCH_BINDING;
1703         if ( LDAP_BACK_CONN_ISBOUND( msc ) || LDAP_BACK_CONN_ISANON( msc ) ) {
1704                 /* already bound (or anonymous) */
1705
1706 #ifdef DEBUG_205
1707                 char    buf[ SLAP_TEXT_BUFLEN ] = { '\0' };
1708                 int     bound = 0;
1709
1710                 if ( LDAP_BACK_CONN_ISBOUND( msc ) ) {
1711                         bound = 1;
1712                 }
1713
1714                 snprintf( buf, sizeof( buf ), " mc=%p ld=%p%s DN=\"%s\"",
1715                         (void *)mc, (void *)msc->msc_ld,
1716                         bound ? " bound" : " anonymous",
1717                         bound == 0 ? "" : msc->msc_bound_ndn.bv_val );
1718                 Debug( LDAP_DEBUG_ANY, "### %s asyncmeta_search_dobind_init[%d]%s\n",
1719                         op->o_log_prefix, candidate, buf );
1720 #endif /* DEBUG_205 */
1721
1722                 retcode = META_SEARCH_CANDIDATE;
1723
1724         } else if ( META_BACK_CONN_CREATING( msc ) || LDAP_BACK_CONN_BINDING( msc ) ) {
1725                 /* another thread is binding the target for this conn; wait */
1726
1727 #ifdef DEBUG_205
1728                 char    buf[ SLAP_TEXT_BUFLEN ] = { '\0' };
1729
1730                 snprintf( buf, sizeof( buf ), " mc=%p ld=%p needbind",
1731                         (void *)mc, (void *)msc->msc_ld );
1732                 Debug( LDAP_DEBUG_ANY, "### %s asyncmeta_search_dobind_init[%d]%s\n",
1733                         op->o_log_prefix, candidate, buf );
1734 #endif /* DEBUG_205 */
1735
1736                 candidates[ candidate ].sr_msgid = META_MSGID_NEED_BIND;
1737                 retcode = META_SEARCH_NEED_BIND;
1738
1739         } else {
1740                 /* we'll need to bind the target for this conn */
1741
1742 #ifdef DEBUG_205
1743                 char buf[ SLAP_TEXT_BUFLEN ];
1744
1745                 snprintf( buf, sizeof( buf ), " mc=%p ld=%p binding",
1746                         (void *)mc, (void *)msc->msc_ld );
1747                 Debug( LDAP_DEBUG_ANY, "### %s asyncmeta_search_dobind_init[%d]%s\n",
1748                         op->o_log_prefix, candidate, buf );
1749 #endif /* DEBUG_205 */
1750
1751                 if ( msc->msc_ld == NULL ) {
1752                         /* for some reason (e.g. because formerly in "binding"
1753                          * state, with eventual connection expiration or invalidation)
1754                          * it was not initialized as expected */
1755
1756                         Debug( LDAP_DEBUG_ANY, "%s asyncmeta_search_dobind_init[%d] mc=%p ld=NULL\n",
1757                                 op->o_log_prefix, candidate, (void *)mc );
1758
1759                         rc = asyncmeta_init_one_conn( op, rs, mc, candidate,
1760                                 LDAP_BACK_CONN_ISPRIV( mc ), LDAP_BACK_DONTSEND, 0 );
1761                         switch ( rc ) {
1762                         case LDAP_SUCCESS:
1763                                 assert( msc->msc_ld != NULL );
1764                                 break;
1765
1766                         case LDAP_SERVER_DOWN:
1767                         case LDAP_UNAVAILABLE:
1768                                 goto down;
1769
1770                         default:
1771                                 goto other;
1772                         }
1773                 }
1774
1775                 LDAP_BACK_CONN_BINDING_SET( msc );
1776         }
1777
1778         if ( retcode != META_SEARCH_BINDING ) {
1779                 return retcode;
1780         }
1781
1782         if ( op->o_conn != NULL &&
1783                 !op->o_do_not_cache &&
1784                 ( BER_BVISNULL( &msc->msc_bound_ndn ) ||
1785                         BER_BVISEMPTY( &msc->msc_bound_ndn ) ||
1786                         ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
1787         {
1788                 rc = asyncmeta_back_proxy_authz_cred( mc, candidate, op, rs, LDAP_BACK_DONTSEND, &binddn, &cred, &method );
1789                 switch ( rc ) {
1790                 case LDAP_SUCCESS:
1791                         break;
1792                 case LDAP_UNAVAILABLE:
1793                         goto down;
1794                 default:
1795                         goto other;
1796                 }
1797
1798                 /* NOTE: we copy things here, even if bind didn't succeed yet,
1799                  * because the connection is not shared until bind is over */
1800                 if ( !BER_BVISNULL( &binddn ) ) {
1801                         ldap_pvt_thread_mutex_lock(&mc->mc_om_mutex);
1802
1803                         ber_bvreplace( &msc->msc_bound_ndn, &binddn );
1804                         if ( META_BACK_TGT_SAVECRED( mt ) && !BER_BVISNULL( &cred ) ) {
1805                                 if ( !BER_BVISNULL( &msc->msc_cred ) ) {
1806                                         memset( msc->msc_cred.bv_val, 0,
1807                                                 msc->msc_cred.bv_len );
1808                                 }
1809                                 ber_bvreplace( &msc->msc_cred, &cred );
1810                         }
1811                         ldap_pvt_thread_mutex_unlock(&mc->mc_om_mutex);
1812                 }
1813                 if ( LDAP_BACK_CONN_ISBOUND( msc ) ) {
1814                         /* apparently, idassert was configured with SASL bind,
1815                          * so bind occurred inside meta_back_proxy_authz_cred() */
1816                         LDAP_BACK_CONN_BINDING_CLEAR( msc );
1817                         return META_SEARCH_CANDIDATE;
1818                 }
1819
1820                 /* paranoid */
1821                 switch ( method ) {
1822                 case LDAP_AUTH_NONE:
1823                 case LDAP_AUTH_SIMPLE:
1824                         /* do a simple bind with binddn, cred */
1825                         break;
1826
1827                 default:
1828                         assert( 0 );
1829                         break;
1830                 }
1831         }
1832
1833         assert( msc->msc_ld != NULL );
1834
1835         if ( !BER_BVISEMPTY( &binddn ) && BER_BVISEMPTY( &cred ) ) {
1836                 /* bind anonymously? */
1837                 Debug( LDAP_DEBUG_ANY, "%s asyncmeta_search_dobind_init[%d] mc=%p: "
1838                         "non-empty dn with empty cred; binding anonymously\n",
1839                         op->o_log_prefix, candidate, (void *)mc );
1840                 cred = slap_empty_bv;
1841
1842         } else if ( BER_BVISEMPTY( &binddn ) && !BER_BVISEMPTY( &cred ) ) {
1843                 /* error */
1844                 Debug( LDAP_DEBUG_ANY, "%s asyncmeta_search_dobind_init[%d] mc=%p: "
1845                         "empty dn with non-empty cred: error\n",
1846                         op->o_log_prefix, candidate, (void *)mc );
1847                 rc = LDAP_OTHER;
1848                 goto other;
1849         }
1850 retry_bind:
1851         rc = ldap_sasl_bind( msc->msc_ld, binddn.bv_val, LDAP_SASL_SIMPLE, &cred,
1852                         NULL, NULL, &msgid );
1853         ldap_get_option( msc->msc_ld, LDAP_OPT_RESULT_CODE, &rc );
1854
1855         if (rc == LDAP_SERVER_DOWN ) {
1856                 goto down;
1857         }
1858         candidates[ candidate ].sr_msgid = msgid;
1859         asyncmeta_set_msc_time(msc);
1860 #ifdef DEBUG_205
1861         {
1862                 char buf[ SLAP_TEXT_BUFLEN ];
1863
1864                 snprintf( buf, sizeof( buf ), "asyncmeta_search_dobind_init[%d] mc=%p ld=%p rc=%d",
1865                         candidate, (void *)mc, (void *)mc->mc_conns[ candidate ].msc_ld, rc );
1866                 Debug( LDAP_DEBUG_ANY, "### %s %s\n",
1867                         op->o_log_prefix, buf, 0 );
1868         }
1869 #endif /* DEBUG_205 */
1870
1871         switch ( rc ) {
1872         case LDAP_SUCCESS:
1873                 assert( msgid >= 0 );
1874                 META_BINDING_SET( &candidates[ candidate ] );
1875                 rs->sr_err = LDAP_SUCCESS;
1876                 return META_SEARCH_BINDING;
1877
1878         case LDAP_X_CONNECTING:
1879                 /* must retry, same conn */
1880                 candidates[ candidate ].sr_msgid = META_MSGID_CONNECTING;
1881                 LDAP_BACK_CONN_BINDING_CLEAR( msc );
1882                 goto retry_bind;
1883
1884         case LDAP_SERVER_DOWN:
1885 down:;
1886                 retcode = META_SEARCH_ERR;
1887                 rs->sr_err = LDAP_UNAVAILABLE;
1888                 candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
1889                 break;
1890
1891                 /* fall thru */
1892
1893         default:
1894 other:;
1895                 rs->sr_err = rc;
1896                 rc = slap_map_api2result( rs );
1897                 candidates[ candidate ].sr_err = rc;
1898                 if ( META_BACK_ONERR_STOP( mi ) ) {
1899                         retcode = META_SEARCH_ERR;
1900
1901                 } else {
1902                         retcode = META_SEARCH_NOT_CANDIDATE;
1903                 }
1904                 candidates[ candidate ].sr_msgid = META_MSGID_IGNORE;
1905                 break;
1906         }
1907
1908         return retcode;
1909 }
1910
1911
1912
1913
1914 meta_search_candidate_t
1915 asyncmeta_dobind_init_with_retry(Operation *op, SlapReply *rs, bm_context_t *bc, a_metaconn_t *mc, int candidate)
1916 {
1917
1918         int rc, retries = 1;
1919         a_metasingleconn_t *msc = &mc->mc_conns[candidate];
1920         a_metainfo_t            *mi = mc->mc_info;
1921         a_metatarget_t          *mt = mi->mi_targets[ candidate ];
1922         SlapReply               *candidates = bc->candidates;
1923
1924 retry_dobind:
1925         rc = asyncmeta_dobind_init(op, rs, bc, mc, candidate);
1926         if (rs->sr_err != LDAP_UNAVAILABLE) {
1927                 return rc;
1928         } else if (retries <= 0) {
1929                 ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
1930                 if (mc->mc_active < 1) {
1931                         asyncmeta_clear_one_msc(NULL, mc, candidate);
1932                 }
1933                 ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1934                 return rc;
1935         }
1936         /* need to retry */
1937         retries--;
1938         if ( LogTest( LDAP_DEBUG_ANY ) ) {
1939                 char    buf[ SLAP_TEXT_BUFLEN ];
1940
1941                 /* this lock is required; however,
1942                  * it's invoked only when logging is on */
1943                 ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
1944                 snprintf( buf, sizeof( buf ),
1945                           "retrying URI=\"%s\" DN=\"%s\"",
1946                           mt->mt_uri,
1947                           BER_BVISNULL( &msc->msc_bound_ndn ) ?
1948                           "" : msc->msc_bound_ndn.bv_val );
1949                 ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
1950
1951                 Debug( LDAP_DEBUG_ANY,
1952                        "%s asyncmeta_search_dobind_init_with_retry[%d]: %s.\n",
1953                        op->o_log_prefix, candidate, buf );
1954         }
1955
1956         ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
1957         if (mc->mc_active < 1) {
1958                 asyncmeta_clear_one_msc(NULL, mc, candidate);
1959         }
1960         ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1961
1962         ( void )rewrite_session_delete( mt->mt_rwmap.rwm_rw, op->o_conn );
1963
1964         rc = asyncmeta_init_one_conn( op, rs, mc, candidate,
1965                                       LDAP_BACK_CONN_ISPRIV( mc ), LDAP_BACK_DONTSEND, 0 );
1966
1967         if (rs->sr_err != LDAP_SUCCESS) {
1968                 ldap_pvt_thread_mutex_lock( &mc->mc_om_mutex );
1969                 if (mc->mc_active < 1) {
1970                         asyncmeta_clear_one_msc(NULL, mc, candidate);
1971                 }
1972                 ldap_pvt_thread_mutex_unlock( &mc->mc_om_mutex );
1973                 return META_SEARCH_ERR;
1974         }
1975
1976         goto retry_dobind;
1977         return rc;
1978 }