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