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