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