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