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