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