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