Skip to content

Commit 9cca574

Browse files
authored
fix: Initial config influences client hello parsing (#4676)
1 parent 2c8ae53 commit 9cca574

7 files changed

+107
-50
lines changed

error/s2n_errno.c

+1
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ static const char *no_such_error = "Internal s2n error";
313313
ERR_ENTRY(S2N_ERR_INVALID_SERIALIZED_CONNECTION, "Serialized connection is invalid"); \
314314
ERR_ENTRY(S2N_ERR_TOO_MANY_CAS, "Too many certificate authorities in trust store"); \
315315
ERR_ENTRY(S2N_ERR_BAD_HEX, "Could not parse malformed hex string"); \
316+
ERR_ENTRY(S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK, "Config set to NULL before client hello callback. This should not be possible outside of tests."); \
316317
/* clang-format on */
317318

318319
#define ERR_STR_CASE(ERR, str) \

error/s2n_errno.h

+1
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ typedef enum {
239239
S2N_ERR_OSSL_PROVIDER,
240240
S2N_ERR_BAD_HEX,
241241
S2N_ERR_TEST_ASSERTION,
242+
S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK,
242243
S2N_ERR_T_INTERNAL_END,
243244

244245
/* S2N_ERR_T_USAGE */

tests/testlib/s2n_sslv2_client_hello.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
#pragma once
1717

18-
/* The following macros can be concatanated to generate a SSLv2 ClientHello
18+
/* The following macros can be concatenated to generate a SSLv2 ClientHello
1919
* message and record
2020
*/
2121

tests/unit/s2n_config_test.c

+47
Original file line numberDiff line numberDiff line change
@@ -1148,5 +1148,52 @@ int main(int argc, char **argv)
11481148
S2N_ERR_SECURITY_POLICY_INCOMPATIBLE_CERT);
11491149
};
11501150
};
1151+
1152+
/* Checks that servers don't use a config before the client hello callback is executed.
1153+
*
1154+
* We want to assert that a config is never used by a server until the client hello callback
1155+
* is called, given that users have the ability to swap out the config during this callback.
1156+
*/
1157+
{
1158+
DEFER_CLEANUP(struct s2n_config *tls12_client_config = s2n_config_new(), s2n_config_ptr_free);
1159+
EXPECT_NOT_NULL(tls12_client_config);
1160+
/* Security policy that only supports TLS12 */
1161+
EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_client_config, "20240501"));
1162+
1163+
DEFER_CLEANUP(struct s2n_config *tls13_client_config = s2n_config_new(), s2n_config_ptr_free);
1164+
EXPECT_NOT_NULL(tls13_client_config);
1165+
/* Security policy that supports TLS13 */
1166+
EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_client_config, "20240503"));
1167+
1168+
struct s2n_config *config_arr[] = { tls12_client_config, tls13_client_config };
1169+
1170+
/* Checks that the handshake gets as far as the client hello callback with a NULL config */
1171+
for (size_t i = 0; i < s2n_array_len(config_arr); i++) {
1172+
DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT),
1173+
s2n_connection_ptr_free);
1174+
EXPECT_NOT_NULL(client_conn);
1175+
DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER),
1176+
s2n_connection_ptr_free);
1177+
EXPECT_NOT_NULL(server_conn);
1178+
1179+
EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_arr[i]));
1180+
1181+
/* Server config pointer is explicitly set to NULL */
1182+
server_conn->config = NULL;
1183+
1184+
DEFER_CLEANUP(struct s2n_test_io_stuffer_pair test_io = { 0 },
1185+
s2n_io_stuffer_pair_free);
1186+
EXPECT_OK(s2n_io_stuffer_pair_init(&test_io));
1187+
EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &test_io));
1188+
1189+
/* S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK is only called in one location, just before the
1190+
* client hello callback. Therefore, we can assert that if we hit this error, we
1191+
* have gotten as far as the client hello callback without dereferencing the config.
1192+
*/
1193+
EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn),
1194+
S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK);
1195+
}
1196+
}
1197+
11511198
END_TEST();
11521199
}

tls/s2n_client_hello.c

+38-30
Original file line numberDiff line numberDiff line change
@@ -471,34 +471,6 @@ int s2n_parse_client_hello(struct s2n_connection *conn)
471471
conn->session_id_len = conn->client_hello.session_id.size;
472472
POSIX_CHECKED_MEMCPY(conn->session_id, conn->client_hello.session_id.data, conn->session_id_len);
473473

