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