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