]> git.sur5r.net Git - openldap/blob - servers/slapd/back-meta/bind.c
- setup framework for monitoring of back-bdb/back-hdb stuff in their
[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 #include "lutil_ldap.h"
38
39 static int
40 meta_back_proxy_authz_bind(
41         metaconn_t              *mc,
42         int                     candidate,
43         Operation               *op,
44         SlapReply               *rs,
45         ldap_back_send_t        sendok );
46
47 static int
48 meta_back_single_bind(
49         Operation               *op,
50         SlapReply               *rs,
51         metaconn_t              *mc,
52         int                     candidate );
53
54 int
55 meta_back_bind( Operation *op, SlapReply *rs )
56 {
57         metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
58         metaconn_t      *mc = NULL;
59
60         int             rc = LDAP_OTHER,
61                         i,
62                         gotit = 0,
63                         isroot = 0;
64
65         SlapReply       *candidates = meta_back_candidates_get( op );
66
67         rs->sr_err = LDAP_SUCCESS;
68
69         Debug( LDAP_DEBUG_ARGS, "%s meta_back_bind: dn=\"%s\".\n",
70                 op->o_log_prefix, op->o_req_dn.bv_val, 0 );
71
72         /* the test on the bind method should be superfluous */
73         if ( op->orb_method == LDAP_AUTH_SIMPLE
74                 && be_isroot_dn( op->o_bd, &op->o_req_ndn ) )
75         {
76                 if ( !be_isroot_pw( op ) ) {
77                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
78                         rs->sr_text = NULL;
79                         send_ldap_result( op, rs );
80                         return rs->sr_err;
81                 }
82
83                 if ( META_BACK_DEFER_ROOTDN_BIND( mi ) ) {
84                         rs->sr_err = LDAP_SUCCESS;
85                         rs->sr_text = NULL;
86                         /* frontend will return success */
87                         return rs->sr_err;
88                 }
89
90                 isroot = 1;
91         }
92
93         /* we need meta_back_getconn() not send result even on error,
94          * because we want to intercept the error and make it
95          * invalidCredentials */
96         mc = meta_back_getconn( op, rs, NULL, LDAP_BACK_BIND_DONTSEND );
97         if ( !mc ) {
98                 if ( LogTest( LDAP_DEBUG_ANY ) ) {
99                         char    buf[ SLAP_TEXT_BUFLEN ];
100
101                         snprintf( buf, sizeof( buf ),
102                                 "meta_back_bind: no target "
103                                 "for dn \"%s\" (%d%s%s).",
104                                 op->o_req_dn.bv_val, rs->sr_err,
105                                 rs->sr_text ? ". " : "",
106                                 rs->sr_text ? rs->sr_text : "" );
107                         Debug( LDAP_DEBUG_ANY,
108                                 "%s %s\n",
109                                 op->o_log_prefix, buf, 0 );
110                 }
111
112                 /* FIXME: there might be cases where we don't want
113                  * to map the error onto invalidCredentials */
114                 switch ( rs->sr_err ) {
115                 case LDAP_NO_SUCH_OBJECT:
116                 case LDAP_UNWILLING_TO_PERFORM:
117                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
118                         rs->sr_text = NULL;
119                         break;
120                 }
121                 send_ldap_result( op, rs );
122                 return rs->sr_err;
123         }
124
125         /*
126          * Each target is scanned ...
127          */
128         mc->mc_authz_target = META_BOUND_NONE;
129         for ( i = 0; i < mi->mi_ntargets; i++ ) {
130                 metatarget_t    *mt = mi->mi_targets[ i ];
131                 int             lerr;
132
133                 /*
134                  * Skip non-candidates
135                  */
136                 if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
137                         continue;
138                 }
139
140                 if ( gotit == 0 ) {
141                         /* set rc to LDAP_SUCCESS only if at least
142                          * one candidate has been tried */
143                         rc = LDAP_SUCCESS;
144                         gotit = 1;
145
146                 } else if ( isroot == 0 ) {
147                         /*
148                          * A bind operation is expected to have
149                          * ONE CANDIDATE ONLY!
150                          */
151                         Debug( LDAP_DEBUG_ANY,
152                                 "### %s meta_back_bind: more than one"
153                                 " candidate selected...\n",
154                                 op->o_log_prefix, 0, 0 );
155                 }
156
157                 if ( isroot ) {
158                         if ( mt->mt_idassert_authmethod == LDAP_AUTH_NONE
159                                 || BER_BVISNULL( &mt->mt_idassert_authcDN ) )
160                         {
161                                 metasingleconn_t        *msc = &mc->mc_conns[ i ];
162
163                                 /* skip the target if no pseudorootdn is provided */
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 ( LDAP_BACK_SAVECRED( mi ) &&
170                                         !BER_BVISNULL( &msc->msc_cred ) )
171                                 {
172                                         /* destroy sensitive data */
173                                         memset( msc->msc_cred.bv_val, 0,
174                                                 msc->msc_cred.bv_len );
175                                         ch_free( msc->msc_cred.bv_val );
176                                         BER_BVZERO( &msc->msc_cred );
177                                 }
178
179                                 continue;
180                         }
181
182                         
183                         (void)meta_back_proxy_authz_bind( mc, i, op, rs, LDAP_BACK_DONTSEND );
184                         lerr = rs->sr_err;
185
186                 } else {
187                         lerr = meta_back_single_bind( op, rs, mc, i );
188                 }
189
190                 if ( lerr != LDAP_SUCCESS ) {
191                         rc = rs->sr_err = lerr;
192                         /* FIXME: in some cases (e.g. unavailable)
193                          * do not assume it's not candidate; rather
194                          * mark this as an error to be eventually
195                          * reported to client */
196                         META_CANDIDATE_CLEAR( &candidates[ i ] );
197                         break;
198                 }
199         }
200
201         /* must re-insert if local DN changed as result of bind */
202         if ( rc == LDAP_SUCCESS ) {
203                 if ( isroot ) {
204                         mc->mc_authz_target = META_BOUND_ALL;
205                         ber_dupbv( &op->orb_edn, be_root_dn( op->o_bd ) );
206                 }
207
208                 if ( !dn_match( &op->o_req_ndn, &mc->mc_local_ndn ) ) {
209                         metaconn_t      *tmpmc;
210                         int             lerr;
211
212                         /* wait for all other ops to release the connection */
213 retry_lock:;
214                         ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
215                         if ( mc->mc_refcnt > 1 ) {
216                                 ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
217                                 ldap_pvt_thread_yield();
218                                 goto retry_lock;
219                         }
220
221                         assert( mc->mc_refcnt == 1 );
222                         tmpmc = avl_delete( &mi->mi_conninfo.lai_tree, (caddr_t)mc,
223                                 meta_back_conndn_cmp );
224                         assert( tmpmc == mc );
225
226                         /* delete all cached connections with the current connection */
227                         if ( LDAP_BACK_SINGLECONN( mi ) ) {
228                                 while ( ( tmpmc = avl_delete( &mi->mi_conninfo.lai_tree, (caddr_t)mc, meta_back_conn_cmp ) ) != NULL )
229                                 {
230                                         Debug( LDAP_DEBUG_TRACE,
231                                                 "=>meta_back_bind: destroying conn %ld (refcnt=%u)\n",
232                                                 LDAP_BACK_PCONN_ID( mc->mc_conn ), mc->mc_refcnt, 0 );
233
234                                         if ( tmpmc->mc_refcnt != 0 ) {
235                                                 /* taint it */
236                                                 LDAP_BACK_CONN_TAINTED_SET( tmpmc );
237
238                                         } else {
239                                                 /*
240                                                  * Needs a test because the handler may be corrupted,
241                                                  * and calling ldap_unbind on a corrupted header results
242                                                  * in a segmentation fault
243                                                  */
244                                                 meta_back_conn_free( tmpmc );
245                                         }
246                                 }
247                         }
248
249                         ber_bvreplace( &mc->mc_local_ndn, &op->o_req_ndn );
250                         if ( isroot ) {
251                                 mc->mc_conn = LDAP_BACK_PCONN_SET( op );
252                         }
253                         lerr = avl_insert( &mi->mi_conninfo.lai_tree, (caddr_t)mc,
254                                 meta_back_conndn_cmp, meta_back_conndn_dup );
255                         ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
256                         if ( lerr == -1 ) {
257                                 meta_clear_candidates( op, mc );
258
259                                 /* we can do this because mc_refcnt == 1 */
260                                 mc->mc_refcnt = 0;
261                                 meta_back_conn_free( mc );
262                                 mc = NULL;
263                         }
264                 }
265         }
266
267         if ( mc != NULL ) {
268                 meta_back_release_conn( op, mc );
269         }
270
271         /*
272          * rc is LDAP_SUCCESS if at least one bind succeeded,
273          * err is the last error that occurred during a bind;
274          * if at least (and at most?) one bind succeeds, fine.
275          */
276         if ( rc != LDAP_SUCCESS ) {
277                 
278                 /*
279                  * deal with bind failure ...
280                  */
281
282                 /*
283                  * no target was found within the naming context, 
284                  * so bind must fail with invalid credentials
285                  */
286                 if ( rs->sr_err == LDAP_SUCCESS && gotit == 0 ) {
287                         rs->sr_err = LDAP_INVALID_CREDENTIALS;
288                 } else {
289                         rs->sr_err = slap_map_api2result( rs );
290                 }
291                 send_ldap_result( op, rs );
292                 return rs->sr_err;
293
294         }
295
296         return LDAP_SUCCESS;
297 }
298
299 static int
300 meta_back_bind_op_result(
301         Operation               *op,
302         SlapReply               *rs,
303         metaconn_t              *mc,
304         int                     candidate,
305         int                     msgid,
306         ldap_back_send_t        sendok )
307 {
308         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
309         metatarget_t            *mt = mi->mi_targets[ candidate ];
310         metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
311         LDAPMessage             *res;
312         struct timeval          tv;
313         int                     rc;
314         int                     nretries = mt->mt_nretries;
315         char                    buf[ SLAP_TEXT_BUFLEN ];
316
317         Debug( LDAP_DEBUG_TRACE,
318                 ">>> %s meta_back_bind_op_result[%d]\n",
319                 op->o_log_prefix, candidate, 0 );
320
321         if ( rs->sr_err == LDAP_SUCCESS ) {
322                 time_t          stoptime = (time_t)(-1),
323                                 timeout;
324                 int             timeout_err = op->o_protocol >= LDAP_VERSION3 ?
325                                 LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
326                 const char      *timeout_text = "Operation timed out";
327                 slap_op_t       opidx = slap_req2op( op->o_tag );
328
329                 /* since timeout is not specified, compute and use
330                  * the one specific to the ongoing operation */
331                 if ( opidx == LDAP_REQ_SEARCH ) {
332                         if ( op->ors_tlimit <= 0 ) {
333                                 timeout = 0;
334
335                         } else {
336                                 timeout = op->ors_tlimit;
337                                 timeout_err = LDAP_TIMELIMIT_EXCEEDED;
338                                 timeout_text = NULL;
339                         }
340
341                 } else {
342                         timeout = mt->mt_timeout[ opidx ];
343                 }
344
345                 /* better than nothing :) */
346                 if ( timeout == 0 ) {
347                         if ( mi->mi_idle_timeout ) {
348                                 timeout = mi->mi_idle_timeout;
349
350                         } else if ( mi->mi_conn_ttl ) {
351                                 timeout = mi->mi_conn_ttl;
352                         }
353                 }
354
355                 if ( timeout ) {
356                         stoptime = op->o_time + timeout;
357                 }
358
359                 LDAP_BACK_TV_SET( &tv );
360
361                 /*
362                  * handle response!!!
363                  */
364 retry:;
365                 rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
366                 switch ( rc ) {
367                 case 0:
368 #if 0
369                         Debug( LDAP_DEBUG_ANY,
370                                 "%s meta_back_bind_op_result[%d]: ldap_result=0 nretries=%d.\n",
371                                 op->o_log_prefix, candidate, nretries );
372 #endif
373
374                         if ( nretries != META_RETRY_NEVER 
375                                 || ( timeout && slap_get_time() <= stoptime ) )
376                         {
377                                 ldap_pvt_thread_yield();
378                                 if ( nretries > 0 ) {
379                                         nretries--;
380                                 }
381                                 tv = mt->mt_bind_timeout;
382                                 goto retry;
383                         }
384
385                         /* don't let anyone else use this handler,
386                          * because there's a pending bind that will not
387                          * be acknowledged */
388                         ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
389                         ldap_unbind_ext( msc->msc_ld, NULL, NULL );
390                         msc->msc_ld = NULL;
391                         LDAP_BACK_CONN_BINDING_CLEAR( msc );
392                         ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
393
394                         rs->sr_err = timeout_err;
395                         rs->sr_text = timeout_text;
396                         break;
397
398                 case -1:
399                         ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
400                                 &rs->sr_err );
401
402                         snprintf( buf, sizeof( buf ),
403                                 "err=%d (%s) nretries=%d",
404                                 rs->sr_err, ldap_err2string( rs->sr_err ), nretries );
405                         Debug( LDAP_DEBUG_ANY,
406                                 "### %s meta_back_bind_op_result[%d]: %s.\n",
407                                 op->o_log_prefix, candidate, buf );
408                         break;
409
410                 default:
411                         /* only touch when activity actually took place... */
412                         if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
413                                 msc->msc_time = op->o_time;
414                         }
415
416                         /* FIXME: matched? referrals? response controls? */
417                         rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
418                                         NULL, NULL, NULL, NULL, 1 );
419                         if ( rc != LDAP_SUCCESS ) {
420                                 rs->sr_err = rc;
421                         }
422                         break;
423                 }
424         }
425
426         rs->sr_err = slap_map_api2result( rs );
427
428         Debug( LDAP_DEBUG_TRACE,
429                 "<<< %s meta_back_bind_op_result[%d] err=%d\n",
430                 op->o_log_prefix, candidate, rs->sr_err );
431
432         return rs->sr_err;
433 }
434
435 /*
436  * meta_back_single_bind
437  *
438  * attempts to perform a bind with creds
439  */
440 static int
441 meta_back_single_bind(
442         Operation               *op,
443         SlapReply               *rs,
444         metaconn_t              *mc,
445         int                     candidate )
446 {
447         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
448         metatarget_t            *mt = mi->mi_targets[ candidate ];
449         struct berval           mdn = BER_BVNULL;
450         metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
451         int                     msgid;
452         dncookie                dc;
453         
454         if ( !BER_BVISNULL( &msc->msc_bound_ndn ) ) {
455                 ch_free( msc->msc_bound_ndn.bv_val );
456                 BER_BVZERO( &msc->msc_bound_ndn );
457         }
458
459         if ( LDAP_BACK_SAVECRED( mi ) && !BER_BVISNULL( &msc->msc_cred ) ) {
460                 /* destroy sensitive data */
461                 memset( msc->msc_cred.bv_val, 0, msc->msc_cred.bv_len );
462                 ch_free( msc->msc_cred.bv_val );
463                 BER_BVZERO( &msc->msc_cred );
464         }
465
466         /*
467          * Rewrite the bind dn if needed
468          */
469         dc.target = mt;
470         dc.conn = op->o_conn;
471         dc.rs = rs;
472         dc.ctx = "bindDN";
473
474         if ( ldap_back_dn_massage( &dc, &op->o_req_dn, &mdn ) ) {
475                 rs->sr_text = "DN rewrite error";
476                 rs->sr_err = LDAP_OTHER;
477                 return rs->sr_err;
478         }
479
480         /* FIXME: this fixes the bind problem right now; we need
481          * to use the asynchronous version to get the "matched"
482          * and more in case of failure ... */
483         /* FIXME: should we check if at least some of the op->o_ctrls
484          * can/should be passed? */
485         rs->sr_err = ldap_sasl_bind( msc->msc_ld, mdn.bv_val,
486                         LDAP_SASL_SIMPLE, &op->orb_cred,
487                         op->o_ctrls, NULL, &msgid );
488         meta_back_bind_op_result( op, rs, mc, candidate, msgid, LDAP_BACK_DONTSEND );
489         if ( rs->sr_err != LDAP_SUCCESS ) {
490                 goto return_results;
491         }
492
493         /* If defined, proxyAuthz will be used also when
494          * back-ldap is the authorizing backend; for this
495          * purpose, a successful bind is followed by a
496          * bind with the configured identity assertion */
497         /* NOTE: use with care */
498         if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) {
499                 meta_back_proxy_authz_bind( mc, candidate, op, rs, LDAP_BACK_SENDERR );
500                 if ( !LDAP_BACK_CONN_ISBOUND( msc ) ) {
501                         goto return_results;
502                 }
503                 goto cache_refresh;
504         }
505
506         ber_bvreplace( &msc->msc_bound_ndn, &op->o_req_ndn );
507         LDAP_BACK_CONN_ISBOUND_SET( msc );
508         mc->mc_authz_target = candidate;
509
510         if ( LDAP_BACK_SAVECRED( mi ) ) {
511                 ber_bvreplace( &msc->msc_cred, &op->orb_cred );
512                 ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
513         }
514
515 cache_refresh:;
516         if ( mi->mi_cache.ttl != META_DNCACHE_DISABLED
517                         && !BER_BVISEMPTY( &op->o_req_ndn ) )
518         {
519                 ( void )meta_dncache_update_entry( &mi->mi_cache,
520                                 &op->o_req_ndn, candidate );
521         }
522
523 return_results:;
524         if ( mdn.bv_val != op->o_req_dn.bv_val ) {
525                 free( mdn.bv_val );
526         }
527
528         if ( META_BACK_TGT_QUARANTINE( mt ) ) {
529                 meta_back_quarantine( op, rs, candidate );
530         }
531
532         return rs->sr_err;
533 }
534
535 /*
536  * meta_back_single_dobind
537  */
538 int
539 meta_back_single_dobind(
540         Operation               *op,
541         SlapReply               *rs,
542         metaconn_t              **mcp,
543         int                     candidate,
544         ldap_back_send_t        sendok,
545         int                     nretries,
546         int                     dolock )
547 {
548         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
549         metatarget_t            *mt = mi->mi_targets[ candidate ];
550         metaconn_t              *mc = *mcp;
551         metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
552         int                     rc;
553         static struct berval    cred = BER_BVC( "" );
554         int                     msgid;
555
556         assert( !LDAP_BACK_CONN_ISBOUND( msc ) );
557
558         /* NOTE: this obsoletes pseudorootdn */
559         if ( op->o_conn != NULL &&
560                 !op->o_do_not_cache &&
561                 ( BER_BVISNULL( &msc->msc_bound_ndn ) ||
562                         BER_BVISEMPTY( &msc->msc_bound_ndn ) ||
563                         ( mt->mt_idassert_flags & LDAP_BACK_AUTH_OVERRIDE ) ) )
564         {
565                 (void)meta_back_proxy_authz_bind( mc, candidate, op, rs, sendok );
566                 rc = rs->sr_err;
567                 goto done;
568         }
569
570         /* FIXME: should we check if at least some of the op->o_ctrls
571          * can/should be passed? */
572         rs->sr_err = ldap_sasl_bind( msc->msc_ld, "", LDAP_SASL_SIMPLE, &cred,
573                         NULL, NULL, &msgid );
574         rc = meta_back_bind_op_result( op, rs, mc, candidate, msgid, sendok );
575
576 done:;
577         rs->sr_err = rc;
578         if ( rc != LDAP_SUCCESS ) {
579                 if ( dolock ) {
580                         ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
581                 }
582                 LDAP_BACK_CONN_BINDING_CLEAR( msc );
583                 if ( META_BACK_ONERR_STOP( mi ) ) {
584                         LDAP_BACK_CONN_TAINTED_SET( mc );
585                         meta_back_release_conn_lock( op, mc, 0 );
586                         *mcp = NULL;
587                 }
588                 if ( dolock ) {
589                         ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
590                 }
591
592                 if ( META_BACK_ONERR_STOP( mi ) && ( sendok & LDAP_BACK_SENDERR ) ) {
593                         send_ldap_result( op, rs );
594                 }
595         }
596
597         if ( META_BACK_TGT_QUARANTINE( mt ) ) {
598                 meta_back_quarantine( op, rs, candidate );
599         }
600
601         return rc;
602 }
603
604 /*
605  * meta_back_dobind
606  */
607 int
608 meta_back_dobind(
609         Operation               *op,
610         SlapReply               *rs,
611         metaconn_t              *mc,
612         ldap_back_send_t        sendok )
613 {
614         metainfo_t              *mi = ( metainfo_t * )op->o_bd->be_private;
615
616         int                     bound = 0,
617                                 i,
618                                 isroot = 0;
619
620         SlapReply               *candidates = meta_back_candidates_get( op );
621
622         if ( be_isroot( op ) ) {
623                 isroot = 1;
624         }
625
626         Debug( LDAP_DEBUG_TRACE,
627                 "%s meta_back_dobind: conn=%ld%s\n",
628                 op->o_log_prefix,
629                 LDAP_BACK_PCONN_ID( mc->mc_conn ),
630                 isroot ? " (isroot)" : "" );
631
632         /*
633          * all the targets are bound as pseudoroot
634          */
635         if ( mc->mc_authz_target == META_BOUND_ALL ) {
636                 bound = 1;
637                 goto done;
638         }
639
640         for ( i = 0; i < mi->mi_ntargets; i++ ) {
641                 metatarget_t            *mt = mi->mi_targets[ i ];
642                 metasingleconn_t        *msc = &mc->mc_conns[ i ];
643                 int                     rc;
644
645                 /*
646                  * Not a candidate
647                  */
648                 if ( !META_IS_CANDIDATE( &candidates[ i ] ) ) {
649                         continue;
650                 }
651
652                 assert( msc->msc_ld != NULL );
653
654                 /*
655                  * If the target is already bound it is skipped
656                  */
657
658 retry_binding:;
659                 ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
660                 if ( LDAP_BACK_CONN_ISBOUND( msc )
661                         || ( LDAP_BACK_CONN_ISANON( msc )
662                                 && mt->mt_idassert_authmethod == LDAP_AUTH_NONE ) )
663                 {
664                         ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
665                         ++bound;
666                         continue;
667
668                 } else if ( LDAP_BACK_CONN_BINDING( msc ) ) {
669                         ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
670                         ldap_pvt_thread_yield();
671                         goto retry_binding;
672
673                 }
674
675                 LDAP_BACK_CONN_BINDING_SET( msc );
676                 ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
677
678                 rc = meta_back_single_dobind( op, rs, &mc, i,
679                         LDAP_BACK_DONTSEND, mt->mt_nretries, 1 );
680                 /*
681                  * NOTE: meta_back_single_dobind() already retries;
682                  * in case of failure, it resets mc...
683                  */
684                 if ( rc != LDAP_SUCCESS ) {
685                         char            buf[ SLAP_TEXT_BUFLEN ];
686
687                         if ( mc == NULL ) {
688                                 /* meta_back_single_dobind() already sent 
689                                  * response and released connection */
690                                 goto send_err;
691                         }
692
693
694                         if ( rc == LDAP_UNAVAILABLE ) {
695                                 /* FIXME: meta_back_retry() already calls
696                                  * meta_back_single_dobind() */
697                                 if ( meta_back_retry( op, rs, &mc, i, sendok ) ) {
698                                         goto retry_ok;
699                                 }
700
701                                 if ( mc != NULL ) {
702                                         ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
703                                         LDAP_BACK_CONN_BINDING_CLEAR( msc );
704                                         ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
705                                 }
706
707                                 return 0;
708                         }
709
710                         ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
711                         LDAP_BACK_CONN_BINDING_CLEAR( msc );
712                         ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
713
714                         snprintf( buf, sizeof( buf ),
715                                 "meta_back_dobind[%d]: (%s) err=%d (%s).",
716                                 i, isroot ? op->o_bd->be_rootdn.bv_val : "anonymous",
717                                 rc, ldap_err2string( rc ) );
718                         Debug( LDAP_DEBUG_ANY,
719                                 "%s %s\n",
720                                 op->o_log_prefix, buf, 0 );
721
722                         /*
723                          * null cred bind should always succeed
724                          * as anonymous, so a failure means
725                          * the target is no longer candidate possibly
726                          * due to technical reasons (remote host down?)
727                          * so better clear the handle
728                          */
729                         /* leave the target candidate, but record the error for later use */
730                         candidates[ i ].sr_err = rc;
731                         if ( META_BACK_ONERR_STOP( mi ) ) {
732                                 bound = 0;
733                                 goto done;
734                         }
735
736                         continue;
737                 } /* else */
738
739 retry_ok:;
740                 Debug( LDAP_DEBUG_TRACE,
741                         "%s meta_back_dobind[%d]: "
742                         "(%s)\n",
743                         op->o_log_prefix, i,
744                         isroot ? op->o_bd->be_rootdn.bv_val : "anonymous" );
745
746                 ldap_pvt_thread_mutex_lock( &mi->mi_conninfo.lai_mutex );
747                 LDAP_BACK_CONN_BINDING_CLEAR( msc );
748                 if ( isroot ) {
749                         LDAP_BACK_CONN_ISBOUND_SET( msc );
750                 } else {
751                         LDAP_BACK_CONN_ISANON_SET( msc );
752                 }
753                 ldap_pvt_thread_mutex_unlock( &mi->mi_conninfo.lai_mutex );
754                 ++bound;
755         }
756
757 done:;
758         Debug( LDAP_DEBUG_TRACE,
759                 "%s meta_back_dobind: conn=%ld bound=%d\n",
760                 op->o_log_prefix, LDAP_BACK_PCONN_ID( mc->mc_conn ), bound );
761
762         if ( bound == 0 ) {
763                 meta_back_release_conn( op, mc );
764
765 send_err:;
766                 if ( sendok & LDAP_BACK_SENDERR ) {
767                         if ( rs->sr_err == LDAP_SUCCESS ) {
768                                 rs->sr_err = LDAP_BUSY;
769                         }
770                         send_ldap_result( op, rs );
771                 }
772
773                 return 0;
774         }
775
776         return ( bound > 0 );
777 }
778
779 /*
780  * meta_back_default_rebind
781  *
782  * This is a callback used for chasing referrals using the same
783  * credentials as the original user on this session.
784  */
785 int 
786 meta_back_default_rebind(
787         LDAP                    *ld,
788         LDAP_CONST char         *url,
789         ber_tag_t               request,
790         ber_int_t               msgid,
791         void                    *params )
792 {
793         metasingleconn_t        *msc = ( metasingleconn_t * )params;
794
795         return ldap_sasl_bind_s( ld, msc->msc_bound_ndn.bv_val,
796                         LDAP_SASL_SIMPLE, &msc->msc_cred,
797                         NULL, NULL, NULL );
798 }
799
800 /*
801  * meta_back_default_urllist
802  *
803  * This is a callback used for mucking with the urllist
804  */
805 int 
806 meta_back_default_urllist(
807         LDAP            *ld,
808         LDAPURLDesc     **urllist,
809         LDAPURLDesc     **url,
810         void            *params )
811 {
812         metatarget_t    *mt = (metatarget_t *)params;
813         LDAPURLDesc     **urltail;
814
815         if ( urllist == url ) {
816                 return LDAP_SUCCESS;
817         }
818
819         for ( urltail = &(*url)->lud_next; *urltail; urltail = &(*urltail)->lud_next )
820                 /* count */ ;
821
822         *urltail = *urllist;
823         *urllist = *url;
824         *url = NULL;
825
826         ldap_pvt_thread_mutex_lock( &mt->mt_uri_mutex );
827         if ( mt->mt_uri ) {
828                 ch_free( mt->mt_uri );
829         }
830
831         ldap_get_option( ld, LDAP_OPT_URI, (void *)&mt->mt_uri );
832         ldap_pvt_thread_mutex_unlock( &mt->mt_uri_mutex );
833
834         return LDAP_SUCCESS;
835 }
836
837 int
838 meta_back_cancel(
839         metaconn_t              *mc,
840         Operation               *op,
841         SlapReply               *rs,
842         ber_int_t               msgid,
843         int                     candidate,
844         ldap_back_send_t        sendok )
845 {
846         metainfo_t              *mi = (metainfo_t *)op->o_bd->be_private;
847
848         metatarget_t            *mt = mi->mi_targets[ candidate ];
849         metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
850
851         int                     rc = LDAP_OTHER;
852
853         Debug( LDAP_DEBUG_TRACE, ">>> %s meta_back_cancel[%d] msgid=%d\n",
854                 op->o_log_prefix, candidate, msgid );
855
856         /* default behavior */
857         if ( META_BACK_TGT_ABANDON( mt ) ) {
858                 rc = ldap_abandon_ext( msc->msc_ld, msgid, NULL, NULL );
859
860         } else if ( META_BACK_TGT_IGNORE( mt ) ) {
861                 rc = LDAP_SUCCESS;
862
863         } else if ( META_BACK_TGT_CANCEL( mt ) ) {
864                 rc = ldap_cancel_s( msc->msc_ld, msgid, NULL, NULL );
865
866         } else {
867                 assert( 0 );
868         }
869
870         Debug( LDAP_DEBUG_TRACE, "<<< %s meta_back_cancel[%d] err=%d\n",
871                 op->o_log_prefix, candidate, rc );
872
873         return rc;
874 }
875
876
877
878 /*
879  * FIXME: error return must be handled in a cleaner way ...
880  */
881 int
882 meta_back_op_result(
883         metaconn_t              *mc,
884         Operation               *op,
885         SlapReply               *rs,
886         int                     candidate,
887         ber_int_t               msgid,
888         time_t                  timeout,
889         ldap_back_send_t        sendok )
890 {
891         metainfo_t      *mi = ( metainfo_t * )op->o_bd->be_private;
892
893         const char      *save_text = rs->sr_text,
894                         *save_matched = rs->sr_matched;
895         BerVarray       save_ref = rs->sr_ref;
896         LDAPControl     **save_ctrls = rs->sr_ctrls;
897         void            *matched_ctx = NULL;
898
899         char            *matched = NULL;
900         char            *text = NULL;
901         char            **refs = NULL;
902         LDAPControl     **ctrls = NULL;
903
904         assert( mc != NULL );
905
906         rs->sr_text = NULL;
907         rs->sr_matched = NULL;
908         rs->sr_ref = NULL;
909         rs->sr_ctrls = NULL;
910
911         if ( candidate != META_TARGET_NONE ) {
912                 metatarget_t            *mt = mi->mi_targets[ candidate ];
913                 metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
914
915 #define ERR_OK(err) ((err) == LDAP_SUCCESS || (err) == LDAP_COMPARE_FALSE || (err) == LDAP_COMPARE_TRUE)
916
917                 if ( ERR_OK( rs->sr_err ) ) {
918                         int             rc;
919                         struct timeval  tv;
920                         LDAPMessage     *res = NULL;
921                         time_t          stoptime = (time_t)(-1);
922                         int             timeout_err = op->o_protocol >= LDAP_VERSION3 ?
923                                                 LDAP_ADMINLIMIT_EXCEEDED : LDAP_OTHER;
924                         const char      *timeout_text = "Operation timed out";
925
926                         /* if timeout is not specified, compute and use
927                          * the one specific to the ongoing operation */
928                         if ( timeout == (time_t)(-1) ) {
929                                 slap_op_t       opidx = slap_req2op( op->o_tag );
930
931                                 if ( opidx == SLAP_OP_SEARCH ) {
932                                         if ( op->ors_tlimit <= 0 ) {
933                                                 timeout = 0;
934
935                                         } else {
936                                                 timeout = op->ors_tlimit;
937                                                 timeout_err = LDAP_TIMELIMIT_EXCEEDED;
938                                                 timeout_text = NULL;
939                                         }
940
941                                 } else {
942                                         timeout = mt->mt_timeout[ opidx ];
943                                 }
944                         }
945
946                         /* better than nothing :) */
947                         if ( timeout == 0 ) {
948                                 if ( mi->mi_idle_timeout ) {
949                                         timeout = mi->mi_idle_timeout;
950
951                                 } else if ( mi->mi_conn_ttl ) {
952                                         timeout = mi->mi_conn_ttl;
953                                 }
954                         }
955
956                         if ( timeout ) {
957                                 stoptime = op->o_time + timeout;
958                         }
959
960                         LDAP_BACK_TV_SET( &tv );
961
962 retry:;
963                         rc = ldap_result( msc->msc_ld, msgid, LDAP_MSG_ALL, &tv, &res );
964                         switch ( rc ) {
965                         case 0:
966                                 if ( timeout && slap_get_time() > stoptime ) {
967                                         (void)meta_back_cancel( mc, op, rs, msgid, candidate, sendok );
968                                         rs->sr_err = timeout_err;
969                                         rs->sr_text = timeout_text;
970                                         break;
971                                 }
972
973                                 LDAP_BACK_TV_SET( &tv );
974                                 ldap_pvt_thread_yield();
975                                 goto retry;
976
977                         case -1:
978                                 ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER,
979                                                 &rs->sr_err );
980                                 break;
981
982
983                         /* otherwise get the result; if it is not
984                          * LDAP_SUCCESS, record it in the reply
985                          * structure (this includes 
986                          * LDAP_COMPARE_{TRUE|FALSE}) */
987                         default:
988                                 /* only touch when activity actually took place... */
989                                 if ( mi->mi_idle_timeout != 0 && msc->msc_time < op->o_time ) {
990                                         msc->msc_time = op->o_time;
991                                 }
992
993                                 rc = ldap_parse_result( msc->msc_ld, res, &rs->sr_err,
994                                                 &matched, &text, &refs, &ctrls, 1 );
995                                 res = NULL;
996                                 rs->sr_text = text;
997                                 if ( rc != LDAP_SUCCESS ) {
998                                         rs->sr_err = rc;
999                                 }
1000                                 if ( refs != NULL ) {
1001                                         int     i;
1002         
1003                                         for ( i = 0; refs[ i ] != NULL; i++ )
1004                                                 /* count */ ;
1005                                         rs->sr_ref = op->o_tmpalloc( sizeof( struct berval ) * ( i + 1 ),
1006                                                 op->o_tmpmemctx );
1007                                         for ( i = 0; refs[ i ] != NULL; i++ ) {
1008                                                 ber_str2bv( refs[ i ], 0, 0, &rs->sr_ref[ i ] );
1009                                         }
1010                                         BER_BVZERO( &rs->sr_ref[ i ] );
1011                                 }
1012                                 if ( ctrls != NULL ) {
1013                                         rs->sr_ctrls = ctrls;
1014                                 }
1015                         }
1016
1017                         assert( res == NULL );
1018                 }
1019
1020                 /* if the error in the reply structure is not
1021                  * LDAP_SUCCESS, try to map it from client 
1022                  * to server error */
1023                 if ( !ERR_OK( rs->sr_err ) ) {
1024                         rs->sr_err = slap_map_api2result( rs );
1025
1026                         /* internal ops ( op->o_conn == NULL ) 
1027                          * must not reply to client */
1028                         if ( op->o_conn && !op->o_do_not_cache && matched ) {
1029
1030                                 /* record the (massaged) matched
1031                                  * DN into the reply structure */
1032                                 rs->sr_matched = matched;
1033                         }
1034                 }
1035
1036                 if ( META_BACK_TGT_QUARANTINE( mt ) ) {
1037                         meta_back_quarantine( op, rs, candidate );
1038                 }
1039
1040         } else {
1041                 int     i,
1042                         err = rs->sr_err;
1043
1044                 for ( i = 0; i < mi->mi_ntargets; i++ ) {
1045                         metasingleconn_t        *msc = &mc->mc_conns[ i ];
1046                         char                    *xtext = NULL;
1047                         char                    *xmatched = NULL;
1048
1049                         rs->sr_err = LDAP_SUCCESS;
1050
1051                         ldap_get_option( msc->msc_ld, LDAP_OPT_ERROR_NUMBER, &rs->sr_err );
1052                         if ( rs->sr_err != LDAP_SUCCESS ) {
1053                                 /*
1054                                  * better check the type of error. In some cases
1055                                  * (search ?) it might be better to return a
1056                                  * success if at least one of the targets gave
1057                                  * positive result ...
1058                                  */
1059                                 ldap_get_option( msc->msc_ld,
1060                                                 LDAP_OPT_ERROR_STRING, &xtext );
1061                                 if ( xtext != NULL && xtext [ 0 ] == '\0' ) {
1062                                         ldap_memfree( xtext );
1063                                         xtext = NULL;
1064                                 }
1065
1066                                 ldap_get_option( msc->msc_ld,
1067                                                 LDAP_OPT_MATCHED_DN, &xmatched );
1068                                 if ( xmatched != NULL && xmatched[ 0 ] == '\0' ) {
1069                                         ldap_memfree( xmatched );
1070                                         xmatched = NULL;
1071                                 }
1072
1073                                 rs->sr_err = slap_map_api2result( rs );
1074         
1075                                 if ( LogTest( LDAP_DEBUG_ANY ) ) {
1076                                         char    buf[ SLAP_TEXT_BUFLEN ];
1077
1078                                         snprintf( buf, sizeof( buf ),
1079                                                 "meta_back_op_result[%d] "
1080                                                 "err=%d text=\"%s\" matched=\"%s\"", 
1081                                                 i, rs->sr_err,
1082                                                 ( xtext ? xtext : "" ),
1083                                                 ( xmatched ? xmatched : "" ) );
1084                                         Debug( LDAP_DEBUG_ANY, "%s %s.\n",
1085                                                 op->o_log_prefix, buf, 0 );
1086                                 }
1087
1088                                 /*
1089                                  * FIXME: need to rewrite "match" (need rwinfo)
1090                                  */
1091                                 switch ( rs->sr_err ) {
1092                                 default:
1093                                         err = rs->sr_err;
1094                                         if ( xtext != NULL ) {
1095                                                 if ( text ) {
1096                                                         ldap_memfree( text );
1097                                                 }
1098                                                 text = xtext;
1099                                                 xtext = NULL;
1100                                         }
1101                                         if ( xmatched != NULL ) {
1102                                                 if ( matched ) {
1103                                                         ldap_memfree( matched );
1104                                                 }
1105                                                 matched = xmatched;
1106                                                 xmatched = NULL;
1107                                         }
1108                                         break;
1109                                 }
1110
1111                                 if ( xtext ) {
1112                                         ldap_memfree( xtext );
1113                                 }
1114         
1115                                 if ( xmatched ) {
1116                                         ldap_memfree( xmatched );
1117                                 }
1118                         }
1119
1120                         if ( META_BACK_TGT_QUARANTINE( mi->mi_targets[ i ] ) ) {
1121                                 meta_back_quarantine( op, rs, i );
1122                         }
1123                 }
1124
1125                 if ( err != LDAP_SUCCESS ) {
1126                         rs->sr_err = err;
1127                 }
1128         }
1129
1130         if ( matched != NULL ) {
1131                 struct berval   dn, pdn;
1132
1133                 ber_str2bv( matched, 0, 0, &dn );
1134                 if ( dnPretty( NULL, &dn, &pdn, op->o_tmpmemctx ) == LDAP_SUCCESS ) {
1135                         ldap_memfree( matched );
1136                         matched_ctx = op->o_tmpmemctx;
1137                         matched = pdn.bv_val;
1138                 }
1139                 rs->sr_matched = matched;
1140         }
1141
1142         if ( op->o_conn &&
1143                 ( ( sendok & LDAP_BACK_SENDOK ) 
1144                         || ( ( sendok & LDAP_BACK_SENDERR ) && rs->sr_err != LDAP_SUCCESS ) ) )
1145         {
1146                 send_ldap_result( op, rs );
1147         }
1148         if ( matched ) {
1149                 op->o_tmpfree( (char *)rs->sr_matched, matched_ctx );
1150         }
1151         if ( text ) {
1152                 ldap_memfree( text );
1153         }
1154         if ( rs->sr_ref ) {
1155                 assert( refs != NULL );
1156                 ber_memvfree( (void **)refs );
1157                 op->o_tmpfree( rs->sr_ref, op->o_tmpmemctx );
1158         }
1159         if ( ctrls ) {
1160                 assert( rs->sr_ctrls != NULL );
1161                 ldap_controls_free( ctrls );
1162         }
1163
1164         rs->sr_text = save_text;
1165         rs->sr_matched = save_matched;
1166         rs->sr_ref = save_ref;
1167         rs->sr_ctrls = save_ctrls;
1168
1169         return( ERR_OK( rs->sr_err ) ? LDAP_SUCCESS : rs->sr_err );
1170 }
1171
1172 /*
1173  * meta_back_proxy_authz_cred()
1174  *
1175  * prepares credentials & method for meta_back_proxy_authz_bind();
1176  * or, if method is SASL, performs the SASL bind directly.
1177  */
1178 int
1179 meta_back_proxy_authz_cred(
1180         metaconn_t              *mc,
1181         int                     candidate,
1182         Operation               *op,
1183         SlapReply               *rs,
1184         ldap_back_send_t        sendok,
1185         struct berval           *binddn,
1186         struct berval           *bindcred,
1187         int                     *method )
1188 {
1189         metainfo_t              *mi = (metainfo_t *)op->o_bd->be_private;
1190         metatarget_t            *mt = mi->mi_targets[ candidate ];
1191         metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
1192         struct berval           ndn;
1193         int                     dobind = 0;
1194
1195         /* don't proxyAuthz if protocol is not LDAPv3 */
1196         switch ( mt->mt_version ) {
1197         case LDAP_VERSION3:
1198                 break;
1199
1200         case 0:
1201                 if ( op->o_protocol == 0 || op->o_protocol == LDAP_VERSION3 ) {
1202                         break;
1203                 }
1204                 /* fall thru */
1205
1206         default:
1207                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1208                 if ( sendok & LDAP_BACK_SENDERR ) {
1209                         send_ldap_result( op, rs );
1210                 }
1211                 LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
1212                 goto done;
1213         }
1214
1215         if ( op->o_tag == LDAP_REQ_BIND ) {
1216                 ndn = op->o_req_ndn;
1217
1218         } else if ( !BER_BVISNULL( &op->o_conn->c_ndn ) ) {
1219                 ndn = op->o_conn->c_ndn;
1220
1221         } else {
1222                 ndn = op->o_ndn;
1223         }
1224
1225         /*
1226          * FIXME: we need to let clients use proxyAuthz
1227          * otherwise we cannot do symmetric pools of servers;
1228          * we have to live with the fact that a user can
1229          * authorize itself as any ID that is allowed
1230          * by the authzTo directive of the "proxyauthzdn".
1231          */
1232         /*
1233          * NOTE: current Proxy Authorization specification
1234          * and implementation do not allow proxy authorization
1235          * control to be provided with Bind requests
1236          */
1237         /*
1238          * if no bind took place yet, but the connection is bound
1239          * and the "proxyauthzdn" is set, then bind as 
1240          * "proxyauthzdn" and explicitly add the proxyAuthz 
1241          * control to every operation with the dn bound 
1242          * to the connection as control value.
1243          */
1244
1245         /* bind as proxyauthzdn only if no idassert mode
1246          * is requested, or if the client's identity
1247          * is authorized */
1248         switch ( mt->mt_idassert_mode ) {
1249         case LDAP_BACK_IDASSERT_LEGACY:
1250                 if ( !BER_BVISNULL( &ndn ) && !BER_BVISEMPTY( &ndn ) ) {
1251                         if ( !BER_BVISNULL( &mt->mt_idassert_authcDN ) && !BER_BVISEMPTY( &mt->mt_idassert_authcDN ) )
1252                         {
1253                                 *binddn = mt->mt_idassert_authcDN;
1254                                 *bindcred = mt->mt_idassert_passwd;
1255                                 dobind = 1;
1256                         }
1257                 }
1258                 break;
1259
1260         default:
1261                 /* NOTE: rootdn can always idassert */
1262                 if ( BER_BVISNULL( &ndn ) && mt->mt_idassert_authz == NULL ) {
1263                         if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
1264                                 rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
1265                                 if ( sendok & LDAP_BACK_SENDERR ) {
1266                                         send_ldap_result( op, rs );
1267                                 }
1268                                 LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
1269
1270                         } else {
1271                                 rs->sr_err = LDAP_SUCCESS;
1272                                 *binddn = slap_empty_bv;
1273                                 *bindcred = slap_empty_bv;
1274                                 break;
1275                         }
1276
1277                         goto done;
1278
1279                 } else if ( mt->mt_idassert_authz && !be_isroot( op ) ) {
1280                         struct berval authcDN;
1281
1282                         if ( BER_BVISNULL( &ndn ) ) {
1283                                 authcDN = slap_empty_bv;
1284
1285                         } else {
1286                                 authcDN = ndn;
1287                         }       
1288                         rs->sr_err = slap_sasl_matches( op, mt->mt_idassert_authz,
1289                                         &authcDN, &authcDN );
1290                         if ( rs->sr_err != LDAP_SUCCESS ) {
1291                                 if ( mt->mt_idassert_flags & LDAP_BACK_AUTH_PRESCRIPTIVE ) {
1292                                         if ( sendok & LDAP_BACK_SENDERR ) {
1293                                                 send_ldap_result( op, rs );
1294                                         }
1295                                         LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
1296
1297                                 } else {
1298                                         rs->sr_err = LDAP_SUCCESS;
1299                                         *binddn = slap_empty_bv;
1300                                         *bindcred = slap_empty_bv;
1301                                         break;
1302                                 }
1303
1304                                 goto done;
1305                         }
1306                 }
1307
1308                 *binddn = mt->mt_idassert_authcDN;
1309                 *bindcred = mt->mt_idassert_passwd;
1310                 dobind = 1;
1311                 break;
1312         }
1313
1314         if ( dobind && mt->mt_idassert_authmethod == LDAP_AUTH_SASL ) {
1315 #ifdef HAVE_CYRUS_SASL
1316                 void            *defaults = NULL;
1317                 struct berval   authzID = BER_BVNULL;
1318                 int             freeauthz = 0;
1319
1320                 /* if SASL supports native authz, prepare for it */
1321                 if ( ( !op->o_do_not_cache || !op->o_is_auth_check ) &&
1322                                 ( mt->mt_idassert_flags & LDAP_BACK_AUTH_NATIVE_AUTHZ ) )
1323                 {
1324                         switch ( mt->mt_idassert_mode ) {
1325                         case LDAP_BACK_IDASSERT_OTHERID:
1326                         case LDAP_BACK_IDASSERT_OTHERDN:
1327                                 authzID = mt->mt_idassert_authzID;
1328                                 break;
1329
1330                         case LDAP_BACK_IDASSERT_ANONYMOUS:
1331                                 BER_BVSTR( &authzID, "dn:" );
1332                                 break;
1333
1334                         case LDAP_BACK_IDASSERT_SELF:
1335                                 if ( BER_BVISNULL( &ndn ) ) {
1336                                         /* connection is not authc'd, so don't idassert */
1337                                         BER_BVSTR( &authzID, "dn:" );
1338                                         break;
1339                                 }
1340                                 authzID.bv_len = STRLENOF( "dn:" ) + ndn.bv_len;
1341                                 authzID.bv_val = slap_sl_malloc( authzID.bv_len + 1, op->o_tmpmemctx );
1342                                 AC_MEMCPY( authzID.bv_val, "dn:", STRLENOF( "dn:" ) );
1343                                 AC_MEMCPY( authzID.bv_val + STRLENOF( "dn:" ),
1344                                                 ndn.bv_val, ndn.bv_len + 1 );
1345                                 freeauthz = 1;
1346                                 break;
1347
1348                         default:
1349                                 break;
1350                         }
1351                 }
1352
1353                 if ( mt->mt_idassert_secprops != NULL ) {
1354                         rs->sr_err = ldap_set_option( msc->msc_ld,
1355                                 LDAP_OPT_X_SASL_SECPROPS,
1356                                 (void *)mt->mt_idassert_secprops );
1357
1358                         if ( rs->sr_err != LDAP_OPT_SUCCESS ) {
1359                                 rs->sr_err = LDAP_OTHER;
1360                                 if ( sendok & LDAP_BACK_SENDERR ) {
1361                                         send_ldap_result( op, rs );
1362                                 }
1363                                 LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
1364                                 goto done;
1365                         }
1366                 }
1367
1368                 defaults = lutil_sasl_defaults( msc->msc_ld,
1369                                 mt->mt_idassert_sasl_mech.bv_val,
1370                                 mt->mt_idassert_sasl_realm.bv_val,
1371                                 mt->mt_idassert_authcID.bv_val,
1372                                 mt->mt_idassert_passwd.bv_val,
1373                                 authzID.bv_val );
1374
1375                 rs->sr_err = ldap_sasl_interactive_bind_s( msc->msc_ld, binddn->bv_val,
1376                                 mt->mt_idassert_sasl_mech.bv_val, NULL, NULL,
1377                                 LDAP_SASL_QUIET, lutil_sasl_interact,
1378                                 defaults );
1379
1380                 rs->sr_err = slap_map_api2result( rs );
1381                 if ( rs->sr_err != LDAP_SUCCESS ) {
1382                         LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
1383                         if ( sendok & LDAP_BACK_SENDERR ) {
1384                                 send_ldap_result( op, rs );
1385                         }
1386
1387                 } else {
1388                         LDAP_BACK_CONN_ISBOUND_SET( msc );
1389                 }
1390
1391                 lutil_sasl_freedefs( defaults );
1392                 if ( freeauthz ) {
1393                         slap_sl_free( authzID.bv_val, op->o_tmpmemctx );
1394                 }
1395
1396                 goto done;
1397 #endif /* HAVE_CYRUS_SASL */
1398         }
1399
1400         *method = mt->mt_idassert_authmethod;
1401         switch ( mt->mt_idassert_authmethod ) {
1402         case LDAP_AUTH_NONE:
1403                 BER_BVSTR( binddn, "" );
1404                 BER_BVSTR( bindcred, "" );
1405                 /* fallthru */
1406
1407         case LDAP_AUTH_SIMPLE:
1408                 break;
1409
1410         default:
1411                 /* unsupported! */
1412                 LDAP_BACK_CONN_ISBOUND_CLEAR( msc );
1413                 rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
1414                 if ( sendok & LDAP_BACK_SENDERR ) {
1415                         send_ldap_result( op, rs );
1416                 }
1417                 break;
1418         }
1419
1420 done:;
1421         return rs->sr_err;
1422 }
1423
1424 static int
1425 meta_back_proxy_authz_bind( metaconn_t *mc, int candidate, Operation *op, SlapReply *rs, ldap_back_send_t sendok )
1426 {
1427         metainfo_t              *mi = (metainfo_t *)op->o_bd->be_private;
1428         metatarget_t            *mt = mi->mi_targets[ candidate ];
1429         metasingleconn_t        *msc = &mc->mc_conns[ candidate ];
1430         struct berval           binddn = BER_BVC( "" ),
1431                                 cred = BER_BVC( "" );
1432         int                     method = LDAP_AUTH_NONE,
1433                                 rc;
1434
1435         rc = meta_back_proxy_authz_cred( mc, candidate, op, rs, sendok, &binddn, &cred, &method );
1436         if ( rc == LDAP_SUCCESS && !LDAP_BACK_CONN_ISBOUND( msc ) ) {
1437                 int     msgid;
1438
1439                 switch ( method ) {
1440                 case LDAP_AUTH_NONE:
1441                 case LDAP_AUTH_SIMPLE:
1442                         rs->sr_err = ldap_sasl_bind( msc->msc_ld,
1443                                         binddn.bv_val, LDAP_SASL_SIMPLE,
1444                                         &cred, NULL, NULL, &msgid );
1445                         rc = meta_back_bind_op_result( op, rs, mc, candidate, msgid, sendok );
1446                         if ( rc == LDAP_SUCCESS ) {
1447                                 /* set rebind stuff in case of successful proxyAuthz bind,
1448                                  * so that referral chasing is attempted using the right
1449                                  * identity */
1450                                 LDAP_BACK_CONN_ISBOUND_SET( msc );
1451                                 ber_bvreplace( &msc->msc_bound_ndn, &binddn );
1452
1453                                 if ( LDAP_BACK_SAVECRED( mi ) ) {
1454                                         if ( !BER_BVISNULL( &msc->msc_cred ) ) {
1455                                                 memset( msc->msc_cred.bv_val, 0,
1456                                                         msc->msc_cred.bv_len );
1457                                         }
1458                                         ber_bvreplace( &msc->msc_cred, &cred );
1459                                         ldap_set_rebind_proc( msc->msc_ld, mt->mt_rebind_f, msc );
1460                                 }
1461                         }
1462                         break;
1463
1464                 default:
1465                         assert( 0 );
1466                         break;
1467                 }
1468         }
1469
1470         return LDAP_BACK_CONN_ISBOUND( msc );
1471 }