diff --git a/tests/unit/s2n_ktls_io_test.c b/tests/unit/s2n_ktls_io_test.c index 2f5aafcd922..d3951d43b41 100644 --- a/tests/unit/s2n_ktls_io_test.c +++ b/tests/unit/s2n_ktls_io_test.c @@ -96,7 +96,7 @@ int main(int argc, char **argv) { BEGIN_TEST(); - const uint8_t test_record_type = 43; + const uint8_t test_record_type = TLS_APPLICATION_DATA; /* test data */ uint8_t test_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; struct s2n_blob test_data_blob = { 0 }; @@ -562,6 +562,48 @@ int main(int argc, char **argv) }; }; + /* Test: s2n_ktls_recvmsg rejects invalid record types + * + * Iterates all 256 possible byte values for the TLS ContentType field + * delivered via kTLS ancillary data. Only alert (21), handshake (22), + * and application_data (23) are valid post-handshake record types per + * RFC 8446 Section 5. All others must be rejected with S2N_ERR_BAD_MESSAGE. + */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + + for (uint16_t record_type_val = 0; record_type_val <= UINT8_MAX; record_type_val++) { + uint8_t injected_record_type = (uint8_t) record_type_val; + bool is_valid = (injected_record_type >= TLS_ALERT + && injected_record_type <= TLS_APPLICATION_DATA); + + /* Inject a record with the test record_type into the client's receive buffer via the mock stuffer IO. */ + struct iovec msg_iov = { .iov_base = test_data, .iov_len = S2N_TEST_TO_SEND }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t bytes_written = 0; + EXPECT_OK(s2n_ktls_sendmsg(server->send_io_context, injected_record_type, &msg_iov, 1, &blocked, &bytes_written)); + EXPECT_EQUAL(bytes_written, S2N_TEST_TO_SEND); + + uint8_t recv_buf[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + uint8_t recv_record_type = 0; + size_t bytes_read = 0; + + if (is_valid) { + EXPECT_OK(s2n_ktls_recvmsg(client->recv_io_context, &recv_record_type, recv_buf, S2N_TEST_TO_SEND, &blocked, &bytes_read)); + EXPECT_EQUAL(recv_record_type, injected_record_type); + EXPECT_EQUAL(bytes_read, S2N_TEST_TO_SEND); + } else { + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_recvmsg(client->recv_io_context, &recv_record_type, + recv_buf, S2N_TEST_TO_SEND, &blocked, &bytes_read), + S2N_ERR_BAD_MESSAGE); + } + } + }; + /* Test s2n_ktls_send */ { const size_t test_iov_lens[] = { 10, 0, 1, 5, 100, 100, 10 }; diff --git a/tests/unit/s2n_self_talk_ktls_test.c b/tests/unit/s2n_self_talk_ktls_test.c index fae81fc0164..b98919ab960 100644 --- a/tests/unit/s2n_self_talk_ktls_test.c +++ b/tests/unit/s2n_self_talk_ktls_test.c @@ -347,33 +347,6 @@ int main(int argc, char **argv) EXPECT_BYTEARRAY_EQUAL(test_data, buffer, read); } - /* Test: s2n_recv with interleaved control messages */ - { - /* TLS1.3 and TLS1.2 have different expectations for control messages */ - EXPECT_EQUAL(reader->actual_protocol_version, S2N_TLS12); - - const uint8_t test_record_type = TLS_CHANGE_CIPHER_SPEC; - uint8_t control_record_data[] = "control record data"; - struct s2n_blob control_record = { 0 }; - EXPECT_SUCCESS(s2n_blob_init(&control_record, control_record_data, - sizeof(control_record_data))); - - for (size_t i = 0; i < 5; i++) { - EXPECT_OK(s2n_record_write(writer, test_record_type, &control_record)); - EXPECT_SUCCESS(s2n_flush(writer, &blocked)); - - int written = s2n_send(writer, test_data, sizeof(test_data), &blocked); - EXPECT_EQUAL(written, sizeof(test_data)); - - uint8_t buffer[sizeof(test_data)] = { 0 }; - int read = s2n_recv(reader, buffer, sizeof(buffer), &blocked); - EXPECT_EQUAL(read, sizeof(test_data)); - EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); - - EXPECT_BYTEARRAY_EQUAL(test_data, buffer, read); - } - }; - /* Test: s2n_recv with incorrectly encrypted application data * * This test closes the connection so should be the last test to use diff --git a/tls/s2n_ktls_io.c b/tls/s2n_ktls_io.c index 2e2703c8eae..740a2c1e37f 100644 --- a/tls/s2n_ktls_io.c +++ b/tls/s2n_ktls_io.c @@ -266,6 +266,25 @@ S2N_RESULT s2n_ktls_recvmsg(void *io_context, uint8_t *record_type, uint8_t *buf RESULT_GUARD(s2n_ktls_get_control_data(&msg, S2N_TLS_GET_RECORD_TYPE, record_type)); + /* Validate that record_type is a legitimate post-handshake TLS ContentType. + * + * kTLS is only enabled after the handshake completes, so the only valid + * record types on this path are: + * - TLS_ALERT (21): close_notify or error alerts + * - TLS_HANDSHAKE (22): post-handshake messages (e.g., KeyUpdate) + * - TLS_APPLICATION_DATA (23): normal data + * + * TLS_CHANGE_CIPHER_SPEC (20) is explicitly excluded because CCS is only + * valid before the peer's Finished message is received (RFC 8446 Section 5, + * paragraph 3), and kTLS is never active during the handshake. + * + * "If a TLS implementation receives an unexpected record type, it MUST + * terminate the connection with an 'unexpected_message' alert." + * - RFC 8446 Section 5 + */ + RESULT_ENSURE(*record_type >= TLS_ALERT, S2N_ERR_BAD_MESSAGE); + RESULT_ENSURE(*record_type <= TLS_APPLICATION_DATA, S2N_ERR_BAD_MESSAGE); + *blocked = S2N_NOT_BLOCKED; *bytes_read = result; return S2N_RESULT_OK;