-
Notifications
You must be signed in to change notification settings - Fork 714
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: initial config should not influence sslv2 #4987
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -16,6 +16,7 @@ | |||||||||||||||||
#include "tls/s2n_config.h" | ||||||||||||||||||
|
||||||||||||||||||
#include <stdlib.h> | ||||||||||||||||||
#include <testlib/s2n_sslv2_client_hello.h> | ||||||||||||||||||
|
||||||||||||||||||
#include "api/s2n.h" | ||||||||||||||||||
#include "crypto/s2n_fips.h" | ||||||||||||||||||
|
@@ -27,6 +28,7 @@ | |||||||||||||||||
#include "tls/s2n_internal.h" | ||||||||||||||||||
#include "tls/s2n_record.h" | ||||||||||||||||||
#include "tls/s2n_security_policies.h" | ||||||||||||||||||
#include "tls/s2n_tls.h" | ||||||||||||||||||
#include "tls/s2n_tls13.h" | ||||||||||||||||||
#include "unstable/npn.h" | ||||||||||||||||||
#include "utils/s2n_map.h" | ||||||||||||||||||
|
@@ -1232,5 +1234,44 @@ int main(int argc, char **argv) | |||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
/* Checks that servers don't use a config before the client hello callback is executed on a | ||||||||||||||||||
* SSLv2-formatted client hello. | ||||||||||||||||||
* | ||||||||||||||||||
* Parsing SSLv2 hellos uses a different code path and need to be tested separately. | ||||||||||||||||||
*/ | ||||||||||||||||||
{ | ||||||||||||||||||
uint8_t sslv2_client_hello[] = { | ||||||||||||||||||
SSLv2_CLIENT_HELLO_PREFIX, | ||||||||||||||||||
SSLv2_CLIENT_HELLO_CIPHER_SUITES, | ||||||||||||||||||
SSLv2_CLIENT_HELLO_CHALLENGE, | ||||||||||||||||||
}; | ||||||||||||||||||
|
||||||||||||||||||
struct s2n_blob client_hello = { | ||||||||||||||||||
.data = sslv2_client_hello, | ||||||||||||||||||
.size = sizeof(sslv2_client_hello), | ||||||||||||||||||
.allocated = 0, | ||||||||||||||||||
.growable = 0 | ||||||||||||||||||
}; | ||||||||||||||||||
Comment on lines
+1249
to
+1254
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We try to avoid directly instantiating blobs and stuffers. That bypasses any safety checks / additional initialization logic. I'm guessing you copied from the other test, but our best practices have gotten better ;) Instead it should probably be:
Suggested change
|
||||||||||||||||||
|
||||||||||||||||||
/* Checks that the handshake gets as far as the client hello callback with a NULL config */ | ||||||||||||||||||
{ | ||||||||||||||||||
DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), | ||||||||||||||||||
s2n_connection_ptr_free); | ||||||||||||||||||
EXPECT_NOT_NULL(server_conn); | ||||||||||||||||||
server_conn->config = NULL; | ||||||||||||||||||
|
||||||||||||||||||
/* Record version and protocol version are in the header for SSLv2 */ | ||||||||||||||||||
server_conn->client_hello_version = S2N_SSLv2; | ||||||||||||||||||
server_conn->client_protocol_version = S2N_TLS12; | ||||||||||||||||||
|
||||||||||||||||||
/* S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK is only called in one location, just before the | ||||||||||||||||||
* client hello callback. Therefore, we can assert that if we hit this error, we | ||||||||||||||||||
* have gotten as far as the client hello callback without dereferencing the config. | ||||||||||||||||||
*/ | ||||||||||||||||||
EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_hello)); | ||||||||||||||||||
EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_CONFIG_NULL_BEFORE_CH_CALLBACK); | ||||||||||||||||||
} | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
END_TEST(); | ||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -446,7 +446,7 @@ int s2n_parse_client_hello(struct s2n_connection *conn) | |
} | ||
|
||
if (conn->client_hello_version == S2N_SSLv2) { | ||
POSIX_GUARD(s2n_sslv2_client_hello_recv(conn)); | ||
POSIX_GUARD(s2n_sslv2_client_hello_parse(conn)); | ||
return S2N_SUCCESS; | ||
} | ||
|
||
|
@@ -594,8 +594,13 @@ int s2n_process_client_hello(struct s2n_connection *conn) | |
POSIX_CHECKED_MEMCPY(previous_cipher_suite_iana, conn->secure->cipher_suite->iana_value, S2N_TLS_CIPHER_SUITE_LEN); | ||
|
||
/* Now choose the ciphers we have certs for. */ | ||
POSIX_GUARD(s2n_set_cipher_as_tls_server(conn, client_hello->cipher_suites.data, | ||
client_hello->cipher_suites.size / 2)); | ||
if (conn->client_hello_version == S2N_SSLv2) { | ||
POSIX_GUARD(s2n_set_cipher_as_sslv2_server(conn, client_hello->cipher_suites.data, | ||
client_hello->cipher_suites.size / S2N_SSLv2_CIPHER_SUITE_LEN)); | ||
} else { | ||
POSIX_GUARD(s2n_set_cipher_as_tls_server(conn, client_hello->cipher_suites.data, | ||
client_hello->cipher_suites.size / 2)); | ||
} | ||
Comment on lines
+597
to
+603
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make more sense for this branching to happen in s2n_set_cipher_as_tls_server? |
||
|
||
/* Check if this is the second client hello in a hello retry handshake */ | ||
if (s2n_is_hello_retry_handshake(conn) && conn->handshake.message_number > 0) { | ||
|
@@ -685,9 +690,7 @@ int s2n_client_hello_recv(struct s2n_connection *conn) | |
} | ||
} | ||
|
||
if (conn->client_hello_version != S2N_SSLv2) { | ||
POSIX_GUARD(s2n_process_client_hello(conn)); | ||
} | ||
POSIX_GUARD(s2n_process_client_hello(conn)); | ||
|
||
return 0; | ||
} | ||
|
@@ -821,7 +824,7 @@ int s2n_client_hello_send(struct s2n_connection *conn) | |
* Alternatively, the TLS1.0 RFC includes a more modern description of the format: | ||
* https://tools.ietf.org/rfc/rfc2246 Appendix E.1 | ||
*/ | ||
int s2n_sslv2_client_hello_recv(struct s2n_connection *conn) | ||
int s2n_sslv2_client_hello_parse(struct s2n_connection *conn) | ||
{ | ||
struct s2n_client_hello *client_hello = &conn->client_hello; | ||
client_hello->sslv2 = true; | ||
|
@@ -831,15 +834,6 @@ int s2n_sslv2_client_hello_recv(struct s2n_connection *conn) | |
POSIX_GUARD(s2n_stuffer_skip_write(&in_stuffer, client_hello->raw_message.size)); | ||
struct s2n_stuffer *in = &in_stuffer; | ||
|
||
const struct s2n_security_policy *security_policy = NULL; | ||
POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy)); | ||
|
||
if (conn->client_protocol_version < security_policy->minimum_protocol_version) { | ||
POSIX_GUARD(s2n_queue_reader_unsupported_protocol_version_alert(conn)); | ||
POSIX_BAIL(S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); | ||
} | ||
conn->actual_protocol_version = MIN(conn->client_protocol_version, conn->server_protocol_version); | ||
|
||
/* We start 5 bytes into the record */ | ||
uint16_t cipher_suites_length = 0; | ||
POSIX_GUARD(s2n_stuffer_read_uint16(in, &cipher_suites_length)); | ||
|
@@ -858,14 +852,6 @@ int s2n_sslv2_client_hello_recv(struct s2n_connection *conn) | |
client_hello->cipher_suites.data = s2n_stuffer_raw_read(in, cipher_suites_length); | ||
POSIX_ENSURE_REF(client_hello->cipher_suites.data); | ||
|
||
/* Find potential certificate matches before we choose the cipher. */ | ||
POSIX_GUARD(s2n_conn_find_name_matching_certs(conn)); | ||
|
||
POSIX_GUARD(s2n_set_cipher_as_sslv2_server(conn, client_hello->cipher_suites.data, | ||
client_hello->cipher_suites.size / S2N_SSLv2_CIPHER_SUITE_LEN)); | ||
POSIX_GUARD_RESULT(s2n_signature_algorithm_select(conn)); | ||
POSIX_GUARD(s2n_select_certs_for_server_auth(conn, &conn->handshake_params.our_chain_and_key)); | ||
|
||
S2N_ERROR_IF(session_id_length > s2n_stuffer_data_available(in), S2N_ERR_BAD_MESSAGE); | ||
POSIX_GUARD(s2n_blob_init(&client_hello->session_id, s2n_stuffer_raw_read(in, session_id_length), session_id_length)); | ||
if (session_id_length > 0 && session_id_length <= S2N_TLS_SESSION_ID_MAX_LEN) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,9 +27,10 @@ int s2n_flush(struct s2n_connection *conn, s2n_blocked_status *more); | |
S2N_RESULT s2n_client_hello_request_validate(struct s2n_connection *conn); | ||
S2N_RESULT s2n_client_hello_request_recv(struct s2n_connection *conn); | ||
int s2n_client_hello_send(struct s2n_connection *conn); | ||
int s2n_parse_client_hello(struct s2n_connection *conn); | ||
Comment on lines
29
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you're going to make this method "public" than you should probably give it a better name: s2n_client_hello_parse. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Am I missing something 🤔 ? Why do you need to make this method public at all? |
||
int s2n_client_hello_recv(struct s2n_connection *conn); | ||
int s2n_establish_session(struct s2n_connection *conn); | ||
int s2n_sslv2_client_hello_recv(struct s2n_connection *conn); | ||
int s2n_sslv2_client_hello_parse(struct s2n_connection *conn); | ||
int s2n_server_hello_retry_send(struct s2n_connection *conn); | ||
int s2n_server_hello_retry_recv(struct s2n_connection *conn); | ||
int s2n_server_hello_write_message(struct s2n_connection *conn); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
conn->server_protocol_version
is set tos2n_highest_protocol_version
ins2n_connection_new
.s2n_highest_protocol_version
is either TLS 1.2 or TLS 1.3, depending on whethers2n_disable/enable_tls13_test
functions have been used.Previously
server_protocol_version was never modified for SSLv2 client hellos. This line was just asserting on
s2n_highest_protocol_version
which was toggled between test runs bys2n_disable/enable_tls13_test
.Proposed
server_protocol_version
is now modified by this section inprocess_client_hello
.s2n-tls/tls/s2n_client_hello.c
Lines 535 to 538 in cc2ea72
From my reading/audit of the codebase,☹️
server_protocol_version
is mostly used for various checks and assertions related to downgrade protection. From the brief audit that I did, I didn't see any places that this would be a noticeable behavior change, but this part of the codebase is pretty new to me and our SSLV2 test coverage is pretty lowI'll also throw out that
server_protocol_version
being set to TLS13 while using SSLV2 client hellos seems suspicious given this RFC requirementThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's definitely a bug. Later code is going to assume that server_protocol_version was set properly, regardless of what it's currently used for.
But the TLS1.3 case not working makes sense. SSLv2 ClientHellos don't contain extensions, and the only way to negotiate TLS1.3 is via extension. Even if that wasn't the case, we're using a hard-coded ClientHello so s2n_disable/enable_tls13_test would have no effect on it.
So yes, your fix is correct. But it's arguable whether the two different test setups are even necessary. Yes, s2n_disable/enable_tls13_test would still affect the server itself, but we're only setting the "tls12_config" on the server. I think this test needs a little more attention.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe just remove s2n_enable/disable_tls13_in_test() for this test? If we're explicitly setting the config then it's not necessary.