diff --git a/tests/integrationv2/test_version_negotiation.py b/tests/integrationv2/test_version_negotiation.py index 38c37ef51c3..7d25db0adea 100644 --- a/tests/integrationv2/test_version_negotiation.py +++ b/tests/integrationv2/test_version_negotiation.py @@ -77,12 +77,12 @@ def test_s2nc_tls13_negotiates_tls12(managed_process, cipher, curve, certificate for results in server.get_results(): results.assert_success() + # This check only cares about S2N. Trying to maintain expected output of other providers doesn't add benefit to + # whether the S2N client was able to negotiate a lower TLS version. if provider is S2N: - # The server is only TLS12, so it reads the version from the CLIENT_HELLO, which is never above TLS12 - # This check only cares about S2N. Trying to maintain expected output of other providers doesn't - # add benefit to whether the S2N client was able to negotiate a lower TLS version. + # The client sends a TLS 1.3 client hello so a client protocol version of TLS 1.3 should always be expected. assert to_bytes("Client protocol version: {}".format( - actual_version)) in results.stdout + Protocols.TLS13.value)) in results.stdout assert to_bytes("Actual protocol version: {}".format( actual_version)) in results.stdout diff --git a/tests/unit/s2n_connection_protocol_versions_test.c b/tests/unit/s2n_connection_protocol_versions_test.c new file mode 100644 index 00000000000..ecd58666140 --- /dev/null +++ b/tests/unit/s2n_connection_protocol_versions_test.c @@ -0,0 +1,390 @@ +/* +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file is distributed +* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +* express or implied. See the License for the specific language governing +* permissions and limitations under the License. +*/ + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" + +static S2N_RESULT s2n_write_test_supported_versions_extension(struct s2n_blob *supported_versions_blob, uint8_t version, + uint8_t extension_length) +{ + RESULT_ENSURE_REF(supported_versions_blob); + + struct s2n_stuffer supported_versions_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&supported_versions_stuffer, supported_versions_blob)); + + /* Write the length byte. */ + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&supported_versions_stuffer, extension_length)); + /* Write the supported version. */ + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&supported_versions_stuffer, version / 10)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&supported_versions_stuffer, version % 10)); + + return S2N_RESULT_OK; +} + +struct s2n_overwrite_client_hello_ctx { + uint8_t client_hello_version; + uint8_t client_supported_version; + uint8_t extension_length; + + uint8_t supported_versions_data[3]; + int invoked_count; +}; + +static int s2n_overwrite_client_hello_cb(struct s2n_connection *conn, void *ctx) +{ + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(ctx); + + struct s2n_overwrite_client_hello_ctx *context = (struct s2n_overwrite_client_hello_ctx *) ctx; + context->invoked_count += 1; + + if (context->extension_length) { + struct s2n_blob supported_versions_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&supported_versions_blob, context->supported_versions_data, + sizeof(context->supported_versions_data))); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(conn); + EXPECT_NOT_NULL(client_hello); + + s2n_extension_type_id supported_versions_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(S2N_EXTENSION_SUPPORTED_VERSIONS, &supported_versions_id)); + s2n_parsed_extension *extension = &client_hello->extensions.parsed_extensions[supported_versions_id]; + + EXPECT_OK(s2n_write_test_supported_versions_extension(&supported_versions_blob, + context->client_supported_version, context->extension_length)); + + extension->extension_type = S2N_EXTENSION_SUPPORTED_VERSIONS; + extension->extension = supported_versions_blob; + } + + /* The client version fields are set when parsing the client hello before the client hello + * callback is invoked. The version fields are overridden to emulate receiving a client hello + * with a different version. + */ + if (context->client_hello_version) { + conn->client_hello_version = context->client_hello_version; + conn->client_protocol_version = context->client_hello_version; + } + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* Safety */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_client_protocol_version(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_client_hello_version(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_server_protocol_version(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_actual_protocol_version(NULL), S2N_ERR_NULL); + } + + /* Test protocol version getters on the server when a supported versions extension is received */ + for (uint8_t server_version = S2N_TLS12; server_version <= S2N_TLS13; server_version++) { + for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { + for (uint8_t client_supported_version = S2N_SSLv3; client_supported_version <= S2N_TLS13; client_supported_version++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + if (server_version == S2N_TLS12) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); + } else if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + } else { + continue; + } + + struct s2n_overwrite_client_hello_ctx context = { + .client_hello_version = client_hello_version, + .client_supported_version = client_supported_version, + .extension_length = 2, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension was received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_TRUE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_server_protocol_version(server), server_version); + EXPECT_EQUAL(s2n_connection_get_client_hello_version(server), client_hello_version); + + /* The reported client protocol version should always match the version specified + * in the supported versions extension, even for TLS 1.2 servers which don't + * process the extension for version selection. + */ + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_supported_version); + + uint8_t actual_protocol_version = s2n_connection_get_actual_protocol_version(server); + if (server_version == S2N_TLS12) { + /* For backwards compatibility, TLS 1.2 servers always use the client hello + * version to determine the client's maximum version, even if a supported + * versions extension was received. + */ + EXPECT_EQUAL(actual_protocol_version, MIN(server_version, client_hello_version)); + } else { + /* TLS 1.3 servers always use the version in the supported versions extension, + * regardless of the client hello version. + */ + EXPECT_EQUAL(actual_protocol_version, MIN(server_version, client_supported_version)); + } + } + } + } + + /* Test protocol version getters on the server when a supported versions extension isn't received */ + for (uint8_t server_version = S2N_TLS12; server_version <= S2N_TLS13; server_version++) { + for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + + /* A TLS 1.2 security policy is set to prevent the client from sending a supported + * versions extension. + */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all_tls12")); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + if (server_version == S2N_TLS12) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all_tls12")); + } else if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + } else { + continue; + } + + struct s2n_overwrite_client_hello_ctx context = { + .client_hello_version = client_hello_version, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension wasn't received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_FALSE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_server_protocol_version(server), server_version); + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_hello_version); + EXPECT_EQUAL(s2n_connection_get_client_hello_version(server), client_hello_version); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server), client_hello_version); + } + } + + /* Test protocol version getters on the client */ + for (uint8_t server_version = S2N_SSLv3; server_version <= S2N_TLS13; server_version++) { + if (server_version == S2N_TLS13 && !s2n_is_tls13_fully_supported()) { + continue; + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + server->server_protocol_version = server_version; + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_CERT)); + + EXPECT_EQUAL(s2n_connection_get_server_protocol_version(client), server_version); + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(client), s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(s2n_connection_get_client_hello_version(client), S2N_TLS12); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(client), server_version); + } + + /* Ensure that TLS 1.2 servers report the client hello version as the client protocol version + * if a malformed supported versions extension was received + */ + for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); + + struct s2n_overwrite_client_hello_ctx context = { + .client_hello_version = client_hello_version, + .client_supported_version = S2N_TLS13, + /* Write an invalid length */ + .extension_length = 11, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension was received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_TRUE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_hello_version); + } + + /* Ensure that TLS 1.2 servers report the client hello version as the client protocol version + * if an invalid supported version is received + */ + for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); + + struct s2n_overwrite_client_hello_ctx context = { + .client_hello_version = client_hello_version, + /* Write an invalid version */ + .client_supported_version = S2N_TLS13 + 10, + .extension_length = 2, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension was received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_TRUE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_hello_version); + } + + /* Ensure that TLS 1.3 servers report an unknown protocol version if a supported versions + * extension can't be processed + */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + + struct s2n_overwrite_client_hello_ctx context = { + .client_supported_version = S2N_TLS13, + /* Write an invalid length */ + .extension_length = 11, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO), + S2N_ERR_BAD_MESSAGE); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension was received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_TRUE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_server_protocol_version(server), S2N_TLS13); + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), s2n_unknown_protocol_version); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server), s2n_unknown_protocol_version); + } + + END_TEST(); +} diff --git a/tls/extensions/s2n_client_supported_versions.c b/tls/extensions/s2n_client_supported_versions.c index 1880dd83870..5be11407b6d 100644 --- a/tls/extensions/s2n_client_supported_versions.c +++ b/tls/extensions/s2n_client_supported_versions.c @@ -74,7 +74,8 @@ static int s2n_client_supported_versions_send(struct s2n_connection *conn, struc return S2N_SUCCESS; } -static int s2n_extensions_client_supported_versions_process(struct s2n_connection *conn, struct s2n_stuffer *extension) +int s2n_extensions_client_supported_versions_process(struct s2n_connection *conn, struct s2n_stuffer *extension, + uint8_t *client_protocol_version_out, uint8_t *actual_protocol_version_out) { uint8_t highest_supported_version = conn->server_protocol_version; uint8_t minimum_supported_version = s2n_unknown_protocol_version; @@ -85,8 +86,8 @@ static int s2n_extensions_client_supported_versions_process(struct s2n_connectio S2N_ERROR_IF(size_of_version_list != s2n_stuffer_data_available(extension), S2N_ERR_BAD_MESSAGE); S2N_ERROR_IF(size_of_version_list % S2N_TLS_PROTOCOL_VERSION_LEN != 0, S2N_ERR_BAD_MESSAGE); - conn->client_protocol_version = s2n_unknown_protocol_version; - conn->actual_protocol_version = s2n_unknown_protocol_version; + uint8_t client_protocol_version = s2n_unknown_protocol_version; + uint8_t actual_protocol_version = s2n_unknown_protocol_version; for (int i = 0; i < size_of_version_list; i += S2N_TLS_PROTOCOL_VERSION_LEN) { uint8_t client_version_parts[S2N_TLS_PROTOCOL_VERSION_LEN]; @@ -101,7 +102,7 @@ static int s2n_extensions_client_supported_versions_process(struct s2n_connectio uint16_t client_version = (client_version_parts[0] * 10) + client_version_parts[1]; - conn->client_protocol_version = MAX(client_version, conn->client_protocol_version); + client_protocol_version = MAX(client_version, client_protocol_version); if (client_version > highest_supported_version) { continue; @@ -113,27 +114,58 @@ static int s2n_extensions_client_supported_versions_process(struct s2n_connectio /* We ignore the client's preferred order and instead choose * the highest version that both client and server support. */ - conn->actual_protocol_version = MAX(client_version, conn->actual_protocol_version); + actual_protocol_version = MAX(client_version, actual_protocol_version); } - POSIX_ENSURE(conn->client_protocol_version != s2n_unknown_protocol_version, S2N_ERR_UNKNOWN_PROTOCOL_VERSION); - POSIX_ENSURE(conn->actual_protocol_version != s2n_unknown_protocol_version, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + *client_protocol_version_out = client_protocol_version; + *actual_protocol_version_out = actual_protocol_version; return S2N_SUCCESS; } -static int s2n_client_supported_versions_recv(struct s2n_connection *conn, struct s2n_stuffer *in) +static S2N_RESULT s2n_client_supported_versions_recv_impl(struct s2n_connection *conn, struct s2n_stuffer *extension) { + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(extension); + + RESULT_GUARD_POSIX(s2n_extensions_client_supported_versions_process(conn, extension, &conn->client_protocol_version, + &conn->actual_protocol_version)); + + RESULT_ENSURE(conn->client_protocol_version != s2n_unknown_protocol_version, S2N_ERR_UNKNOWN_PROTOCOL_VERSION); + RESULT_ENSURE(conn->actual_protocol_version != s2n_unknown_protocol_version, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + return S2N_RESULT_OK; +} + +static int s2n_client_supported_versions_recv(struct s2n_connection *conn, struct s2n_stuffer *extension) +{ + /* For backwards compatibility, the supported versions extension isn't used for protocol + * version selection if the server doesn't support TLS 1.3. This ensures that TLS 1.2 servers + * experience no behavior change due to processing the TLS 1.3 extension. See + * https://github.com/aws/s2n-tls/issues/4240. + * + *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.1 + *= type=exception + *= reason=The client hello legacy version is used for version selection on TLS 1.2 servers for backwards compatibility + *# If this extension is present in the ClientHello, servers MUST NOT use + *# the ClientHello.legacy_version value for version negotiation and MUST + *# use only the "supported_versions" extension to determine client + *# preferences. + */ if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) { return S2N_SUCCESS; } - int result = s2n_extensions_client_supported_versions_process(conn, in); - if (result != S2N_SUCCESS) { + s2n_result result = s2n_client_supported_versions_recv_impl(conn, extension); + if (s2n_result_is_error(result)) { + conn->client_protocol_version = s2n_unknown_protocol_version; + conn->actual_protocol_version = s2n_unknown_protocol_version; + s2n_queue_reader_unsupported_protocol_version_alert(conn); POSIX_ENSURE(s2n_errno != S2N_ERR_SAFETY, S2N_ERR_BAD_MESSAGE); } - POSIX_GUARD(result); + POSIX_GUARD_RESULT(result); + return S2N_SUCCESS; } diff --git a/tls/extensions/s2n_client_supported_versions.h b/tls/extensions/s2n_client_supported_versions.h index 7b69b92895c..a88b34f6ba9 100644 --- a/tls/extensions/s2n_client_supported_versions.h +++ b/tls/extensions/s2n_client_supported_versions.h @@ -20,6 +20,9 @@ extern const s2n_extension_type s2n_client_supported_versions_extension; +int s2n_extensions_client_supported_versions_process(struct s2n_connection *conn, struct s2n_stuffer *extension, + uint8_t *client_protocol_version_out, uint8_t *actual_protocol_version_out); + /* Old-style extension functions -- remove after extensions refactor is complete */ int s2n_extensions_client_supported_versions_recv(struct s2n_connection *conn, struct s2n_stuffer *extension); int s2n_extensions_client_supported_versions_size(struct s2n_connection *conn); diff --git a/tls/s2n_connection.c b/tls/s2n_connection.c index c57975fa9ff..3c592ba689c 100644 --- a/tls/s2n_connection.c +++ b/tls/s2n_connection.c @@ -32,6 +32,7 @@ #include "crypto/s2n_openssl_x509.h" #include "error/s2n_errno.h" #include "tls/extensions/s2n_client_server_name.h" +#include "tls/extensions/s2n_client_supported_versions.h" #include "tls/s2n_alerts.h" #include "tls/s2n_cipher_suites.h" #include "tls/s2n_handshake.h" @@ -938,10 +939,58 @@ const char *s2n_connection_get_kem_group_name(struct s2n_connection *conn) return conn->kex_params.client_kem_group_params.kem_group->name; } +static S2N_RESULT s2n_connection_get_client_supported_version(struct s2n_connection *conn, + uint8_t *client_supported_version) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_EQ(conn->mode, S2N_SERVER); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(conn); + RESULT_ENSURE_REF(client_hello); + + s2n_parsed_extension *supported_versions_extension = NULL; + RESULT_GUARD_POSIX(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SUPPORTED_VERSIONS, &client_hello->extensions, + &supported_versions_extension)); + RESULT_ENSURE_REF(supported_versions_extension); + + struct s2n_stuffer supported_versions_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&supported_versions_stuffer, &supported_versions_extension->extension)); + + uint8_t client_protocol_version = s2n_unknown_protocol_version; + uint8_t actual_protocol_version = s2n_unknown_protocol_version; + RESULT_GUARD_POSIX(s2n_extensions_client_supported_versions_process(conn, &supported_versions_stuffer, + &client_protocol_version, &actual_protocol_version)); + + RESULT_ENSURE_NE(client_protocol_version, s2n_unknown_protocol_version); + + *client_supported_version = client_protocol_version; + + return S2N_RESULT_OK; +} + int s2n_connection_get_client_protocol_version(struct s2n_connection *conn) { POSIX_ENSURE_REF(conn); + /* For backwards compatibility, the client_protocol_version field isn't updated via the + * supported versions extension on TLS 1.2 servers. See + * https://github.com/aws/s2n-tls/issues/4240. + * + * The extension is processed here to ensure that TLS 1.2 servers report the same client + * protocol version to applications as TLS 1.3 servers. + */ + if (conn->mode == S2N_SERVER && conn->server_protocol_version <= S2N_TLS12) { + uint8_t client_supported_version = s2n_unknown_protocol_version; + s2n_result result = s2n_connection_get_client_supported_version(conn, &client_supported_version); + + /* If the extension wasn't received, or if a client protocol version couldn't be determined + * after processing the extension, the extension is ignored. + */ + if (s2n_result_is_ok(result)) { + return client_supported_version; + } + } + return conn->client_protocol_version; }