diff --git a/.evergreen/generated_configs/legacy-config.yml b/.evergreen/generated_configs/legacy-config.yml index 671f24d245..746a953066 100644 --- a/.evergreen/generated_configs/legacy-config.yml +++ b/.evergreen/generated_configs/legacy-config.yml @@ -10232,6 +10232,14 @@ 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: + - debug-compile-sspi-winssl + - .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 1b1b7a6769..6bb09174cd 100644 --- a/.evergreen/legacy_config_generator/evergreen_config_lib/variants.py +++ b/.evergreen/legacy_config_generator/evergreen_config_lib/variants.py @@ -213,6 +213,16 @@ def days(n: int) -> int: ], {'CC': 'Visual Studio 15 2017 Win64'}, ), + Variant( + 'windows-2022', + 'Windows (VS 2022)', + 'windows-vsCurrent-large', + [ + 'debug-compile-sspi-winssl', + '.authentication-tests .winssl', + ], + {'CC': 'Visual Studio 17 2022 Win64'}, + ), Variant( 'mingw-windows2016', 'MinGW-W64 (Windows Server 2016)', diff --git a/src/libmongoc/src/mongoc/mongoc-client.c b/src/libmongoc/src/mongoc/mongoc-client.c index acc2d42229..1cd342f0ec 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 0ba5107c5f..cdaf154ca6 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 bf22348ec1..5d0ebd167d 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 9b21d8ff19..c99155d54f 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 63954254f1..3c2494f410 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 57091ae8a3..339223a1b4 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 diff --git a/src/libmongoc/tests/test-mongoc-secure-channel.c b/src/libmongoc/tests/test-mongoc-secure-channel.c index 0d84e1a90c..04ae59cc9c 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;