Skip to content
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

ktls: recv alerts #4199

Merged
merged 7 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 82 additions & 0 deletions tests/unit/s2n_ktls_io_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "testlib/s2n_ktls_test_utils.h"
#include "testlib/s2n_testlib.h"
#include "tls/s2n_ktls.h"
#include "tls/s2n_tls.h"
#include "utils/s2n_random.h"

#define S2N_TEST_TO_SEND 10
Expand Down Expand Up @@ -902,5 +903,86 @@ int main(int argc, char **argv)
};
};

/* Test: s2n_ktls_read_full_record */
{
const struct iovec test_iovec = {
.iov_base = test_data,
.iov_len = sizeof(test_data),
};
s2n_blocked_status blocked = S2N_NOT_BLOCKED;

const size_t max_frag_len = S2N_DEFAULT_FRAGMENT_LENGTH;
/* Our test assumptions are wrong if this isn't true */
EXPECT_TRUE(max_frag_len < sizeof(test_data));

/* Safety */
{
DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(conn);
uint8_t record_type = 0;
EXPECT_FAILURE_WITH_ERRNO(s2n_ktls_read_full_record(NULL, &record_type),
S2N_ERR_NULL);
EXPECT_FAILURE_WITH_ERRNO(s2n_ktls_read_full_record(conn, NULL),
S2N_ERR_NULL);
};

/* Test: Basic read succeeds */
{
DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(conn);

DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 },
s2n_ktls_io_stuffer_pair_free);
EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair));
struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in;

size_t written = 0;
EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_ALERT, &test_iovec, 1, &blocked, &written));
EXPECT_EQUAL(written, sizeof(test_data));

uint8_t record_type = 0;
EXPECT_SUCCESS(s2n_ktls_read_full_record(conn, &record_type));
EXPECT_EQUAL(record_type, TLS_ALERT);

EXPECT_EQUAL(conn->in.blob.allocated, max_frag_len);
EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), max_frag_len);
uint8_t *read = s2n_stuffer_raw_read(&conn->in, max_frag_len);
EXPECT_BYTEARRAY_EQUAL(read, test_data, max_frag_len);
};

/* Test: Small read succeeds */
lrstewart marked this conversation as resolved.
Show resolved Hide resolved
{
const size_t small_frag_len = 10;
EXPECT_TRUE(small_frag_len < max_frag_len);
EXPECT_TRUE(small_frag_len < sizeof(test_data));
struct iovec small_test_iovec = test_iovec;
small_test_iovec.iov_len = small_frag_len;

DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(conn);

DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 },
s2n_ktls_io_stuffer_pair_free);
EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair));
struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in;

size_t written = 0;
EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_ALERT, &small_test_iovec, 1, &blocked, &written));
EXPECT_EQUAL(written, small_frag_len);

uint8_t record_type = 0;
EXPECT_SUCCESS(s2n_ktls_read_full_record(conn, &record_type));
EXPECT_EQUAL(record_type, TLS_ALERT);

EXPECT_EQUAL(conn->in.blob.allocated, max_frag_len);
EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), small_frag_len);
uint8_t *read = s2n_stuffer_raw_read(&conn->in, small_frag_len);
EXPECT_BYTEARRAY_EQUAL(read, test_data, small_frag_len);
};
};

END_TEST();
}
91 changes: 84 additions & 7 deletions tests/unit/s2n_self_talk_ktls_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "s2n_test.h"
#include "testlib/s2n_testlib.h"
#include "tls/s2n_ktls.h"
#include "tls/s2n_tls.h"
#include "utils/s2n_random.h"

/* There are issues with MacOS and FreeBSD so we define the constant ourselves.
Expand Down Expand Up @@ -116,6 +117,12 @@ int main(int argc, char **argv)
EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config));
EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default"));

/* Even if we detected ktls support at compile time, enabling ktls
* can fail at runtime if the system is not properly configured.
*/
bool ktls_send_supported = true;
bool ktls_recv_supported = true;

/* Test enabling ktls for sending */
{
DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT),
Expand Down Expand Up @@ -144,18 +151,23 @@ int main(int argc, char **argv)
EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.client));
EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client));

if (s2n_connection_ktls_enable_send(client) != S2N_SUCCESS) {
/* Even if we detected ktls support at compile time, enabling ktls
* can fail at runtime if the system is not properly configured.
*/
if (s2n_connection_ktls_enable_send(client) == S2N_SUCCESS) {
EXPECT_SUCCESS(s2n_connection_ktls_enable_send(server));
} else {
EXPECT_FALSE(ktls_expected);
END_TEST();
ktls_send_supported = false;
}

if (s2n_connection_ktls_enable_recv(client) == S2N_SUCCESS) {
EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(server));
} else {
EXPECT_FALSE(ktls_expected);
ktls_recv_supported = false;
}
EXPECT_SUCCESS(s2n_connection_ktls_enable_send(server));
};