474-
/* Set default key exchange curve.
475-
* This is going to be our fallback if the client has no preference.
476-
*
477-
* P-256 is our preferred fallback option because the TLS1.3 RFC requires
478-
* all implementations to support it:
479-
*
480-
* https://tools.ietf.org/rfc/rfc8446#section-9.1
481-
* A TLS-compliant application MUST support key exchange with secp256r1 (NIST P-256)
482-
* and SHOULD support key exchange with X25519 [RFC7748]
483-
*
484-
*= https://www.rfc-editor.org/rfc/rfc4492#section-4
485-
*# A client that proposes ECC cipher suites may choose not to include these extensions.
486-
*# In this case, the server is free to choose any one of the elliptic curves or point formats listed in Section 5.
487-
*
488-
*/
489-
const struct s2n_ecc_preferences *ecc_pref = NULL;
490-
POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref));
491-
POSIX_ENSURE_REF(ecc_pref);
492-
POSIX_ENSURE_GT(ecc_pref->count, 0);
493-
if (s2n_ecc_preferences_includes_curve(ecc_pref, TLS_EC_CURVE_SECP_256_R1)) {
494-
conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp256r1;
495-
} else {
496-
/* If P-256 isn't allowed by the current security policy, instead choose
497-
* the first / most preferred curve.
498-
*/
499-
conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0];
500-
}
501-
502474
POSIX_GUARD_RESULT(s2n_client_hello_verify_for_retry(conn,
503475
&previous_hello_retry, &conn->client_hello, previous_client_random));
504476
return S2N_SUCCESS;
@@ -565,6 +537,34 @@ int s2n_process_client_hello(struct s2n_connection *conn)
565537
conn->actual_protocol_version = MIN(conn->server_protocol_version, S2N_TLS12);
566538
}
567539

540+
/* Set default key exchange curve.
541+
* This is going to be our fallback if the client has no preference.
542+
*
543+
* P-256 is our preferred fallback option because the TLS1.3 RFC requires
544+
* all implementations to support it:
545+
*
546+
* https://tools.ietf.org/rfc/rfc8446#section-9.1
547+
* A TLS-compliant application MUST support key exchange with secp256r1 (NIST P-256)
548+
* and SHOULD support key exchange with X25519 [RFC7748]
549+
*
550+
*= https://www.rfc-editor.org/rfc/rfc4492#section-4
551+
*# A client that proposes ECC cipher suites may choose not to include these extensions.
552+
*# In this case, the server is free to choose any one of the elliptic curves or point formats listed in Section 5.
553+
*
554+
*/
555+
const struct s2n_ecc_preferences *ecc_pref = NULL;
556+
POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref));
557+
POSIX_ENSURE_REF(ecc_pref);
558+
POSIX_ENSURE_GT(ecc_pref->count, 0);
559+
if (s2n_ecc_preferences_includes_curve(ecc_pref, TLS_EC_CURVE_SECP_256_R1)) {
560+
conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp256r1;
561+
} else {
562+
/* If P-256 isn't allowed by the current security policy, instead choose
563+
* the first / most preferred curve.
564+
*/
565+
conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0];
566+
}
567+
568568
POSIX_GUARD(s2n_extension_list_process(S2N_EXTENSION_LIST_CLIENT_HELLO, conn, &conn->client_hello.extensions));
569569

570570
/* After parsing extensions, select a curve and corresponding keyshare to use */
@@ -594,7 +594,8 @@ int s2n_process_client_hello(struct s2n_connection *conn)
594594
POSIX_CHECKED_MEMCPY(previous_cipher_suite_iana, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN);
595595

596596
/* Now choose the ciphers we have certs for. */
597-
POSIX_GUARD(s2n_set_cipher_as_tls_server(conn, client_hello->cipher_suites.data, client_hello->cipher_suites.size / 2));
597+
POSIX_GUARD(s2n_set_cipher_as_tls_server(conn, client_hello->cipher_suites.data,
598+
client_hello->cipher_suites.size / 2));
598599

