1 /* $NetBSD: bind.c,v 1.3 2025/09/05 21:16:24 christos Exp $ */ 2 3 /* $OpenLDAP$ */ 4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 * 6 * Copyright 1998-2024 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.3 2025/09/05 21:16:24 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 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 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 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 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 = LDAP_UNAVAILABLE, rc = LDAP_SUCCESS; 204 char *message = "no connections available"; 205 enum op_restriction client_restricted; 206 207 CONNECTION_LOCK(client); 208 pin = client->c_pin_id; 209 210 if ( pin ) { 211 LloadOperation *pinned_op, needle = { 212 .o_client_connid = client->c_connid, 213 .o_client_msgid = 0, 214 .o_pin_id = client->c_pin_id, 215 }; 216 217 Debug( LDAP_DEBUG_CONNS, "request_bind: " 218 "client connid=%lu is pinned pin=%lu\n", 219 client->c_connid, pin ); 220 221 pinned_op = 222 ldap_tavl_delete( &client->c_ops, &needle, operation_client_cmp ); 223 if ( pinned_op ) { 224 assert( op->o_tag == pinned_op->o_tag ); 225 226 pinned_op->o_client_msgid = op->o_client_msgid; 227 228 /* Preserve the new BerElement and its pointers, reclaim the old 229 * one in operation_destroy_from_client if it's still there */ 230 needle.o_ber = pinned_op->o_ber; 231 pinned_op->o_ber = op->o_ber; 232 op->o_ber = needle.o_ber; 233 234 pinned_op->o_request = op->o_request; 235 pinned_op->o_ctrls = op->o_ctrls; 236 237 /* No one has seen this operation yet, plant the pin back in its stead */ 238 client->c_n_ops_executing--; 239 op->o_res = LLOAD_OP_COMPLETED; 240 ldap_tavl_delete( &client->c_ops, op, operation_client_cmp ); 241 op->o_client = NULL; 242 assert( op->o_upstream == NULL ); 243 244 rc = ldap_tavl_insert( &client->c_ops, pinned_op, operation_client_cmp, 245 ldap_avl_dup_error ); 246 assert( rc == LDAP_SUCCESS ); 247 248 /* No one has seen this operation yet */ 249 op->o_refcnt--; 250 operation_destroy( op ); 251 252 /* We didn't start a new operation, just continuing an existing one */ 253 lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_received--; 254 255 op = pinned_op; 256 } 257 } 258 259 ldap_tavl_delete( &client->c_ops, op, operation_client_cmp ); 260 client->c_n_ops_executing--; 261 262 client_reset( client ); 263 264 client->c_state = LLOAD_C_BINDING; 265 client->c_type = LLOAD_C_OPEN; 266 267 if ( (copy = ber_alloc()) == NULL ) { 268 goto fail; 269 } 270 ber_init2( copy, &op->o_request, 0 ); 271 272 tag = ber_get_int( copy, &version ); 273 if ( tag == LBER_ERROR ) { 274 Debug( LDAP_DEBUG_PACKETS, "request_bind: " 275 "failed to parse version field\n" ); 276 goto fail; 277 } else if ( version != LDAP_VERSION3 ) { 278 CONNECTION_UNLOCK(client); 279 operation_send_reject( 280 op, LDAP_PROTOCOL_ERROR, "LDAP version unsupported", 1 ); 281 CONNECTION_LOCK(client); 282 goto fail; 283 } 284 285 tag = ber_get_stringbv( copy, &binddn, LBER_BV_NOTERM ); 286 if ( tag == LBER_ERROR ) { 287 Debug( LDAP_DEBUG_PACKETS, "request_bind: " 288 "failed to parse bind name field\n" ); 289 goto fail; 290 } 291 292 if ( !BER_BVISNULL( &client->c_auth ) ) { 293 ch_free( client->c_auth.bv_val ); 294 BER_BVZERO( &client->c_auth ); 295 } 296 297 tag = ber_skip_element( copy, &auth ); 298 if ( tag == LDAP_AUTH_SIMPLE ) { 299 if ( !BER_BVISEMPTY( &binddn ) ) { 300 char *ptr; 301 client->c_auth.bv_len = STRLENOF("dn:") + binddn.bv_len; 302 client->c_auth.bv_val = ch_malloc( client->c_auth.bv_len + 1 ); 303 304 ptr = lutil_strcopy( client->c_auth.bv_val, "dn:" ); 305 ptr = lutil_strncopy( ptr, binddn.bv_val, binddn.bv_len ); 306 *ptr = '\0'; 307 } 308 309 if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) { 310 ber_memfree( client->c_sasl_bind_mech.bv_val ); 311 BER_BVZERO( &client->c_sasl_bind_mech ); 312 } 313 } else if ( tag == LDAP_AUTH_SASL ) { 314 ber_init2( copy, &auth, 0 ); 315 316 if ( ber_get_stringbv( copy, &mech, LBER_BV_NOTERM ) == LBER_ERROR ) { 317 goto fail; 318 } 319 if ( !ber_bvcmp( &mech, &mech_external ) ) { 320 struct berval credentials = BER_BVNULL; 321 322 ber_get_stringbv( copy, &credentials, LBER_BV_NOTERM ); 323 rc = bind_mech_external( client, op, &credentials ); 324 325 /* terminate the upstream side if client switched mechanisms */ 326 if ( pin ) { 327 operation_abandon( op ); 328 } 329 330 ber_free( copy, 0 ); 331 return rc; 332 } else if ( BER_BVISNULL( &client->c_sasl_bind_mech ) ) { 333 ber_dupbv( &client->c_sasl_bind_mech, &mech ); 334 } else if ( ber_bvcmp( &mech, &client->c_sasl_bind_mech ) ) { 335 ber_bvreplace( &client->c_sasl_bind_mech, &mech ); 336 } 337 } else { 338 goto fail; 339 } 340 341 rc = ldap_tavl_insert( &client->c_ops, op, operation_client_cmp, ldap_avl_dup_error ); 342 assert( rc == LDAP_SUCCESS ); 343 client->c_n_ops_executing++; 344 345 client_restricted = client->c_restricted; 346 CONNECTION_UNLOCK(client); 347 348 if ( pin ) { 349 checked_lock( &op->o_link_mutex ); 350 upstream = op->o_upstream; 351 checked_unlock( &op->o_link_mutex ); 352 } 353 354 if ( upstream ) { 355 checked_lock( &upstream->c_io_mutex ); 356 CONNECTION_LOCK(upstream); 357 if ( !IS_ALIVE( upstream, c_live ) ) { 358 CONNECTION_UNLOCK(upstream); 359 checked_unlock( &upstream->c_io_mutex ); 360 upstream = NULL; 361 } 362 } 363 364 /* If we were pinned but lost the link, don't look for a new upstream, we 365 * have to reject the op and clear pin */ 366 if ( upstream ) { 367 /* No need to do anything */ 368 } else if ( !pin && client_restricted != LLOAD_OP_RESTRICTED_ISOLATE ) { 369 upstream_select( op, &upstream, &res, &message ); 370 } else { 371 Debug( LDAP_DEBUG_STATS, "request_bind: " 372 "connid=%lu, msgid=%d pinned upstream lost\n", 373 op->o_client_connid, op->o_client_msgid ); 374 operation_send_reject( op, LDAP_OTHER, 375 "connection to the remote server has been severed", 1 ); 376 pin = 0; 377 goto done; 378 } 379 380 if ( !upstream ) { 381 Debug( LDAP_DEBUG_STATS, "request_bind: " 382 "connid=%lu, msgid=%d no available connection found\n", 383 op->o_client_connid, op->o_client_msgid ); 384 operation_send_reject( op, res, message, 1 ); 385 assert( client->c_pin_id == 0 ); 386 goto done; 387 } 388 assert_locked( &upstream->c_io_mutex ); 389 /* 390 * At this point, either: 391 * - upstream is READY and pin == 0 392 * - upstream is BINDING, pin != 0 and op->o_upstream_msgid == 0 393 * 394 * A pinned upstream we marked for closing at some point ago should have 395 * closed by now. 396 */ 397 398 ber = upstream->c_pendingber; 399 if ( ber == NULL && (ber = ber_alloc()) == NULL ) { 400 checked_unlock( &upstream->c_io_mutex ); 401 if ( !pin ) { 402 LloadBackend *b = upstream->c_backend; 403 404 upstream->c_n_ops_executing--; 405 CONNECTION_UNLOCK(upstream); 406 407 checked_lock( &b->b_mutex ); 408 b->b_n_ops_executing--; 409 operation_update_backend_counters( op, b ); 410 checked_unlock( &b->b_mutex ); 411 } else { 412 CONNECTION_UNLOCK(upstream); 413 } 414 415 Debug( LDAP_DEBUG_ANY, "request_bind: " 416 "ber_alloc failed\n" ); 417 418 OPERATION_UNLINK(op); 419 420 CONNECTION_LOCK(client); 421 goto fail; 422 } 423 upstream->c_pendingber = ber; 424 425 if ( !pin ) { 426 lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_forwarded++; 427 } 428 429 if ( pin ) { 430 ldap_tavl_delete( &upstream->c_ops, op, operation_upstream_cmp ); 431 if ( tag == LDAP_AUTH_SIMPLE ) { 432 pin = op->o_pin_id = 0; 433 } 434 } else if ( tag == LDAP_AUTH_SASL && !op->o_pin_id ) { 435 checked_lock( &lload_pin_mutex ); 436 pin = op->o_pin_id = lload_next_pin++; 437 Debug( LDAP_DEBUG_CONNS, "request_bind: " 438 "client connid=%lu allocated pin=%lu linking it to upstream " 439 "connid=%lu\n", 440 op->o_client_connid, pin, upstream->c_connid ); 441 checked_unlock( &lload_pin_mutex ); 442 } 443 444 op->o_upstream = upstream; 445 op->o_upstream_connid = upstream->c_connid; 446 op->o_upstream_msgid = upstream->c_next_msgid++; 447 op->o_res = LLOAD_OP_FAILED; 448 449 /* Was it unlinked in the meantime? No need to send a response since the 450 * client is dead */ 451 if ( !IS_ALIVE( op, o_refcnt ) ) { 452 LloadBackend *b = upstream->c_backend; 453 454 upstream->c_n_ops_executing--; 455 checked_unlock( &upstream->c_io_mutex ); 456 CONNECTION_UNLOCK(upstream); 457 458 checked_lock( &b->b_mutex ); 459 b->b_n_ops_executing--; 460 checked_unlock( &b->b_mutex ); 461 462 assert( !IS_ALIVE( client, c_live ) ); 463 checked_lock( &op->o_link_mutex ); 464 if ( op->o_upstream ) { 465 op->o_upstream = NULL; 466 } 467 checked_unlock( &op->o_link_mutex ); 468 rc = -1; 469 goto done; 470 } 471 472 if ( BER_BVISNULL( &mech ) ) { 473 if ( !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) { 474 ber_memfree( upstream->c_sasl_bind_mech.bv_val ); 475 BER_BVZERO( &upstream->c_sasl_bind_mech ); 476 } 477 } else if ( ber_bvcmp( &upstream->c_sasl_bind_mech, &mech ) ) { 478 ber_bvreplace( &upstream->c_sasl_bind_mech, &mech ); 479 } 480 481 Debug( LDAP_DEBUG_TRACE, "request_bind: " 482 "added bind from client connid=%lu to upstream connid=%lu " 483 "as msgid=%d\n", 484 op->o_client_connid, op->o_upstream_connid, op->o_upstream_msgid ); 485 if ( ldap_tavl_insert( &upstream->c_ops, op, operation_upstream_cmp, 486 ldap_avl_dup_error ) ) { 487 assert(0); 488 } 489 upstream->c_state = LLOAD_C_BINDING; 490 CONNECTION_UNLOCK(upstream); 491 492 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS 493 if ( lload_features & LLOAD_FEATURE_VC ) { 494 rc = client_bind_as_vc( op, upstream, &binddn, tag, &auth ); 495 } else 496 #endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */ 497 { 498 rc = client_bind( op, upstream, &binddn, tag, &auth ); 499 } 500 checked_unlock( &upstream->c_io_mutex ); 501 502 done: 503 504 CONNECTION_LOCK(client); 505 if ( rc == LDAP_SUCCESS ) { 506 client->c_pin_id = pin; 507 CONNECTION_UNLOCK(client); 508 509 if ( upstream ) { 510 connection_write_cb( -1, 0, upstream ); 511 } 512 } else { 513 fail: 514 rc = -1; 515 516 client->c_pin_id = 0; 517 CONNECTION_DESTROY(client); 518 } 519 520 ber_free( copy, 0 ); 521 return rc; 522 } 523 524 /* 525 * Remember the response, but first ask the server what 526 * authorization identity has been negotiated. 527 * 528 * Also, this request will fail if the server thinks a SASL 529 * confidentiality/integrity layer has been negotiated so we catch 530 * it early and no other clients are affected. 531 */ 532 int 533 finish_sasl_bind( 534 LloadConnection *upstream, 535 LloadOperation *op, 536 BerElement *ber ) 537 { 538 BerElement *output; 539 LloadOperation *removed; 540 ber_int_t msgid; 541 int rc; 542 543 CONNECTION_ASSERT_LOCKED(upstream); 544 removed = ldap_tavl_delete( &upstream->c_ops, op, operation_upstream_cmp ); 545 if ( !removed ) { 546 assert( upstream->c_state != LLOAD_C_BINDING ); 547 /* FIXME: has client replaced this bind since? */ 548 assert(0); 549 } 550 assert( removed == op && upstream->c_state == LLOAD_C_BINDING ); 551 552 CONNECTION_UNLOCK(upstream); 553 554 checked_lock( &upstream->c_io_mutex ); 555 output = upstream->c_pendingber; 556 if ( output == NULL && (output = ber_alloc()) == NULL ) { 557 checked_unlock( &upstream->c_io_mutex ); 558 CONNECTION_LOCK_DESTROY(upstream); 559 return -1; 560 } 561 upstream->c_pendingber = output; 562 563 msgid = upstream->c_next_msgid++; 564 ber_printf( output, "t{tit{ts}}", LDAP_TAG_MESSAGE, 565 LDAP_TAG_MSGID, msgid, 566 LDAP_REQ_EXTENDED, 567 LDAP_TAG_EXOP_REQ_OID, LDAP_EXOP_WHO_AM_I ); 568 569 /* Make sure noone flushes the buffer before we re-insert the operation */ 570 CONNECTION_LOCK(upstream); 571 checked_unlock( &upstream->c_io_mutex ); 572 573 op->o_upstream_msgid = msgid; 574 575 /* remember the response for later */ 576 ber_free( op->o_ber, 1 ); 577 op->o_ber = ber; 578 579 /* Could we have been unlinked in the meantime? */ 580 rc = ldap_tavl_insert( 581 &upstream->c_ops, op, operation_upstream_cmp, ldap_avl_dup_error ); 582 assert( rc == LDAP_SUCCESS ); 583 584 CONNECTION_UNLOCK(upstream); 585 586 Debug( LDAP_DEBUG_TRACE, "finish_sasl_bind: " 587 "SASL exchange in lieu of client connid=%lu to upstream " 588 "connid=%lu finished, resolving final authzid name msgid=%d\n", 589 op->o_client_connid, op->o_upstream_connid, op->o_upstream_msgid ); 590 591 connection_write_cb( -1, 0, upstream ); 592 return LDAP_SUCCESS; 593 } 594 595 int 596 handle_bind_response( 597 LloadConnection *client, 598 LloadOperation *op, 599 BerElement *ber ) 600 { 601 LloadConnection *upstream; 602 BerValue response; 603 BerElement *copy; 604 LloadOperation *removed; 605 ber_int_t result; 606 ber_tag_t tag; 607 int rc = LDAP_SUCCESS; 608 609 if ( (copy = ber_alloc()) == NULL ) { 610 rc = -1; 611 goto done; 612 } 613 614 tag = ber_peek_element( ber, &response ); 615 assert( tag == LDAP_RES_BIND ); 616 617 ber_init2( copy, &response, 0 ); 618 619 tag = ber_get_enum( copy, &result ); 620 ber_free( copy, 0 ); 621 622 if ( tag == LBER_ERROR ) { 623 rc = -1; 624 goto done; 625 } 626 627 Debug( LDAP_DEBUG_STATS, "handle_bind_response: " 628 "received response for bind request msgid=%d by client " 629 "connid=%lu, result=%d\n", 630 op->o_client_msgid, op->o_client_connid, result ); 631 632 checked_lock( &op->o_link_mutex ); 633 upstream = op->o_upstream; 634 checked_unlock( &op->o_link_mutex ); 635 if ( !upstream ) { 636 return LDAP_SUCCESS; 637 } 638 639 CONNECTION_LOCK(upstream); 640 if ( !ldap_tavl_find( upstream->c_ops, op, operation_upstream_cmp ) ) { 641 /* 642 * operation might not be found because: 643 * - it has timed out (only happens when debugging/hung/...) 644 * a response has been sent for us, we must not send another 645 * - it has been abandoned (new bind, unbind) 646 * no response is expected 647 * - ??? 648 */ 649 CONNECTION_UNLOCK(upstream); 650 return LDAP_SUCCESS; 651 } 652 653 /* 654 * We might be marked for closing, forward the response if we can, but do 655 * no more if it's a SASL bind - just finish the operation and send failure 656 * in that case (since we can't resolve the bind identity correctly). 657 */ 658 if ( upstream->c_state == LLOAD_C_CLOSING ) { 659 /* FIXME: this is too ad-hoc */ 660 if ( ( result == LDAP_SUCCESS || 661 result == LDAP_SASL_BIND_IN_PROGRESS ) && 662 !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) { 663 CONNECTION_UNLOCK(upstream); 664 operation_send_reject( 665 op, LDAP_OTHER, "upstream connection is closing", 0 ); 666 667 ber_free( ber, 1 ); 668 return LDAP_SUCCESS; 669 } 670 671 assert( op->o_client_msgid && op->o_upstream_msgid ); 672 op->o_pin_id = 0; 673 674 } else if ( result == LDAP_SASL_BIND_IN_PROGRESS ) { 675 ldap_tavl_delete( &upstream->c_ops, op, operation_upstream_cmp ); 676 op->o_upstream_msgid = 0; 677 rc = ldap_tavl_insert( 678 &upstream->c_ops, op, operation_upstream_cmp, ldap_avl_dup_error ); 679 assert( rc == LDAP_SUCCESS ); 680 } else { 681 int sasl_finished = 0; 682 if ( !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) { 683 sasl_finished = 1; 684 ber_memfree( upstream->c_sasl_bind_mech.bv_val ); 685 BER_BVZERO( &upstream->c_sasl_bind_mech ); 686 } 687 688 assert( op->o_client_msgid && op->o_upstream_msgid ); 689 op->o_pin_id = 0; 690 691 if ( (lload_features & LLOAD_FEATURE_PROXYAUTHZ) && sasl_finished && 692 result == LDAP_SUCCESS ) { 693 return finish_sasl_bind( upstream, op, ber ); 694 } 695 op->o_res = LLOAD_OP_COMPLETED; 696 } 697 CONNECTION_UNLOCK(upstream); 698 699 if ( !op->o_pin_id ) { 700 operation_unlink_upstream( op, upstream ); 701 } 702 703 CONNECTION_LOCK(client); 704 removed = ldap_tavl_delete( &client->c_ops, op, operation_client_cmp ); 705 assert( !removed || op == removed ); 706 707 if ( client->c_state == LLOAD_C_BINDING ) { 708 assert( removed ); 709 switch ( result ) { 710 case LDAP_SASL_BIND_IN_PROGRESS: 711 op->o_saved_msgid = op->o_client_msgid; 712 op->o_client_msgid = 0; 713 rc = ldap_tavl_insert( &client->c_ops, op, operation_client_cmp, 714 ldap_avl_dup_error ); 715 assert( rc == LDAP_SUCCESS ); 716 break; 717 case LDAP_SUCCESS: 718 default: { 719 client->c_state = LLOAD_C_READY; 720 client->c_type = LLOAD_C_OPEN; 721 client->c_pin_id = 0; 722 client->c_n_ops_executing--; 723 if ( !BER_BVISNULL( &client->c_auth ) ) { 724 if ( result != LDAP_SUCCESS ) { 725 ber_memfree( client->c_auth.bv_val ); 726 BER_BVZERO( &client->c_auth ); 727 } else if ( !ber_bvstrcasecmp( 728 &client->c_auth, &lloadd_identity ) ) { 729 client->c_type = LLOAD_C_PRIVILEGED; 730 } 731 } 732 if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) { 733 ber_memfree( client->c_sasl_bind_mech.bv_val ); 734 BER_BVZERO( &client->c_sasl_bind_mech ); 735 } 736 break; 737 } 738 } 739 } else { 740 if ( removed ) { 741 client->c_n_ops_executing--; 742 } 743 assert( client->c_state == LLOAD_C_DYING || 744 client->c_state == LLOAD_C_CLOSING ); 745 } 746 CONNECTION_UNLOCK(client); 747 748 done: 749 if ( rc ) { 750 operation_send_reject( op, LDAP_OTHER, "internal error", 1 ); 751 752 ber_free( ber, 1 ); 753 return LDAP_SUCCESS; 754 } 755 return forward_final_response( client, op, ber ); 756 } 757 758 int 759 handle_whoami_response( 760 LloadConnection *client, 761 LloadOperation *op, 762 BerElement *ber ) 763 { 764 LloadConnection *upstream; 765 BerValue matched, diagmsg; 766 BerElement *saved_response = op->o_ber; 767 LloadOperation *removed; 768 ber_int_t result; 769 ber_tag_t tag; 770 ber_len_t len; 771 772 Debug( LDAP_DEBUG_TRACE, "handle_whoami_response: " 773 "connid=%ld received whoami response in lieu of connid=%ld\n", 774 op->o_upstream_connid, client->c_connid ); 775 776 tag = ber_scanf( ber, "{emm" /* "}" */, 777 &result, &matched, &diagmsg ); 778 if ( tag == LBER_ERROR ) { 779 operation_send_reject( op, LDAP_OTHER, "upstream protocol error", 0 ); 780 return -1; 781 } 782 783 checked_lock( &op->o_link_mutex ); 784 upstream = op->o_upstream; 785 checked_unlock( &op->o_link_mutex ); 786 if ( !upstream ) { 787 return LDAP_SUCCESS; 788 } 789 790 op->o_res = LLOAD_OP_COMPLETED; 791 /* Clear upstream status */ 792 operation_unlink_upstream( op, upstream ); 793 794 if ( result == LDAP_PROTOCOL_ERROR ) { 795 LloadBackend *b; 796 797 CONNECTION_LOCK(upstream); 798 b = upstream->c_backend; 799 Debug( LDAP_DEBUG_ANY, "handle_whoami_response: " 800 "Who Am I? extended operation not supported on backend %s, " 801 "proxyauthz with clients that do SASL binds will not work " 802 "msg=%s!\n", 803 b->b_uri.bv_val, diagmsg.bv_val ); 804 CONNECTION_UNLOCK(upstream); 805 operation_send_reject( op, LDAP_OTHER, "upstream protocol error", 0 ); 806 return -1; 807 } 808 809 tag = ber_peek_tag( ber, &len ); 810 811 CONNECTION_LOCK(client); 812 813 assert( client->c_state == LLOAD_C_BINDING || 814 client->c_state == LLOAD_C_CLOSING ); 815 816 assert( BER_BVISNULL( &client->c_auth ) ); 817 if ( !BER_BVISNULL( &client->c_auth ) ) { 818 ber_memfree( client->c_auth.bv_val ); 819 BER_BVZERO( &client->c_auth ); 820 } 821 822 if ( tag == LDAP_TAG_EXOP_RES_VALUE ) { 823 tag = ber_scanf( ber, "o", &client->c_auth ); 824 if ( tag == LBER_ERROR ) { 825 CONNECTION_DESTROY(client); 826 return -1; 827 } 828 } 829 830 removed = ldap_tavl_delete( &client->c_ops, op, operation_client_cmp ); 831 assert( !removed || op == removed ); 832 op->o_pin_id = 0; 833 if ( removed ) { 834 client->c_n_ops_executing--; 835 } 836 837 Debug( LDAP_DEBUG_TRACE, "handle_whoami_response: " 838 "connid=%ld new authid=%s\n", 839 client->c_connid, client->c_auth.bv_val ); 840 841 if ( client->c_state == LLOAD_C_BINDING ) { 842 client->c_state = LLOAD_C_READY; 843 client->c_type = LLOAD_C_OPEN; 844 client->c_pin_id = 0; 845 if ( !BER_BVISNULL( &client->c_auth ) && 846 !ber_bvstrcasecmp( &client->c_auth, &lloadd_identity ) ) { 847 client->c_type = LLOAD_C_PRIVILEGED; 848 } 849 if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) { 850 ber_memfree( client->c_sasl_bind_mech.bv_val ); 851 BER_BVZERO( &client->c_sasl_bind_mech ); 852 } 853 } 854 855 CONNECTION_UNLOCK(client); 856 857 /* defer the disposal of ber to operation_destroy */ 858 op->o_ber = ber; 859 860 return forward_final_response( client, op, saved_response ); 861 } 862 863 #ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS 864 int 865 handle_vc_bind_response( 866 LloadConnection *client, 867 LloadOperation *op, 868 BerElement *ber ) 869 { 870 BerElement *output; 871 BerValue matched, diagmsg, creds = BER_BVNULL, controls = BER_BVNULL; 872 ber_int_t result; 873 ber_tag_t tag; 874 ber_len_t len; 875 int rc = 0; 876 877 tag = ber_scanf( ber, "{emm" /* "}" */, 878 &result, &matched, &diagmsg ); 879 if ( tag == LBER_ERROR ) { 880 rc = -1; 881 goto done; 882 } 883 884 tag = ber_peek_tag( ber, &len ); 885 if ( result == LDAP_PROTOCOL_ERROR ) { 886 LloadConnection *upstream; 887 888 checked_lock( &op->o_link_mutex ); 889 upstream = op->o_upstream; 890 checked_unlock( &op->o_link_mutex ); 891 if ( upstream ) { 892 LloadBackend *b; 893 894 CONNECTION_LOCK(upstream); 895 b = upstream->c_backend; 896 Debug( LDAP_DEBUG_ANY, "handle_vc_bind_response: " 897 "VC extended operation not supported on backend %s\n", 898 b->b_uri.bv_val ); 899 CONNECTION_UNLOCK(upstream); 900 } 901 } 902 903 Debug( LDAP_DEBUG_STATS, "handle_vc_bind_response: " 904 "received response for bind request msgid=%d by client " 905 "connid=%lu, result=%d\n", 906 op->o_client_msgid, op->o_client_connid, result ); 907 908 CONNECTION_LOCK(client); 909 910 if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE ) { 911 if ( !BER_BVISNULL( &client->c_vc_cookie ) ) { 912 ber_memfree( client->c_vc_cookie.bv_val ); 913 } 914 tag = ber_scanf( ber, "o", &client->c_vc_cookie ); 915 if ( tag == LBER_ERROR ) { 916 rc = -1; 917 CONNECTION_UNLOCK(client); 918 goto done; 919 } 920 tag = ber_peek_tag( ber, &len ); 921 } 922 923 if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_SCREDS ) { 924 tag = ber_scanf( ber, "m", &creds ); 925 if ( tag == LBER_ERROR ) { 926 rc = -1; 927 CONNECTION_UNLOCK(client); 928 goto done; 929 } 930 tag = ber_peek_tag( ber, &len ); 931 } 932 933 if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS ) { 934 tag = ber_scanf( ber, "m", &controls ); 935 if ( tag == LBER_ERROR ) { 936 rc = -1; 937 CONNECTION_UNLOCK(client); 938 goto done; 939 } 940 } 941 942 if ( client->c_state == LLOAD_C_BINDING ) { 943 switch ( result ) { 944 case LDAP_SASL_BIND_IN_PROGRESS: 945 break; 946 case LDAP_SUCCESS: 947 default: { 948 client->c_state = LLOAD_C_READY; 949 client->c_type = LLOAD_C_OPEN; 950 client->c_pin_id = 0; 951 if ( result != LDAP_SUCCESS ) { 952 ber_memfree( client->c_auth.bv_val ); 953 BER_BVZERO( &client->c_auth ); 954 } else if ( !ber_bvstrcasecmp( 955 &client->c_auth, &lloadd_identity ) ) { 956 client->c_type = LLOAD_C_PRIVILEGED; 957 } 958 if ( !BER_BVISNULL( &client->c_vc_cookie ) ) { 959 ber_memfree( client->c_vc_cookie.bv_val ); 960 BER_BVZERO( &client->c_vc_cookie ); 961 } 962 if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) { 963 ber_memfree( client->c_sasl_bind_mech.bv_val ); 964 BER_BVZERO( &client->c_sasl_bind_mech ); 965 } 966 break; 967 } 968 } 969 } else { 970 assert( client->c_state == LLOAD_C_INVALID || 971 client->c_state == LLOAD_C_CLOSING ); 972 } 973 CONNECTION_UNLOCK(client); 974 975 checked_lock( &client->c_io_mutex ); 976 output = client->c_pendingber; 977 if ( output == NULL && (output = ber_alloc()) == NULL ) { 978 rc = -1; 979 checked_unlock( &client->c_io_mutex ); 980 goto done; 981 } 982 client->c_pendingber = output; 983 984 rc = ber_printf( output, "t{tit{eOOtO}tO}", LDAP_TAG_MESSAGE, 985 LDAP_TAG_MSGID, op->o_client_msgid, LDAP_RES_BIND, 986 result, &matched, &diagmsg, 987 LDAP_TAG_SASL_RES_CREDS, BER_BV_OPTIONAL( &creds ), 988 LDAP_TAG_CONTROLS, BER_BV_OPTIONAL( &controls ) ); 989 990 checked_unlock( &client->c_io_mutex ); 991 if ( rc >= 0 ) { 992 connection_write_cb( -1, 0, client ); 993 rc = 0; 994 } 995 996 done: 997 OPERATION_UNLINK(op); 998 ber_free( ber, 1 ); 999 return rc; 1000 } 1001 #endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */ 1002