1 /* $NetBSD: bind.c,v 1.2 2021/08/14 16:14:58 christos Exp $ */
2
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5 *
6 * Copyright 1998-2021 The OpenLDAP Foundation.
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
18 #include <sys/cdefs.h>
19 __RCSID("$NetBSD: bind.c,v 1.2 2021/08/14 16:14:58 christos Exp $");
20
21 #include "portable.h"
22
23 #include <ac/socket.h>
24 #include <ac/errno.h>
25 #include <ac/string.h>
26 #include <ac/time.h>
27 #include <ac/unistd.h>
28
29 #include "lutil.h"
30 #include "lload.h"
31
32 struct berval mech_external = BER_BVC("EXTERNAL");
33
34 int
bind_mech_external(LloadConnection * client,LloadOperation * op,struct berval * credentials)35 bind_mech_external(
36 LloadConnection *client,
37 LloadOperation *op,
38 struct berval *credentials )
39 {
40 BerValue binddn;
41 void *ssl;
42 char *ptr, *message = "";
43 int result = LDAP_SUCCESS;
44
45 CONNECTION_ASSERT_LOCKED(client);
46 client->c_state = LLOAD_C_READY;
47 client->c_type = LLOAD_C_OPEN;
48
49 op->o_res = LLOAD_OP_COMPLETED;
50
51 /*
52 * We only support implicit assertion.
53 *
54 * Although RFC 4513 says the credentials field must be missing, RFC 4422
55 * doesn't and libsasl2 will pass a zero-length string to send. We have to
56 * allow that.
57 */
58 if ( !BER_BVISEMPTY( credentials ) ) {
59 result = LDAP_UNWILLING_TO_PERFORM;
60 message = "proxy authorization is not supported";
61 goto done;
62 }
63
64 #ifdef HAVE_TLS
65 ssl = ldap_pvt_tls_sb_ctx( client->c_sb );
66 if ( !ssl || ldap_pvt_tls_get_peer_dn( ssl, &binddn, NULL, 0 ) ) {
67 result = LDAP_INVALID_CREDENTIALS;
68 message = "no externally negotiated identity";
69 goto done;
70 }
71 client->c_auth.bv_len = binddn.bv_len + STRLENOF("dn:");
72 client->c_auth.bv_val = ch_malloc( client->c_auth.bv_len + 1 );
73
74 ptr = lutil_strcopy( client->c_auth.bv_val, "dn:" );
75 ptr = lutil_strncopy( ptr, binddn.bv_val, binddn.bv_len );
76 *ptr = '\0';
77
78 ber_memfree( binddn.bv_val );
79
80 if ( !ber_bvstrcasecmp( &client->c_auth, &lloadd_identity ) ) {
81 client->c_type = LLOAD_C_PRIVILEGED;
82 }
83 #else /* ! HAVE_TLS */
84 result = LDAP_AUTH_METHOD_NOT_SUPPORTED;
85 message = "requested SASL mechanism not supported";
86 #endif /* ! HAVE_TLS */
87
88 done:
89 CONNECTION_UNLOCK(client);
90 operation_send_reject( op, result, message, 1 );
91 return LDAP_SUCCESS;
92 }
93
94 static int
client_bind(LloadOperation * op,LloadConnection * upstream,struct berval * binddn,ber_tag_t tag,struct berval * auth)95 client_bind(
96 LloadOperation *op,
97 LloadConnection *upstream,
98 struct berval *binddn,
99 ber_tag_t tag,
100 struct berval *auth )
101 {
102 ber_printf( upstream->c_pendingber, "t{titOtO}", LDAP_TAG_MESSAGE,
103 LDAP_TAG_MSGID, op->o_upstream_msgid,
104 LDAP_REQ_BIND, &op->o_request,
105 LDAP_TAG_CONTROLS, BER_BV_OPTIONAL( &op->o_ctrls ) );
106
107 return 0;
108 }
109
110 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
111 static int
client_bind_as_vc(LloadOperation * op,LloadConnection * upstream,struct berval * binddn,ber_tag_t tag,struct berval * auth)112 client_bind_as_vc(
113 LloadOperation *op,
114 LloadConnection *upstream,
115 struct berval *binddn,
116 ber_tag_t tag,
117 struct berval *auth )
118 {
119 CONNECTION_LOCK(upstream);
120 ber_printf( upstream->c_pendingber, "t{tit{tst{{tOOtOtO}}}}", LDAP_TAG_MESSAGE,
121 LDAP_TAG_MSGID, op->o_upstream_msgid,
122 LDAP_REQ_EXTENDED,
123 LDAP_TAG_EXOP_REQ_OID, LDAP_EXOP_VERIFY_CREDENTIALS,
124 LDAP_TAG_EXOP_REQ_VALUE,
125 LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE, BER_BV_OPTIONAL( &upstream->c_vc_cookie ),
126 &binddn, tag, &auth,
127 LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS, BER_BV_OPTIONAL( &op->o_ctrls ) );
128 CONNECTION_UNLOCK(upstream);
129 return 0;
130 }
131 #endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
132
133 /*
134 * The client connection can be in the following states:
135 * 1) there are between zero and many non-bind operations pending
136 * client->c_state == LLOAD_C_READY && client->c_pin_id == 0
137 * 2) there is one bind operation pending (waiting on an upstream response)
138 * a) It is a simple bind
139 * b) It is a SASL bind
140 * 3) there is one SASL bind in progress (received a LDAP_SASL_BIND_IN_PROGRESS
141 * response)
142 *
143 * In cases 2 and 3, client->c_state == LLOAD_C_BINDING, a SASL bind is in
144 * progress/pending if c_sasl_bind_mech is set.
145 *
146 * In the first case, client_reset abandons all operations on the respective
147 * upstreams, case 2a has client_reset send an anonymous bind to upstream to
148 * terminate the bind. In cases 2b and 3, c_pin_id is set and we retrieve the
149 * op. The rest is the same for both.
150 *
151 * If c_pin_id is unset, we request an upstream connection assigned, otherwise,
152 * we try to reuse the pinned upstream. In the case of no upstream, we reject
153 * the request. A SASL bind request means we acquire a new pin_id if we don't
154 * have one already.
155 *
156 * We have to reset c_auth (which holds the current or pending identity) and
157 * make sure we set it up eventually:
158 * - In the case of a simple bind, we already know the final identity being
159 * requested so we set it up immediately
160 * - In SASL binds, for mechanisms we implement ourselves (EXTERNAL), we set it
161 * up at some point
162 * - Otherwise, we have to ask the upstream what it thinks as the bind
163 * succeeds, we send an LDAP "Who Am I?" exop, this is one of the few
164 * requests we send on our own. If we implement the mechanism, we provide the
165 * identity (EXTERNAL uses the client certificate DN)
166 *
167 * At the end of the request processing, if nothing goes wrong, we're in state
168 * 2b (with c_pin_id set to the op's o_pin_id), or state 2a (we could reset
169 * c_pin_id/o_pin_id if we wanted but we don't always do that at the moment).
170 * If something does go wrong, we're either tearing down the client or we
171 * reject the request and switch to state 1 (clearing c_pin_id).
172 *
173 * As usual, we have to make any changes to the target connection before we've
174 * sent the PDU over it - while we are in charge of the read side and nothing
175 * happens there without our ceding control, the other read side could wake up
176 * at any time and preempt us.
177 *
178 * On a response (in handle_bind_response):
179 * - to a simple bind, clear c_auth on a failure otherwise keep it while we
180 * just reset the client to state 1
181 * - failure response to a SASL bind - reset client to state 1
182 * - LDAP_SASL_BIND_IN_PROGRESS - clear o_*_msgid from the op (have to
183 * remove+reinsert it from the respective c_ops!), we need it since it is the
184 * vessel maintaining the pin between client and upstream
185 * - all of the above forward the response immediately
186 * - LDAP_SUCCESS for a SASL bind - we send a "Who Am I?" request to retrieve
187 * the client's DN, only on receiving the response do we finalise the
188 * exchange by forwarding the successful bind response
189 *
190 * We can't do the same for VC Exop since the exchange is finished at the end
191 * and we need a change to the VC Exop spec to have the server (optionally?)
192 * respond with the final authzid (saving us a roundtrip as well).
193 */
194 int
request_bind(LloadConnection * client,LloadOperation * op)195 request_bind( LloadConnection *client, LloadOperation *op )
196 {
197 LloadConnection *upstream = NULL;
198 BerElement *ber, *copy;
199 struct berval binddn, auth, mech = BER_BVNULL;
200 ber_int_t version;
201 ber_tag_t tag;
202 unsigned long pin;
203 int res, rc = LDAP_SUCCESS;
204
205 CONNECTION_LOCK(client);
206 pin = client->c_pin_id;
207
208 if ( pin ) {
209 LloadOperation *pinned_op, needle = {
210 .o_client_connid = client->c_connid,
211 .o_client_msgid = 0,
212 .o_pin_id = client->c_pin_id,
213 };
214
215 Debug( LDAP_DEBUG_CONNS, "request_bind: "
216 "client connid=%lu is pinned pin=%lu\n",
217 client->c_connid, pin );
218
219 pinned_op =
220 ldap_tavl_delete( &client->c_ops, &needle, operation_client_cmp );
221 if ( pinned_op ) {
222 assert( op->o_tag == pinned_op->o_tag );
223
224 pinned_op->o_client_msgid = op->o_client_msgid;
225
226 /* Preserve the new BerElement and its pointers, reclaim the old
227 * one in operation_destroy_from_client if it's still there */
228 needle.o_ber = pinned_op->o_ber;
229 pinned_op->o_ber = op->o_ber;
230 op->o_ber = needle.o_ber;
231
232 pinned_op->o_request = op->o_request;
233 pinned_op->o_ctrls = op->o_ctrls;
234
235 /* No one has seen this operation yet, plant the pin back in its stead */
236 client->c_n_ops_executing--;
237 op->o_res = LLOAD_OP_COMPLETED;
238 ldap_tavl_delete( &client->c_ops, op, operation_client_cmp );
239 op->o_client = NULL;
240 assert( op->o_upstream == NULL );
241
242 rc = ldap_tavl_insert( &client->c_ops, pinned_op, operation_client_cmp,
243 ldap_avl_dup_error );
244 assert( rc == LDAP_SUCCESS );
245
246 /* No one has seen this operation yet */
247 op->o_refcnt--;
248 operation_destroy( op );
249
250 /* We didn't start a new operation, just continuing an existing one */
251 lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_received--;
252
253 op = pinned_op;
254 }
255 }
256
257 ldap_tavl_delete( &client->c_ops, op, operation_client_cmp );
258 client->c_n_ops_executing--;
259
260 client_reset( client );
261
262 client->c_state = LLOAD_C_BINDING;
263 client->c_type = LLOAD_C_OPEN;
264
265 if ( (copy = ber_alloc()) == NULL ) {
266 goto fail;
267 }
268 ber_init2( copy, &op->o_request, 0 );
269
270 tag = ber_get_int( copy, &version );
271 if ( tag == LBER_ERROR ) {
272 Debug( LDAP_DEBUG_PACKETS, "request_bind: "
273 "failed to parse version field\n" );
274 goto fail;
275 } else if ( version != LDAP_VERSION3 ) {
276 CONNECTION_UNLOCK(client);
277 operation_send_reject(
278 op, LDAP_PROTOCOL_ERROR, "LDAP version unsupported", 1 );
279 CONNECTION_LOCK(client);
280 goto fail;
281 }
282
283 tag = ber_get_stringbv( copy, &binddn, LBER_BV_NOTERM );
284 if ( tag == LBER_ERROR ) {
285 Debug( LDAP_DEBUG_PACKETS, "request_bind: "
286 "failed to parse bind name field\n" );
287 goto fail;
288 }
289
290 if ( !BER_BVISNULL( &client->c_auth ) ) {
291 ch_free( client->c_auth.bv_val );
292 BER_BVZERO( &client->c_auth );
293 }
294
295 tag = ber_skip_element( copy, &auth );
296 if ( tag == LDAP_AUTH_SIMPLE ) {
297 if ( !BER_BVISEMPTY( &binddn ) ) {
298 char *ptr;
299 client->c_auth.bv_len = STRLENOF("dn:") + binddn.bv_len;
300 client->c_auth.bv_val = ch_malloc( client->c_auth.bv_len + 1 );
301
302 ptr = lutil_strcopy( client->c_auth.bv_val, "dn:" );
303 ptr = lutil_strncopy( ptr, binddn.bv_val, binddn.bv_len );
304 *ptr = '\0';
305 }
306
307 if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
308 ber_memfree( client->c_sasl_bind_mech.bv_val );
309 BER_BVZERO( &client->c_sasl_bind_mech );
310 }
311 } else if ( tag == LDAP_AUTH_SASL ) {
312 ber_init2( copy, &auth, 0 );
313
314 if ( ber_get_stringbv( copy, &mech, LBER_BV_NOTERM ) == LBER_ERROR ) {
315 goto fail;
316 }
317 if ( !ber_bvcmp( &mech, &mech_external ) ) {
318 struct berval credentials = BER_BVNULL;
319
320 ber_get_stringbv( copy, &credentials, LBER_BV_NOTERM );
321 rc = bind_mech_external( client, op, &credentials );
322
323 /* terminate the upstream side if client switched mechanisms */
324 if ( pin ) {
325 operation_abandon( op );
326 }
327
328 ber_free( copy, 0 );
329 return rc;
330 } else if ( BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
331 ber_dupbv( &client->c_sasl_bind_mech, &mech );
332 } else if ( ber_bvcmp( &mech, &client->c_sasl_bind_mech ) ) {
333 ber_bvreplace( &client->c_sasl_bind_mech, &mech );
334 }
335 } else {
336 goto fail;
337 }
338
339 rc = ldap_tavl_insert( &client->c_ops, op, operation_client_cmp, ldap_avl_dup_error );
340 assert( rc == LDAP_SUCCESS );
341 client->c_n_ops_executing++;
342 CONNECTION_UNLOCK(client);
343
344 if ( pin ) {
345 checked_lock( &op->o_link_mutex );
346 upstream = op->o_upstream;
347 checked_unlock( &op->o_link_mutex );
348
349 if ( upstream ) {
350 checked_lock( &upstream->c_io_mutex );
351 CONNECTION_LOCK(upstream);
352 if ( !IS_ALIVE( upstream, c_live ) ) {
353 CONNECTION_UNLOCK(upstream);
354 checked_unlock( &upstream->c_io_mutex );
355 upstream = NULL;
356 }
357 }
358 }
359
360 /* If we were pinned but lost the link, don't look for a new upstream, we
361 * have to reject the op and clear pin */
362 if ( upstream ) {
363 /* No need to do anything */
364 } else if ( !pin ) {
365 upstream = backend_select( op, &res );
366 } else {
367 Debug( LDAP_DEBUG_STATS, "request_bind: "
368 "connid=%lu, msgid=%d pinned upstream lost\n",
369 op->o_client_connid, op->o_client_msgid );
370 operation_send_reject( op, LDAP_OTHER,
371 "connection to the remote server has been severed", 1 );
372 pin = 0;
373 goto done;
374 }
375
376 if ( !upstream ) {
377 Debug( LDAP_DEBUG_STATS, "request_bind: "
378 "connid=%lu, msgid=%d no available connection found\n",
379 op->o_client_connid, op->o_client_msgid );
380 operation_send_reject( op, res, "no connections available", 1 );
381 assert( client->c_pin_id == 0 );
382 goto done;
383 }
384 assert_locked( &upstream->c_io_mutex );
385 /*
386 * At this point, either:
387 * - upstream is READY and pin == 0
388 * - upstream is BINDING, pin != 0 and op->o_upstream_msgid == 0
389 *
390 * A pinned upstream we marked for closing at some point ago should have
391 * closed by now.
392 */
393
394 ber = upstream->c_pendingber;
395 if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
396 checked_unlock( &upstream->c_io_mutex );
397 if ( !pin ) {
398 LloadBackend *b = upstream->c_backend;
399
400 upstream->c_n_ops_executing--;
401 CONNECTION_UNLOCK(upstream);
402
403 checked_lock( &b->b_mutex );
404 b->b_n_ops_executing--;
405 operation_update_backend_counters( op, b );
406 checked_unlock( &b->b_mutex );
407 } else {
408 CONNECTION_UNLOCK(upstream);
409 }
410
411 Debug( LDAP_DEBUG_ANY, "request_bind: "
412 "ber_alloc failed\n" );
413
414 operation_unlink( op );
415
416 CONNECTION_LOCK(client);
417 goto fail;
418 }
419 upstream->c_pendingber = ber;
420
421 if ( !pin ) {
422 lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_forwarded++;
423 }
424
425 if ( pin ) {
426 ldap_tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
427 if ( tag == LDAP_AUTH_SIMPLE ) {
428 pin = op->o_pin_id = 0;
429 }
430 } else if ( tag == LDAP_AUTH_SASL && !op->o_pin_id ) {
431 checked_lock( &lload_pin_mutex );
432 pin = op->o_pin_id = lload_next_pin++;
433 Debug( LDAP_DEBUG_CONNS, "request_bind: "
434 "client connid=%lu allocated pin=%lu linking it to upstream "
435 "connid=%lu\n",
436 op->o_client_connid, pin, upstream->c_connid );
437 checked_unlock( &lload_pin_mutex );
438 }
439
440 op->o_upstream = upstream;
441 op->o_upstream_connid = upstream->c_connid;
442 op->o_upstream_msgid = upstream->c_next_msgid++;
443 op->o_res = LLOAD_OP_FAILED;
444
445 /* Was it unlinked in the meantime? No need to send a response since the
446 * client is dead */
447 if ( !IS_ALIVE( op, o_refcnt ) ) {
448 LloadBackend *b = upstream->c_backend;
449
450 upstream->c_n_ops_executing--;
451 checked_unlock( &upstream->c_io_mutex );
452 CONNECTION_UNLOCK(upstream);
453
454 checked_lock( &b->b_mutex );
455 b->b_n_ops_executing--;
456 checked_unlock( &b->b_mutex );
457
458 assert( !IS_ALIVE( client, c_live ) );
459 checked_lock( &op->o_link_mutex );
460 if ( op->o_upstream ) {
461 op->o_upstream = NULL;
462 }
463 checked_unlock( &op->o_link_mutex );
464 rc = -1;
465 goto done;
466 }
467
468 if ( BER_BVISNULL( &mech ) ) {
469 if ( !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) {
470 ber_memfree( upstream->c_sasl_bind_mech.bv_val );
471 BER_BVZERO( &upstream->c_sasl_bind_mech );
472 }
473 } else if ( ber_bvcmp( &upstream->c_sasl_bind_mech, &mech ) ) {
474 ber_bvreplace( &upstream->c_sasl_bind_mech, &mech );
475 }
476
477 Debug( LDAP_DEBUG_TRACE, "request_bind: "
478 "added bind from client connid=%lu to upstream connid=%lu "
479 "as msgid=%d\n",
480 op->o_client_connid, op->o_upstream_connid, op->o_upstream_msgid );
481 if ( ldap_tavl_insert( &upstream->c_ops, op, operation_upstream_cmp,
482 ldap_avl_dup_error ) ) {
483 assert(0);
484 }
485 upstream->c_state = LLOAD_C_BINDING;
486 CONNECTION_UNLOCK(upstream);
487
488 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
489 if ( lload_features & LLOAD_FEATURE_VC ) {
490 rc = client_bind_as_vc( op, upstream, &binddn, tag, &auth );
491 } else
492 #endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
493 {
494 rc = client_bind( op, upstream, &binddn, tag, &auth );
495 }
496 checked_unlock( &upstream->c_io_mutex );
497
498 done:
499
500 CONNECTION_LOCK(client);
501 if ( rc == LDAP_SUCCESS ) {
502 client->c_pin_id = pin;
503 CONNECTION_UNLOCK(client);
504
505 if ( upstream ) {
506 connection_write_cb( -1, 0, upstream );
507 }
508 } else {
509 fail:
510 rc = -1;
511
512 client->c_pin_id = 0;
513 CONNECTION_DESTROY(client);
514 }
515
516 ber_free( copy, 0 );
517 return rc;
518 }
519
520 /*
521 * Remember the response, but first ask the server what
522 * authorization identity has been negotiated.
523 *
524 * Also, this request will fail if the server thinks a SASL
525 * confidentiality/integrity layer has been negotiated so we catch
526 * it early and no other clients are affected.
527 */
528 int
finish_sasl_bind(LloadConnection * upstream,LloadOperation * op,BerElement * ber)529 finish_sasl_bind(
530 LloadConnection *upstream,
531 LloadOperation *op,
532 BerElement *ber )
533 {
534 BerElement *output;
535 LloadOperation *removed;
536 ber_int_t msgid;
537 int rc;
538
539 CONNECTION_ASSERT_LOCKED(upstream);
540 removed = ldap_tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
541 if ( !removed ) {
542 assert( upstream->c_state != LLOAD_C_BINDING );
543 /* FIXME: has client replaced this bind since? */
544 assert(0);
545 }
546 assert( removed == op && upstream->c_state == LLOAD_C_BINDING );
547
548 CONNECTION_UNLOCK(upstream);
549
550 checked_lock( &upstream->c_io_mutex );
551 output = upstream->c_pendingber;
552 if ( output == NULL && (output = ber_alloc()) == NULL ) {
553 checked_unlock( &upstream->c_io_mutex );
554 CONNECTION_LOCK_DESTROY(upstream);
555 return -1;
556 }
557 upstream->c_pendingber = output;
558
559 msgid = upstream->c_next_msgid++;
560 ber_printf( output, "t{tit{ts}}", LDAP_TAG_MESSAGE,
561 LDAP_TAG_MSGID, msgid,
562 LDAP_REQ_EXTENDED,
563 LDAP_TAG_EXOP_REQ_OID, LDAP_EXOP_WHO_AM_I );
564
565 /* Make sure noone flushes the buffer before we re-insert the operation */
566 CONNECTION_LOCK(upstream);
567 checked_unlock( &upstream->c_io_mutex );
568
569 op->o_upstream_msgid = msgid;
570
571 /* remember the response for later */
572 ber_free( op->o_ber, 1 );
573 op->o_ber = ber;
574
575 /* Could we have been unlinked in the meantime? */
576 rc = ldap_tavl_insert(
577 &upstream->c_ops, op, operation_upstream_cmp, ldap_avl_dup_error );
578 assert( rc == LDAP_SUCCESS );
579
580 CONNECTION_UNLOCK(upstream);
581
582 Debug( LDAP_DEBUG_TRACE, "finish_sasl_bind: "
583 "SASL exchange in lieu of client connid=%lu to upstream "
584 "connid=%lu finished, resolving final authzid name msgid=%d\n",
585 op->o_client_connid, op->o_upstream_connid, op->o_upstream_msgid );
586
587 connection_write_cb( -1, 0, upstream );
588 return LDAP_SUCCESS;
589 }
590
591 int
handle_bind_response(LloadConnection * client,LloadOperation * op,BerElement * ber)592 handle_bind_response(
593 LloadConnection *client,
594 LloadOperation *op,
595 BerElement *ber )
596 {
597 LloadConnection *upstream;
598 BerValue response;
599 BerElement *copy;
600 LloadOperation *removed;
601 ber_int_t result;
602 ber_tag_t tag;
603 int rc = LDAP_SUCCESS;
604
605 if ( (copy = ber_alloc()) == NULL ) {
606 rc = -1;
607 goto done;
608 }
609
610 tag = ber_peek_element( ber, &response );
611 assert( tag == LDAP_RES_BIND );
612
613 ber_init2( copy, &response, 0 );
614
615 tag = ber_get_enum( copy, &result );
616 ber_free( copy, 0 );
617
618 if ( tag == LBER_ERROR ) {
619 rc = -1;
620 goto done;
621 }
622
623 Debug( LDAP_DEBUG_STATS, "handle_bind_response: "
624 "received response for bind request msgid=%d by client "
625 "connid=%lu, result=%d\n",
626 op->o_client_msgid, op->o_client_connid, result );
627
628 checked_lock( &op->o_link_mutex );
629 upstream = op->o_upstream;
630 checked_unlock( &op->o_link_mutex );
631 if ( !upstream ) {
632 return LDAP_SUCCESS;
633 }
634
635 CONNECTION_LOCK(upstream);
636 if ( !ldap_tavl_find( upstream->c_ops, op, operation_upstream_cmp ) ) {
637 /*
638 * operation might not be found because:
639 * - it has timed out (only happens when debugging/hung/...)
640 * a response has been sent for us, we must not send another
641 * - it has been abandoned (new bind, unbind)
642 * no response is expected
643 * - ???
644 */
645 CONNECTION_UNLOCK(upstream);
646 return LDAP_SUCCESS;
647 }
648
649 /*
650 * We might be marked for closing, forward the response if we can, but do
651 * no more if it's a SASL bind - just finish the operation and send failure
652 * in that case (since we can't resolve the bind identity correctly).
653 */
654 if ( upstream->c_state == LLOAD_C_CLOSING ) {
655 /* FIXME: this is too ad-hoc */
656 if ( ( result == LDAP_SUCCESS ||
657 result == LDAP_SASL_BIND_IN_PROGRESS ) &&
658 !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) {
659 CONNECTION_UNLOCK(upstream);
660 operation_send_reject(
661 op, LDAP_OTHER, "upstream connection is closing", 0 );
662
663 ber_free( ber, 1 );
664 return LDAP_SUCCESS;
665 }
666
667 assert( op->o_client_msgid && op->o_upstream_msgid );
668 op->o_pin_id = 0;
669
670 } else if ( result == LDAP_SASL_BIND_IN_PROGRESS ) {
671 ldap_tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
672 op->o_upstream_msgid = 0;
673 rc = ldap_tavl_insert(
674 &upstream->c_ops, op, operation_upstream_cmp, ldap_avl_dup_error );
675 assert( rc == LDAP_SUCCESS );
676 } else {
677 int sasl_finished = 0;
678 if ( !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) {
679 sasl_finished = 1;
680 ber_memfree( upstream->c_sasl_bind_mech.bv_val );
681 BER_BVZERO( &upstream->c_sasl_bind_mech );
682 }
683
684 assert( op->o_client_msgid && op->o_upstream_msgid );
685 op->o_pin_id = 0;
686
687 if ( (lload_features & LLOAD_FEATURE_PROXYAUTHZ) && sasl_finished &&
688 result == LDAP_SUCCESS ) {
689 return finish_sasl_bind( upstream, op, ber );
690 }
691 op->o_res = LLOAD_OP_COMPLETED;
692 }
693 CONNECTION_UNLOCK(upstream);
694
695 if ( !op->o_pin_id ) {
696 operation_unlink_upstream( op, upstream );
697 }
698
699 CONNECTION_LOCK(client);
700 removed = ldap_tavl_delete( &client->c_ops, op, operation_client_cmp );
701 assert( !removed || op == removed );
702
703 if ( client->c_state == LLOAD_C_BINDING ) {
704 assert( removed );
705 switch ( result ) {
706 case LDAP_SASL_BIND_IN_PROGRESS:
707 op->o_saved_msgid = op->o_client_msgid;
708 op->o_client_msgid = 0;
709 rc = ldap_tavl_insert( &client->c_ops, op, operation_client_cmp,
710 ldap_avl_dup_error );
711 assert( rc == LDAP_SUCCESS );
712 break;
713 case LDAP_SUCCESS:
714 default: {
715 client->c_state = LLOAD_C_READY;
716 client->c_type = LLOAD_C_OPEN;
717 client->c_pin_id = 0;
718 client->c_n_ops_executing--;
719 if ( !BER_BVISNULL( &client->c_auth ) ) {
720 if ( result != LDAP_SUCCESS ) {
721 ber_memfree( client->c_auth.bv_val );
722 BER_BVZERO( &client->c_auth );
723 } else if ( !ber_bvstrcasecmp(
724 &client->c_auth, &lloadd_identity ) ) {
725 client->c_type = LLOAD_C_PRIVILEGED;
726 }
727 }
728 if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
729 ber_memfree( client->c_sasl_bind_mech.bv_val );
730 BER_BVZERO( &client->c_sasl_bind_mech );
731 }
732 break;
733 }
734 }
735 } else {
736 if ( removed ) {
737 client->c_n_ops_executing--;
738 }
739 assert( client->c_state == LLOAD_C_DYING ||
740 client->c_state == LLOAD_C_CLOSING );
741 }
742 CONNECTION_UNLOCK(client);
743
744 done:
745 if ( rc ) {
746 operation_send_reject( op, LDAP_OTHER, "internal error", 1 );
747
748 ber_free( ber, 1 );
749 return LDAP_SUCCESS;
750 }
751 return forward_final_response( client, op, ber );
752 }
753
754 int
handle_whoami_response(LloadConnection * client,LloadOperation * op,BerElement * ber)755 handle_whoami_response(
756 LloadConnection *client,
757 LloadOperation *op,
758 BerElement *ber )
759 {
760 LloadConnection *upstream;
761 BerValue matched, diagmsg;
762 BerElement *saved_response = op->o_ber;
763 LloadOperation *removed;
764 ber_int_t result;
765 ber_tag_t tag;
766 ber_len_t len;
767
768 Debug( LDAP_DEBUG_TRACE, "handle_whoami_response: "
769 "connid=%ld received whoami response in lieu of connid=%ld\n",
770 op->o_upstream_connid, client->c_connid );
771
772 tag = ber_scanf( ber, "{emm" /* "}" */,
773 &result, &matched, &diagmsg );
774 if ( tag == LBER_ERROR ) {
775 operation_send_reject( op, LDAP_OTHER, "upstream protocol error", 0 );
776 return -1;
777 }
778
779 checked_lock( &op->o_link_mutex );
780 upstream = op->o_upstream;
781 checked_unlock( &op->o_link_mutex );
782 if ( !upstream ) {
783 return LDAP_SUCCESS;
784 }
785
786 op->o_res = LLOAD_OP_COMPLETED;
787 /* Clear upstream status */
788 operation_unlink_upstream( op, upstream );
789
790 if ( result == LDAP_PROTOCOL_ERROR ) {
791 LloadBackend *b;
792
793 CONNECTION_LOCK(upstream);
794 b = upstream->c_backend;
795 Debug( LDAP_DEBUG_ANY, "handle_whoami_response: "
796 "Who Am I? extended operation not supported on backend %s, "
797 "proxyauthz with clients that do SASL binds will not work "
798 "msg=%s!\n",
799 b->b_uri.bv_val, diagmsg.bv_val );
800 CONNECTION_UNLOCK(upstream);
801 operation_send_reject( op, LDAP_OTHER, "upstream protocol error", 0 );
802 return -1;
803 }
804
805 tag = ber_peek_tag( ber, &len );
806
807 CONNECTION_LOCK(client);
808
809 assert( client->c_state == LLOAD_C_BINDING ||
810 client->c_state == LLOAD_C_CLOSING );
811
812 assert( BER_BVISNULL( &client->c_auth ) );
813 if ( !BER_BVISNULL( &client->c_auth ) ) {
814 ber_memfree( client->c_auth.bv_val );
815 BER_BVZERO( &client->c_auth );
816 }
817
818 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
819 tag = ber_scanf( ber, "o", &client->c_auth );
820 if ( tag == LBER_ERROR ) {
821 CONNECTION_DESTROY(client);
822 return -1;
823 }
824 }
825
826 removed = ldap_tavl_delete( &client->c_ops, op, operation_client_cmp );
827 assert( !removed || op == removed );
828 op->o_pin_id = 0;
829 if ( removed ) {
830 client->c_n_ops_executing--;
831 }
832
833 Debug( LDAP_DEBUG_TRACE, "handle_whoami_response: "
834 "connid=%ld new authid=%s\n",
835 client->c_connid, client->c_auth.bv_val );
836
837 if ( client->c_state == LLOAD_C_BINDING ) {
838 client->c_state = LLOAD_C_READY;
839 client->c_type = LLOAD_C_OPEN;
840 client->c_pin_id = 0;
841 if ( !BER_BVISNULL( &client->c_auth ) &&
842 !ber_bvstrcasecmp( &client->c_auth, &lloadd_identity ) ) {
843 client->c_type = LLOAD_C_PRIVILEGED;
844 }
845 if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
846 ber_memfree( client->c_sasl_bind_mech.bv_val );
847 BER_BVZERO( &client->c_sasl_bind_mech );
848 }
849 }
850
851 CONNECTION_UNLOCK(client);
852
853 /* defer the disposal of ber to operation_destroy */
854 op->o_ber = ber;
855
856 return forward_final_response( client, op, saved_response );
857 }
858
859 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
860 int
handle_vc_bind_response(LloadConnection * client,LloadOperation * op,BerElement * ber)861 handle_vc_bind_response(
862 LloadConnection *client,
863 LloadOperation *op,
864 BerElement *ber )
865 {
866 BerElement *output;
867 BerValue matched, diagmsg, creds = BER_BVNULL, controls = BER_BVNULL;
868 ber_int_t result;
869 ber_tag_t tag;
870 ber_len_t len;
871 int rc = 0;
872
873 tag = ber_scanf( ber, "{emm" /* "}" */,
874 &result, &matched, &diagmsg );
875 if ( tag == LBER_ERROR ) {
876 rc = -1;
877 goto done;
878 }
879
880 tag = ber_peek_tag( ber, &len );
881 if ( result == LDAP_PROTOCOL_ERROR ) {
882 LloadConnection *upstream;
883
884 checked_lock( &op->o_link_mutex );
885 upstream = op->o_upstream;
886 checked_unlock( &op->o_link_mutex );
887 if ( upstream ) {
888 LloadBackend *b;
889
890 CONNECTION_LOCK(upstream);
891 b = upstream->c_backend;
892 Debug( LDAP_DEBUG_ANY, "handle_vc_bind_response: "
893 "VC extended operation not supported on backend %s\n",
894 b->b_uri.bv_val );
895 CONNECTION_UNLOCK(upstream);
896 }
897 }
898
899 Debug( LDAP_DEBUG_STATS, "handle_vc_bind_response: "
900 "received response for bind request msgid=%d by client "
901 "connid=%lu, result=%d\n",
902 op->o_client_msgid, op->o_client_connid, result );
903
904 CONNECTION_LOCK(client);
905
906 if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE ) {
907 if ( !BER_BVISNULL( &client->c_vc_cookie ) ) {
908 ber_memfree( client->c_vc_cookie.bv_val );
909 }
910 tag = ber_scanf( ber, "o", &client->c_vc_cookie );
911 if ( tag == LBER_ERROR ) {
912 rc = -1;
913 CONNECTION_UNLOCK(client);
914 goto done;
915 }
916 tag = ber_peek_tag( ber, &len );
917 }
918
919 if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_SCREDS ) {
920 tag = ber_scanf( ber, "m", &creds );
921 if ( tag == LBER_ERROR ) {
922 rc = -1;
923 CONNECTION_UNLOCK(client);
924 goto done;
925 }
926 tag = ber_peek_tag( ber, &len );
927 }
928
929 if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS ) {
930 tag = ber_scanf( ber, "m", &controls );
931 if ( tag == LBER_ERROR ) {
932 rc = -1;
933 CONNECTION_UNLOCK(client);
934 goto done;
935 }
936 }
937
938 if ( client->c_state == LLOAD_C_BINDING ) {
939 switch ( result ) {
940 case LDAP_SASL_BIND_IN_PROGRESS:
941 break;
942 case LDAP_SUCCESS:
943 default: {
944 client->c_state = LLOAD_C_READY;
945 client->c_type = LLOAD_C_OPEN;
946 client->c_pin_id = 0;
947 if ( result != LDAP_SUCCESS ) {
948 ber_memfree( client->c_auth.bv_val );
949 BER_BVZERO( &client->c_auth );
950 } else if ( !ber_bvstrcasecmp(
951 &client->c_auth, &lloadd_identity ) ) {
952 client->c_type = LLOAD_C_PRIVILEGED;
953 }
954 if ( !BER_BVISNULL( &client->c_vc_cookie ) ) {
955 ber_memfree( client->c_vc_cookie.bv_val );
956 BER_BVZERO( &client->c_vc_cookie );
957 }
958 if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
959 ber_memfree( client->c_sasl_bind_mech.bv_val );
960 BER_BVZERO( &client->c_sasl_bind_mech );
961 }
962 break;
963 }
964 }
965 } else {
966 assert( client->c_state == LLOAD_C_INVALID ||
967 client->c_state == LLOAD_C_CLOSING );
968 }
969 CONNECTION_UNLOCK(client);
970
971 checked_lock( &client->c_io_mutex );
972 output = client->c_pendingber;
973 if ( output == NULL && (output = ber_alloc()) == NULL ) {
974 rc = -1;
975 checked_unlock( &client->c_io_mutex );
976 goto done;
977 }
978 client->c_pendingber = output;
979
980 rc = ber_printf( output, "t{tit{eOOtO}tO}", LDAP_TAG_MESSAGE,
981 LDAP_TAG_MSGID, op->o_client_msgid, LDAP_RES_BIND,
982 result, &matched, &diagmsg,
983 LDAP_TAG_SASL_RES_CREDS, BER_BV_OPTIONAL( &creds ),
984 LDAP_TAG_CONTROLS, BER_BV_OPTIONAL( &controls ) );
985
986 checked_unlock( &client->c_io_mutex );
987 if ( rc >= 0 ) {
988 connection_write_cb( -1, 0, client );
989 rc = 0;
990 }
991
992 done:
993 operation_unlink( op );
994 ber_free( ber, 1 );
995 return rc;
996 }
997 #endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
998