/* Test sending with ktls */
for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) {
for (size_t mode_i = 0; mode_i < s2n_array_len(modes) && ktls_send_supported; mode_i++) {
lrstewart marked this conversation as resolved.
Show resolved Hide resolved
const s2n_mode mode = modes[mode_i];

DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT),
Expand Down Expand Up @@ -264,6 +276,71 @@ int main(int argc, char **argv)

EXPECT_SUCCESS(s2n_shutdown(reader, &blocked));
EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED);
EXPECT_TRUE(s2n_connection_check_io_status(reader, S2N_IO_CLOSED));
};
};

/* Test receiving with ktls */
for (size_t mode_i = 0; mode_i < s2n_array_len(modes) && ktls_recv_supported; mode_i++) {
const s2n_mode mode = modes[mode_i];

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(client);
EXPECT_SUCCESS(s2n_connection_set_config(server, config));

DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close);
EXPECT_OK(s2n_new_inet_socket_pair(&io_pair));
EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair));

/* The test negotiate method assumes non-blocking sockets */
EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server));
EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.client));
EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client));

struct s2n_connection *conns[] = {
[S2N_CLIENT] = client,
[S2N_SERVER] = server,
};
struct s2n_connection *reader = conns[mode];
struct s2n_connection *writer = conns[S2N_PEER_MODE(mode)];
EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(reader));

s2n_blocked_status blocked = S2N_NOT_BLOCKED;

/* Our IO methods are more predictable if they use blocking sockets. */
EXPECT_SUCCESS(s2n_fd_set_blocking(io_pair.server));
EXPECT_SUCCESS(s2n_fd_set_blocking(io_pair.client));

/* Test: s2n_recv not implemented yet */
{
uint8_t buffer[10] = { 0 };
int received = s2n_recv(reader, buffer, sizeof(buffer), &blocked);
EXPECT_FAILURE_WITH_ERRNO(received, S2N_ERR_UNIMPLEMENTED);
}

/* Test: s2n_shutdown */
{
/* Send some application data for the reader to skip */
for (size_t i = 0; i < 3; i++) {
EXPECT_SUCCESS(s2n_send(writer, test_data, 10, &blocked));
}

/* Send the close_notify */
EXPECT_SUCCESS(s2n_shutdown_send(writer, &blocked));
EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED);

/* Verify that the reader skips the application data and successfully
lrstewart marked this conversation as resolved.
Show resolved Hide resolved
* receives the close_notify.
*/
EXPECT_SUCCESS(s2n_shutdown(reader, &blocked));
EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED);
EXPECT_TRUE(s2n_connection_check_io_status(reader, S2N_IO_CLOSED));
};
};

Expand Down
135 changes: 135 additions & 0 deletions tests/unit/s2n_shutdown_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -704,5 +704,140 @@ int main(int argc, char **argv)
};
};

/* Test: ktls enabled */
{
/* Test: Successfully shutdown */
{
DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(client);
EXPECT_OK(s2n_ktls_configure_connection(client, S2N_KTLS_MODE_SEND));
EXPECT_OK(s2n_ktls_configure_connection(client, S2N_KTLS_MODE_RECV));
EXPECT_OK(s2n_skip_handshake(client));

DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(server);
EXPECT_OK(s2n_ktls_configure_connection(server, S2N_KTLS_MODE_SEND));
EXPECT_OK(s2n_ktls_configure_connection(server, S2N_KTLS_MODE_RECV));
EXPECT_OK(s2n_skip_handshake(server));

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));

s2n_blocked_status blocked = S2N_NOT_BLOCKED;
EXPECT_SUCCESS(s2n_shutdown_send(client, &blocked));
EXPECT_TRUE(client->alert_sent);

EXPECT_SUCCESS(s2n_shutdown(server, &blocked));
EXPECT_TRUE(server->alert_sent);
EXPECT_TRUE(s2n_connection_check_io_status(server, S2N_IO_CLOSED));
};

/* Test: Successfully shutdown after blocking */
{
DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(client);
EXPECT_OK(s2n_ktls_configure_connection(client, S2N_KTLS_MODE_SEND));
EXPECT_OK(s2n_ktls_configure_connection(client, S2N_KTLS_MODE_RECV));
EXPECT_OK(s2n_skip_handshake(client));

DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(server);
EXPECT_OK(s2n_ktls_configure_connection(server, S2N_KTLS_MODE_SEND));
EXPECT_OK(s2n_ktls_configure_connection(server, S2N_KTLS_MODE_RECV));
EXPECT_OK(s2n_skip_handshake(server));

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));

/* Setup the client->server stuffer to not fit the entire close_notify */
EXPECT_SUCCESS(s2n_stuffer_free(&io_pair.server_in.data_buffer));
EXPECT_SUCCESS(s2n_stuffer_alloc(&io_pair.server_in.data_buffer, 1));

