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