599600
/* Check if this is the second client hello in a hello retry handshake */
600601
if (s2n_is_hello_retry_handshake(conn) && conn->handshake.message_number > 0) {
@@ -671,6 +672,12 @@ int s2n_client_hello_recv(struct s2n_connection *conn)
671672
/* Mark the client hello callback as invoked to avoid calling it again. */
672673
conn->client_hello.callback_invoked = true;
673674

675+
/* Do NOT move this null check. A test exists to assert that a server connection can get
676+
* as far as the client hello callback without using its config. To do this we need a
677+
* specific error for a null config just before the client hello callback. The test's
678+
* assertions are weakened if this check is moved. */
679+
POSIX_ENSURE(conn->config, S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK);
680+
674681
/* Call client_hello_cb if exists, letting application to modify s2n_connection or swap s2n_config */
675682
if (conn->config->client_hello_cb) {
676683
int rc = conn->config->client_hello_cb(conn, conn->config->client_hello_cb_ctx);
@@ -854,7 +861,8 @@ int s2n_sslv2_client_hello_recv(struct s2n_connection *conn)
854861
/* Find potential certificate matches before we choose the cipher. */
855862
POSIX_GUARD(s2n_conn_find_name_matching_certs(conn));
856863

857-
POSIX_GUARD(s2n_set_cipher_as_sslv2_server(conn, client_hello->cipher_suites.data, client_hello->cipher_suites.size / S2N_SSLv2_CIPHER_SUITE_LEN));
864+
POSIX_GUARD(s2n_set_cipher_as_sslv2_server(conn, client_hello->cipher_suites.data,
865+
client_hello->cipher_suites.size / S2N_SSLv2_CIPHER_SUITE_LEN));
858866
POSIX_GUARD_RESULT(s2n_signature_algorithm_select(conn));
859867
POSIX_GUARD(s2n_select_certs_for_server_auth(conn, &conn->handshake_params.our_chain_and_key));
860868

tls/s2n_connection.c

+1
Original file line numberDiff line numberDiff line change
@@ -1295,6 +1295,7 @@ S2N_CLEANUP_RESULT s2n_connection_apply_error_blinding(struct s2n_connection **c
12951295
case S2N_ERR_CANCELLED:
12961296
case S2N_ERR_CIPHER_NOT_SUPPORTED:
12971297
case S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED:
1298+
case S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK:
12981299
RESULT_GUARD(s2n_connection_set_closed(*conn));
12991300
break;
13001301
default:

tls/s2n_handshake_io.c

+18-19
Original file line numberDiff line numberDiff line change
@@ -1496,26 +1496,25 @@ static int s2n_handshake_read_io(struct s2n_connection *conn)
14961496
return S2N_SUCCESS;
14971497
}
14981498

1499-
s2n_cert_auth_type client_cert_auth_type;
1500-
POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type));
1501-
1502-
/* If client auth is optional, we initially assume it will not be requested.
1503-
* If we received a request, switch to a client auth handshake.
1504-
*/
1505-
if (conn->mode == S2N_CLIENT
1506-
&& client_cert_auth_type != S2N_CERT_AUTH_REQUIRED
1507-
&& message_type == TLS_CERT_REQ) {
1508-
POSIX_ENSURE(client_cert_auth_type == S2N_CERT_AUTH_OPTIONAL, S2N_ERR_UNEXPECTED_CERT_REQUEST);
1509-
POSIX_ENSURE(IS_FULL_HANDSHAKE(conn), S2N_ERR_HANDSHAKE_STATE);
1510-
POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, CLIENT_AUTH));
1511-
}
1499+
if (conn->mode == S2N_CLIENT) {
1500+
s2n_cert_auth_type client_cert_auth_type = { 0 };
1501+
POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type));
1502+
/* If client auth is optional, we initially assume it will not be requested.
1503+
* If we received a request, switch to a client auth handshake.
1504+
*/
1505+
if (client_cert_auth_type != S2N_CERT_AUTH_REQUIRED && message_type == TLS_CERT_REQ) {
1506+
POSIX_ENSURE(client_cert_auth_type == S2N_CERT_AUTH_OPTIONAL, S2N_ERR_UNEXPECTED_CERT_REQUEST);
1507+
POSIX_ENSURE(IS_FULL_HANDSHAKE(conn), S2N_ERR_HANDSHAKE_STATE);
1508+
POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, CLIENT_AUTH));
1509+
}
15121510

1513-
/* According to rfc6066 section 8, server may choose not to send "CertificateStatus" message even if it has
1514-
* sent "status_request" extension in the ServerHello message. */
1515-
if (conn->mode == S2N_CLIENT
1516-
&& EXPECTED_MESSAGE_TYPE(conn) == TLS_SERVER_CERT_STATUS
1517-
&& message_type != TLS_SERVER_CERT_STATUS) {
1518-
POSIX_GUARD_RESULT(s2n_handshake_type_unset_tls12_flag(conn, OCSP_STATUS));
1511+
/* According to rfc6066 section 8, the server may choose not to send a "CertificateStatus"
1512+
* message even if it has sent a "status_request" extension in the ServerHello message.
1513+
*/
1514+
if (EXPECTED_MESSAGE_TYPE(conn) == TLS_SERVER_CERT_STATUS
1515+
&& message_type != TLS_SERVER_CERT_STATUS) {
1516+
POSIX_GUARD_RESULT(s2n_handshake_type_unset_tls12_flag(conn, OCSP_STATUS));
1517+
}
15191518
}
15201519

15211520
/*

0 commit comments

Comments
 (0)