From d3bb538a0cd29ad112a0a562d3fa5102d54b7c0b Mon Sep 17 00:00:00 2001 From: Julia-Garland Date: Tue, 7 Oct 2025 14:56:20 +0000 Subject: [PATCH 1/6] Handle tls 1.3 renegotiation --- src/libmongoc/src/mongoc/mongoc-client.c | 3 +- .../src/mongoc/mongoc-stream-tls-private.h | 1 + ...mongoc-stream-tls-secure-channel-private.h | 6 +++ .../mongoc/mongoc-stream-tls-secure-channel.c | 40 ++++++++++++++++++- src/libmongoc/src/mongoc/mongoc-stream-tls.c | 3 +- .../src/mongoc/mongoc-topology-scanner.c | 4 +- 6 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/libmongoc/src/mongoc/mongoc-client.c b/src/libmongoc/src/mongoc/mongoc-client.c index acc2d42229c..1cd342f0ec2 100644 --- a/src/libmongoc/src/mongoc/mongoc-client.c +++ b/src/libmongoc/src/mongoc/mongoc-client.c @@ -825,7 +825,8 @@ mongoc_client_connect(bool use_ssl, base_stream, host->host, ssl_opts, true, (SSL_CTX *)openssl_ctx_void); #elif defined(MONGOC_ENABLE_SSL_SECURE_CHANNEL) // Use shared Secure Channel credentials. - base_stream = mongoc_stream_tls_new_with_secure_channel_cred(base_stream, ssl_opts, secure_channel_cred_ptr); + base_stream = + mongoc_stream_tls_new_with_secure_channel_cred(base_stream, host->host, ssl_opts, secure_channel_cred_ptr); #else base_stream = mongoc_stream_tls_new_with_hostname(base_stream, host->host, ssl_opts, true); #endif diff --git a/src/libmongoc/src/mongoc/mongoc-stream-tls-private.h b/src/libmongoc/src/mongoc/mongoc-stream-tls-private.h index 0ba5107c5f8..cdaf154ca64 100644 --- a/src/libmongoc/src/mongoc/mongoc-stream-tls-private.h +++ b/src/libmongoc/src/mongoc/mongoc-stream-tls-private.h @@ -58,6 +58,7 @@ mongoc_stream_tls_new_with_hostname_and_openssl_context(mongoc_stream_t *base_st #elif defined(MONGOC_ENABLE_SSL_SECURE_CHANNEL) mongoc_stream_t * mongoc_stream_tls_new_with_secure_channel_cred(mongoc_stream_t *base_stream, + const char *host, mongoc_ssl_opt_t *opt, mongoc_shared_ptr secure_channel_cred_ptr) BSON_GNUC_WARN_UNUSED_RESULT; #endif // MONGOC_ENABLE_SSL_SECURE_CHANNEL diff --git a/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel-private.h b/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel-private.h index bf22348ec1e..5d0ebd167d8 100644 --- a/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel-private.h +++ b/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel-private.h @@ -22,6 +22,8 @@ #ifdef MONGOC_ENABLE_SSL_SECURE_CHANNEL #include +#include + #include #include // For SCH_CREDENTIALS @@ -77,6 +79,8 @@ typedef struct { */ typedef struct { ssl_connect_state connecting_state; + mongoc_stream_tls_t *tls; /* back pointer to parent */ + char *hostname; /* the host name the stream was created with */ mongoc_shared_ptr cred_ptr; // Manages a mongoc_secure_channel_cred. mongoc_secure_channel_cred_handle *cred_handle; mongoc_secure_channel_ctxt *ctxt; @@ -87,6 +91,7 @@ typedef struct { unsigned long req_flags, ret_flags; int recv_unrecoverable_err; /* _mongoc_stream_tls_secure_channel_read had an unrecoverable err */ + bool recv_renegotiate; /* true if server requests renegotiation on TLS 1.3 */ bool recv_sspi_close_notify; /* true if connection closed by close_notify */ bool recv_connection_closed; /* true if connection closed, regardless how */ } mongoc_stream_tls_secure_channel_t; @@ -103,6 +108,7 @@ mongoc_secure_channel_cred_deleter(void *cred_void); struct _mongoc_stream_t * mongoc_stream_tls_secure_channel_new_with_creds(struct _mongoc_stream_t *base_stream, + const char *host, const struct _mongoc_ssl_opt_t *opt, mongoc_shared_ptr cred_ptr /* optional */); diff --git a/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c index 9b21d8ff190..c99155d54f1 100644 --- a/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c @@ -464,6 +464,7 @@ _mongoc_stream_tls_secure_channel_decrypt(mongoc_stream_tls_secure_channel_t *se { size_t size = 0; size_t remaining; + bool secbuf_extra_received = false; SecBuffer inbuf[4]; SecBufferDesc inbuf_desc; SECURITY_STATUS sspi_status = SEC_E_OK; @@ -474,6 +475,8 @@ _mongoc_stream_tls_secure_channel_decrypt(mongoc_stream_tls_secure_channel_t *se /* decrypt loop */ while (secure_channel->encdata_offset > 0 && sspi_status == SEC_E_OK) { + secbuf_extra_received = false; + /* prepare data buffer for DecryptMessage call */ _mongoc_secure_channel_init_sec_buffer(&inbuf[0], SECBUFFER_DATA, @@ -534,6 +537,8 @@ _mongoc_stream_tls_secure_channel_decrypt(mongoc_stream_tls_secure_channel_t *se secure_channel->encdata_offset = inbuf[3].cbBuffer; } + secbuf_extra_received = true; + TRACE("encrypted data cached: offset %d length %d", (int)secure_channel->encdata_offset, (int)secure_channel->encdata_length); @@ -546,6 +551,27 @@ _mongoc_stream_tls_secure_channel_decrypt(mongoc_stream_tls_secure_channel_t *se /* check if server wants to renegotiate the connection context */ if (sspi_status == SEC_I_RENEGOTIATE) { TRACE("%s", "remote party requests renegotiation"); + + if (secbuf_extra_received) { + bool ret; + bson_error_t error; + + secure_channel->recv_renegotiate = true; + + /* the tls handshake will pass the contents of SECBUFFER_EXTRA to the server */ + secure_channel->connecting_state = ssl_connect_2_writing; + ret = mongoc_secure_channel_handshake_step_2(secure_channel->tls, secure_channel->hostname, &error); + if (!ret) { + TRACE("TLS 1.3 renegotiation failed: %s", error.message); + secure_channel->recv_unrecoverable_err = true; + return; + } + + /* now continue decrypting data */ + secure_channel->connecting_state = ssl_connect_done; + sspi_status = SEC_E_OK; + continue; + } } /* check if the server closed the connection */ else if (sspi_status == SEC_I_CONTEXT_EXPIRED) { @@ -678,6 +704,12 @@ _mongoc_stream_tls_secure_channel_readv( ssize_t read_ret = _mongoc_stream_tls_secure_channel_read( stream, (char *)iov[i].iov_base + iov_pos, (int)(iov[i].iov_len - iov_pos)); + /* used up all read bytes for tls renegotiation, try reading again to get next message */ + if (read_ret == 0 && secure_channel->recv_renegotiate) { + secure_channel->recv_renegotiate = false; + continue; + } + if (read_ret < 0) { RETURN(-1); } @@ -990,13 +1022,13 @@ mongoc_secure_channel_cred_deleter(void *cred_void) mongoc_stream_t * mongoc_stream_tls_secure_channel_new(mongoc_stream_t *base_stream, const char *host, mongoc_ssl_opt_t *opt, int client) { - BSON_UNUSED(host); BSON_UNUSED(client); - return mongoc_stream_tls_secure_channel_new_with_creds(base_stream, opt, MONGOC_SHARED_PTR_NULL); + return mongoc_stream_tls_secure_channel_new_with_creds(base_stream, host, opt, MONGOC_SHARED_PTR_NULL); } mongoc_stream_t * mongoc_stream_tls_secure_channel_new_with_creds(mongoc_stream_t *base_stream, + const char *host, const mongoc_ssl_opt_t *opt, mongoc_shared_ptr cred_ptr) { @@ -1011,6 +1043,8 @@ mongoc_stream_tls_secure_channel_new_with_creds(mongoc_stream_t *base_stream, secure_channel = (mongoc_stream_tls_secure_channel_t *)bson_malloc0(sizeof *secure_channel); + secure_channel->hostname = bson_strdup(host); + secure_channel->decdata_buffer = bson_malloc(MONGOC_SCHANNEL_BUFFER_INIT_SIZE); secure_channel->decdata_length = MONGOC_SCHANNEL_BUFFER_INIT_SIZE; secure_channel->encdata_buffer = bson_malloc(MONGOC_SCHANNEL_BUFFER_INIT_SIZE); @@ -1035,6 +1069,8 @@ mongoc_stream_tls_secure_channel_new_with_creds(mongoc_stream_t *base_stream, tls->timeout_msec = -1; tls->base_stream = base_stream; + secure_channel->tls = tls; + TRACE("%s", "SSL/TLS connection with endpoint AcquireCredentialsHandle"); /* setup Schannel API options */ diff --git a/src/libmongoc/src/mongoc/mongoc-stream-tls.c b/src/libmongoc/src/mongoc/mongoc-stream-tls.c index 63954254f1e..3c2494f410c 100644 --- a/src/libmongoc/src/mongoc/mongoc-stream-tls.c +++ b/src/libmongoc/src/mongoc/mongoc-stream-tls.c @@ -222,6 +222,7 @@ mongoc_stream_tls_new_with_hostname_and_openssl_context( // Returns a new stream on success. Returns `NULL` on failure. mongoc_stream_t * mongoc_stream_tls_new_with_secure_channel_cred(mongoc_stream_t *base_stream, + const char *host, mongoc_ssl_opt_t *opt, mongoc_shared_ptr secure_channel_cred_ptr) { @@ -232,7 +233,7 @@ mongoc_stream_tls_new_with_secure_channel_cred(mongoc_stream_t *base_stream, // For compatibility with `mongoc_stream_tls_new_with_hostname`, modify `opt` directly: opt->allow_invalid_hostname = true; } - return mongoc_stream_tls_secure_channel_new_with_creds(base_stream, opt, secure_channel_cred_ptr); + return mongoc_stream_tls_secure_channel_new_with_creds(base_stream, host, opt, secure_channel_cred_ptr); } #endif // MONGOC_ENABLE_SSL_SECURE_CHANNEL diff --git a/src/libmongoc/src/mongoc/mongoc-topology-scanner.c b/src/libmongoc/src/mongoc/mongoc-topology-scanner.c index 57091ae8a31..339223a1b46 100644 --- a/src/libmongoc/src/mongoc/mongoc-topology-scanner.c +++ b/src/libmongoc/src/mongoc/mongoc-topology-scanner.c @@ -856,8 +856,8 @@ _mongoc_topology_scanner_node_setup_stream_for_tls(mongoc_topology_scanner_node_ tls_stream = mongoc_stream_tls_new_with_hostname_and_openssl_context( stream, node->host.host, node->ts->ssl_opts, 1, node->ts->openssl_ctx); #elif defined(MONGOC_ENABLE_SSL_SECURE_CHANNEL) - tls_stream = - mongoc_stream_tls_new_with_secure_channel_cred(stream, node->ts->ssl_opts, node->ts->secure_channel_cred_ptr); + tls_stream = mongoc_stream_tls_new_with_secure_channel_cred( + stream, node->host.host, node->ts->ssl_opts, node->ts->secure_channel_cred_ptr); #else tls_stream = mongoc_stream_tls_new_with_hostname(stream, node->host.host, node->ts->ssl_opts, 1); #endif From b952747fe9c1e970e332e4b5ffc34d30877ebcce Mon Sep 17 00:00:00 2001 From: Julia-Garland Date: Tue, 7 Oct 2025 20:05:55 +0000 Subject: [PATCH 2/6] Add missing arg --- src/libmongoc/tests/test-mongoc-secure-channel.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libmongoc/tests/test-mongoc-secure-channel.c b/src/libmongoc/tests/test-mongoc-secure-channel.c index 0d84e1a90c5..04ae59cc9cb 100644 --- a/src/libmongoc/tests/test-mongoc-secure-channel.c +++ b/src/libmongoc/tests/test-mongoc-secure-channel.c @@ -30,7 +30,8 @@ connect_with_secure_channel_cred(const mongoc_ssl_opt_t *ssl_opt, mongoc_shared_ return false; } - mongoc_stream_t *tls_stream = mongoc_stream_tls_secure_channel_new_with_creds(tcp_stream, ssl_opt, cred_ptr); + mongoc_stream_t *tls_stream = + mongoc_stream_tls_secure_channel_new_with_creds(tcp_stream, host.host, ssl_opt, cred_ptr); if (!tls_stream) { mongoc_stream_destroy(tcp_stream); return false; From 78dcc8e4f7da29e2677abf5b132a4231e02fe647 Mon Sep 17 00:00:00 2001 From: Julia-Garland Date: Wed, 8 Oct 2025 09:39:51 -0400 Subject: [PATCH 3/6] Add evergreen test --- .evergreen/generated_configs/legacy-config.yml | 7 +++++++ .../evergreen_config_lib/variants.py | 9 +++++++++ 2 files changed, 16 insertions(+) diff --git a/.evergreen/generated_configs/legacy-config.yml b/.evergreen/generated_configs/legacy-config.yml index 671f24d2453..cd2c8296813 100644 --- a/.evergreen/generated_configs/legacy-config.yml +++ b/.evergreen/generated_configs/legacy-config.yml @@ -10232,6 +10232,13 @@ buildvariants: - test-aws-openssl-regular-latest - .authentication-tests .openssl !.sasl - .authentication-tests .winssl +- name: windows-2022 + display_name: Windows (VS 2022) + expansions: + CC: Visual Studio 17 2022 Win64 + run_on: windows-vsCurrent-large + tasks: + - .authentication-tests .winssl - name: mingw-windows2016 display_name: MinGW-W64 (Windows Server 2016) expansions: diff --git a/.evergreen/legacy_config_generator/evergreen_config_lib/variants.py b/.evergreen/legacy_config_generator/evergreen_config_lib/variants.py index 1b1b7a67692..fd869da21b3 100644 --- a/.evergreen/legacy_config_generator/evergreen_config_lib/variants.py +++ b/.evergreen/legacy_config_generator/evergreen_config_lib/variants.py @@ -213,6 +213,15 @@ def days(n: int) -> int: ], {'CC': 'Visual Studio 15 2017 Win64'}, ), + Variant( + 'windows-2022', + 'Windows (VS 2022)', + 'windows-vsCurrent-large', + [ + '.authentication-tests .winssl', + ], + {'CC': 'Visual Studio 17 2022 Win64'}, + ), Variant( 'mingw-windows2016', 'MinGW-W64 (Windows Server 2016)', From 9c197f67d2be46d07a21752f7cfb925a4a0bc447 Mon Sep 17 00:00:00 2001 From: Julia-Garland Date: Wed, 8 Oct 2025 10:32:29 -0400 Subject: [PATCH 4/6] Add debug-compile-sspi-winssl to evergreen --- .evergreen/generated_configs/legacy-config.yml | 1 + .../legacy_config_generator/evergreen_config_lib/variants.py | 1 + 2 files changed, 2 insertions(+) diff --git a/.evergreen/generated_configs/legacy-config.yml b/.evergreen/generated_configs/legacy-config.yml index cd2c8296813..746a9530666 100644 --- a/.evergreen/generated_configs/legacy-config.yml +++ b/.evergreen/generated_configs/legacy-config.yml @@ -10238,6 +10238,7 @@ buildvariants: CC: Visual Studio 17 2022 Win64 run_on: windows-vsCurrent-large tasks: + - debug-compile-sspi-winssl - .authentication-tests .winssl - name: mingw-windows2016 display_name: MinGW-W64 (Windows Server 2016) diff --git a/.evergreen/legacy_config_generator/evergreen_config_lib/variants.py b/.evergreen/legacy_config_generator/evergreen_config_lib/variants.py index fd869da21b3..6bb09174cd0 100644 --- a/.evergreen/legacy_config_generator/evergreen_config_lib/variants.py +++ b/.evergreen/legacy_config_generator/evergreen_config_lib/variants.py @@ -218,6 +218,7 @@ def days(n: int) -> int: 'Windows (VS 2022)', 'windows-vsCurrent-large', [ + 'debug-compile-sspi-winssl', '.authentication-tests .winssl', ], {'CC': 'Visual Studio 17 2022 Win64'}, From 06a1199b78b39590aa77a781f8ce4d4595675cb2 Mon Sep 17 00:00:00 2001 From: Julia-Garland Date: Thu, 9 Oct 2025 14:04:59 -0400 Subject: [PATCH 5/6] KA suggestions --- src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c index c99155d54f1..1559fe65398 100644 --- a/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c @@ -191,6 +191,7 @@ _mongoc_stream_tls_secure_channel_destroy(mongoc_stream_t *stream) mongoc_stream_destroy(tls->base_stream); + bson_free(secure_channel->hostname); bson_free(secure_channel); bson_free(stream); @@ -558,7 +559,8 @@ _mongoc_stream_tls_secure_channel_decrypt(mongoc_stream_tls_secure_channel_t *se secure_channel->recv_renegotiate = true; - /* the tls handshake will pass the contents of SECBUFFER_EXTRA to the server */ + /* mongoc_secure_channel_handshake_step_2 passes the received SECBUFFER_EXTRA to + * InitializeSecurityContext */s secure_channel->connecting_state = ssl_connect_2_writing; ret = mongoc_secure_channel_handshake_step_2(secure_channel->tls, secure_channel->hostname, &error); if (!ret) { From 2ff30226da26baf12415a9841059afae3bbb6c9c Mon Sep 17 00:00:00 2001 From: Julia-Garland Date: Thu, 9 Oct 2025 14:41:19 -0400 Subject: [PATCH 6/6] Typo fix --- src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c b/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c index 1559fe65398..0d4b70d616e 100644 --- a/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c +++ b/src/libmongoc/src/mongoc/mongoc-stream-tls-secure-channel.c @@ -560,7 +560,7 @@ _mongoc_stream_tls_secure_channel_decrypt(mongoc_stream_tls_secure_channel_t *se secure_channel->recv_renegotiate = true; /* mongoc_secure_channel_handshake_step_2 passes the received SECBUFFER_EXTRA to - * InitializeSecurityContext */s + * InitializeSecurityContext */ secure_channel->connecting_state = ssl_connect_2_writing; ret = mongoc_secure_channel_handshake_step_2(secure_channel->tls, secure_channel->hostname, &error); if (!ret) {