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 32struct berval mech_external = BER_BVC("EXTERNAL"); 33 34int 35bind_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 88done: 89 CONNECTION_UNLOCK(client); 90 operation_send_reject( op, result, message, 1 ); 91 return LDAP_SUCCESS; 92} 93 94static int 95client_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 111static int 112client_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 */ 194int 195request_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 498done: 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 { 509fail: 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 */ 528int 529finish_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 591int 592handle_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 744done: 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 754int 755handle_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 860int 861handle_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 992done: 993 operation_unlink( op ); 994 ber_free( ber, 1 ); 995 return rc; 996} 997#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */ 998