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