s2n_blocked_status blocked = S2N_NOT_BLOCKED;
EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(client, &blocked), S2N_ERR_IO_BLOCKED);
EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE);
EXPECT_FALSE(s2n_connection_check_io_status(client, S2N_IO_WRITABLE));
EXPECT_TRUE(s2n_connection_check_io_status(client, S2N_IO_READABLE));

EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(server, &blocked), S2N_ERR_IO_BLOCKED);
EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ);
EXPECT_FALSE(s2n_connection_check_io_status(server, S2N_IO_WRITABLE));
EXPECT_TRUE(s2n_connection_check_io_status(server, S2N_IO_READABLE));

/* Reuse the client->server stuffer for the remaining close_notify */
EXPECT_SUCCESS(s2n_stuffer_wipe(&io_pair.server_in.data_buffer));

EXPECT_SUCCESS(s2n_shutdown(client, &blocked));
EXPECT_SUCCESS(s2n_shutdown(server, &blocked));
};

/* Test: Skip application data when waiting for close_notify */
{
DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(client);
EXPECT_OK(s2n_ktls_configure_connection(client, S2N_KTLS_MODE_SEND));
EXPECT_OK(s2n_ktls_configure_connection(client, S2N_KTLS_MODE_RECV));
EXPECT_OK(s2n_skip_handshake(client));

DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(server);
EXPECT_OK(s2n_ktls_configure_connection(server, S2N_KTLS_MODE_SEND));
EXPECT_OK(s2n_ktls_configure_connection(server, S2N_KTLS_MODE_RECV));
EXPECT_OK(s2n_skip_handshake(server));

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));

/* Send some application data for shutdown to skip */
uint8_t app_data[] = "hello world";
size_t app_data_size = sizeof(app_data);
s2n_blocked_status blocked = S2N_NOT_BLOCKED;
size_t app_data_count = 5;
for (size_t i = 0; i < app_data_count; i++) {
EXPECT_SUCCESS(s2n_send(client, app_data, app_data_size, &blocked));
EXPECT_SUCCESS(s2n_send(server, app_data, app_data_size, &blocked));
}
EXPECT_OK(s2n_test_validate_ancillary(&io_pair.client_in, TLS_APPLICATION_DATA, app_data_size));
EXPECT_OK(s2n_test_validate_ancillary(&io_pair.server_in, TLS_APPLICATION_DATA, app_data_size));
EXPECT_OK(s2n_test_records_in_ancillary(&io_pair.client_in, app_data_count));
EXPECT_OK(s2n_test_records_in_ancillary(&io_pair.server_in, app_data_count));

/* Client's first shutdown blocks on reading the close_notify,
* but successfully writes the close_notify and skips all the app data.*/
EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(client, &blocked), S2N_ERR_IO_BLOCKED);
EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ);
EXPECT_FALSE(s2n_connection_check_io_status(client, S2N_IO_WRITABLE));
EXPECT_TRUE(s2n_connection_check_io_status(client, S2N_IO_READABLE));
EXPECT_TRUE(client->alert_sent);
EXPECT_OK(s2n_test_records_in_ancillary(&io_pair.client_in, 0));
EXPECT_OK(s2n_test_records_in_ancillary(&io_pair.server_in, app_data_count + 1));

/* Server's first shutdown successfully skips all the app data
* and receives the close_notify */
EXPECT_SUCCESS(s2n_shutdown(server, &blocked));
EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED);
EXPECT_TRUE(s2n_connection_check_io_status(server, S2N_IO_CLOSED));
EXPECT_TRUE(server->alert_sent);
EXPECT_OK(s2n_test_records_in_ancillary(&io_pair.client_in, 1));
EXPECT_OK(s2n_test_records_in_ancillary(&io_pair.server_in, 0));

/* Client's second shutdown successfully receives the close_notify */
EXPECT_SUCCESS(s2n_shutdown(client, &blocked));
EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED);
EXPECT_TRUE(s2n_connection_check_io_status(client, S2N_IO_CLOSED));
EXPECT_OK(s2n_test_records_in_ancillary(&io_pair.client_in, 0));
EXPECT_OK(s2n_test_records_in_ancillary(&io_pair.server_in, 0));
};
};

END_TEST();
}
1 change: 1 addition & 0 deletions tls/s2n_ktls.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ ssize_t s2n_ktls_sendv_with_offset(struct s2n_connection *conn, const struct iov
ssize_t count, ssize_t offs, s2n_blocked_status *blocked);
int s2n_ktls_record_writev(struct s2n_connection *conn, uint8_t content_type,
const struct iovec *in, int in_count, size_t offs, size_t to_write);
int s2n_ktls_read_full_record(struct s2n_connection *conn, uint8_t *record_type);

/* These functions will be part of the public API. */
int s2n_connection_ktls_enable_send(struct s2n_connection *conn);
Expand Down
Loading