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