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