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