From 0c5ccb93428e1f588d020290f94bd4e3c9bab7d3 Mon Sep 17 00:00:00 2001 From: Apoorv Kothari Date: Mon, 19 Jun 2023 15:44:21 -0700 Subject: [PATCH] ktls: config socket ULP --- error/s2n_errno.c | 7 + error/s2n_errno.h | 7 + tests/testlib/s2n_connection_test_utils.c | 9 + tests/testlib/s2n_testlib.h | 1 + tests/unit/s2n_ktls_test.c | 216 +++++++++++++++++++++- tests/unit/s2n_shutdown_test.c | 9 - tls/s2n_ktls.c | 167 ++++++++++++++++- tls/s2n_ktls.h | 21 ++- tls/s2n_ktls_linux.h | 31 ++++ tls/s2n_ktls_unsupported.h | 26 +++ 10 files changed, 478 insertions(+), 16 deletions(-) create mode 100644 tls/s2n_ktls_linux.h create mode 100644 tls/s2n_ktls_unsupported.h diff --git a/error/s2n_errno.c b/error/s2n_errno.c index c958e9f3065..a80185e859e 100644 --- a/error/s2n_errno.c +++ b/error/s2n_errno.c @@ -290,6 +290,13 @@ static const char *no_such_error = "Internal s2n error"; ERR_ENTRY(S2N_ERR_INTERNAL_LIBCRYPTO_ERROR, "An internal error has occurred in the libcrypto API") \ ERR_ENTRY(S2N_ERR_NO_RENEGOTIATION, "Only secure, server-initiated renegotiation is supported") \ ERR_ENTRY(S2N_ERR_APP_DATA_BLOCKED, "Blocked on application data during handshake") \ + ERR_ENTRY(S2N_ERR_KTLS_ALREADY_ENABLED, "kTLS is already enabled on the socket") \ + ERR_ENTRY(S2N_ERR_KTLS_MANAGED_IO, "kTLS cannot be enabled while custom I/O is configured for the connection") \ + ERR_ENTRY(S2N_ERR_KTLS_HANDSHAKE_NOT_COMPLETE, "kTLS can only be enabled once the handshake is completed") \ + ERR_ENTRY(S2N_ERR_KTLS_UNSUPPORTED_PLATFORM, "kTLS is unsupported on this platform") \ + ERR_ENTRY(S2N_ERR_KTLS_UNSUPPORTED_CONN, "kTLS is unsupported for this connection") \ + ERR_ENTRY(S2N_ERR_KTLS_ULP, "An error occurred when attempting to configure the socket for kTLS. Ensure the 'tls' kernel module is enabled.") \ + ERR_ENTRY(S2N_ERR_KTLS_DISABLED_FOR_TEST, "Some kTLS operations have been disabled for testing") \ ERR_ENTRY(S2N_ERR_ATOMIC, "Atomic operations in this environment would require locking") \ /* clang-format on */ diff --git a/error/s2n_errno.h b/error/s2n_errno.h index dd5b841b1c0..725b42b2e6b 100644 --- a/error/s2n_errno.h +++ b/error/s2n_errno.h @@ -306,6 +306,13 @@ typedef enum { S2N_ERR_SECRET_SCHEDULE_STATE, S2N_ERR_CERT_OWNERSHIP, S2N_ERR_INTERNAL_LIBCRYPTO_ERROR, + S2N_ERR_KTLS_ALREADY_ENABLED, + S2N_ERR_KTLS_MANAGED_IO, + S2N_ERR_KTLS_HANDSHAKE_NOT_COMPLETE, + S2N_ERR_KTLS_UNSUPPORTED_PLATFORM, + S2N_ERR_KTLS_UNSUPPORTED_CONN, + S2N_ERR_KTLS_ULP, + S2N_ERR_KTLS_DISABLED_FOR_TEST, S2N_ERR_ATOMIC, S2N_ERR_T_USAGE_END, } s2n_error; diff --git a/tests/testlib/s2n_connection_test_utils.c b/tests/testlib/s2n_connection_test_utils.c index 977d90843c9..546665d45fa 100644 --- a/tests/testlib/s2n_connection_test_utils.c +++ b/tests/testlib/s2n_connection_test_utils.c @@ -332,3 +332,12 @@ S2N_RESULT s2n_set_all_mutually_supported_groups(struct s2n_connection *conn) return S2N_RESULT_OK; } + +S2N_RESULT s2n_skip_handshake(struct s2n_connection *conn) +{ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + while (!s2n_handshake_is_complete(conn)) { + conn->handshake.message_number++; + } + return S2N_RESULT_OK; +} diff --git a/tests/testlib/s2n_testlib.h b/tests/testlib/s2n_testlib.h index 9e4dd6d4e99..1cb99b052a0 100644 --- a/tests/testlib/s2n_testlib.h +++ b/tests/testlib/s2n_testlib.h @@ -75,6 +75,7 @@ int s2n_connection_allow_response_extension(struct s2n_connection *conn, uint16_ int s2n_connection_allow_all_response_extensions(struct s2n_connection *conn); int s2n_connection_set_all_protocol_versions(struct s2n_connection *conn, uint8_t version); S2N_RESULT s2n_set_all_mutually_supported_groups(struct s2n_connection *conn); +S2N_RESULT s2n_skip_handshake(struct s2n_connection *conn); S2N_RESULT s2n_connection_set_secrets(struct s2n_connection *conn); diff --git a/tests/unit/s2n_ktls_test.c b/tests/unit/s2n_ktls_test.c index fc8b427f21d..fcd0e487811 100644 --- a/tests/unit/s2n_ktls_test.c +++ b/tests/unit/s2n_ktls_test.c @@ -13,13 +13,53 @@ * permissions and limitations under the License. */ -#include "crypto/s2n_cipher.h" +#include "tls/s2n_ktls.h" + #include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +S2N_RESULT s2n_ktls_retrieve_file_descriptor(struct s2n_connection *conn, s2n_ktls_mode ktls_mode, int *fd); +S2N_RESULT s2n_disable_ktls_socket_config_for_testing(void); + +/* set kTLS supported cipher */ +struct s2n_cipher ktls_temp_supported_cipher = { + .ktls_supported = true, +}; +struct s2n_record_algorithm ktls_temp_supported_record_alg = { + .cipher = &ktls_temp_supported_cipher, +}; +struct s2n_cipher_suite ktls_temp_supported_cipher_suite = { + .record_alg = &ktls_temp_supported_record_alg, +}; + +S2N_RESULT s2n_test_configure_mock_ktls_connection(struct s2n_connection *conn, int fd, bool complete_handshake) +{ + RESULT_ENSURE_REF(conn); + + /* config I/O */ + RESULT_GUARD_POSIX(s2n_connection_set_write_fd(conn, fd)); + RESULT_GUARD_POSIX(s2n_connection_set_read_fd(conn, fd)); + conn->managed_send_io = true; + conn->managed_recv_io = true; + conn->ktls_send_enabled = false; + conn->ktls_recv_enabled = false; + + /* configure connection so that the handshake is complete */ + conn->secure->cipher_suite = &ktls_temp_supported_cipher_suite; + conn->actual_protocol_version = S2N_TLS12; + if (complete_handshake) { + RESULT_GUARD(s2n_skip_handshake(conn)); + } + + return S2N_RESULT_OK; +} int main(int argc, char **argv) { BEGIN_TEST(); + EXPECT_OK(s2n_disable_ktls_socket_config_for_testing()); + /* ktls_supported ciphers */ { struct s2n_cipher cipher = s2n_aes128_gcm; @@ -38,5 +78,179 @@ int main(int argc, char **argv) EXPECT_FALSE(cipher.ktls_supported); }; + if (!s2n_ktls_is_supported_on_platform()) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + int fd = 1; + EXPECT_OK(s2n_test_configure_mock_ktls_connection(server_conn, fd, true)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_UNSUPPORTED_PLATFORM); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), S2N_ERR_KTLS_UNSUPPORTED_PLATFORM); + } else { + /* ktls handshake must be complete */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + int fd = 1; + EXPECT_OK(s2n_test_configure_mock_ktls_connection(server_conn, fd, false)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_HANDSHAKE_NOT_COMPLETE); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), S2N_ERR_KTLS_HANDSHAKE_NOT_COMPLETE); + }; + + /* s2n_connection_ktls_enable */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + int fd = 1; + EXPECT_OK(s2n_test_configure_mock_ktls_connection(server_conn, fd, true)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_DISABLED_FOR_TEST); + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), S2N_ERR_KTLS_DISABLED_FOR_TEST); + }; + + /* ktls already enabled */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + int fd = 1; + EXPECT_OK(s2n_test_configure_mock_ktls_connection(server_conn, fd, true)); + + server_conn->ktls_send_enabled = true; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_ALREADY_ENABLED); + + server_conn->ktls_recv_enabled = true; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), S2N_ERR_KTLS_ALREADY_ENABLED); + }; + + /* unsupported protocols */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + int fd = 1; + EXPECT_OK(s2n_test_configure_mock_ktls_connection(server_conn, fd, true)); + + server_conn->actual_protocol_version = S2N_TLS13; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_UNSUPPORTED_CONN); + + server_conn->actual_protocol_version = S2N_TLS11; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_UNSUPPORTED_CONN); + }; + + /* unsupported ciphers */ + { + /* set kTLS un-supported cipher */ + struct s2n_cipher ktls_temp_unsupported_cipher = { + .ktls_supported = false, + }; + struct s2n_record_algorithm ktls_temp_unsupported_record_alg = { + .cipher = &ktls_temp_unsupported_cipher, + }; + struct s2n_cipher_suite ktls_temp_unsupported_cipher_suite = { + .record_alg = &ktls_temp_unsupported_record_alg, + }; + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + int fd = 1; + EXPECT_OK(s2n_test_configure_mock_ktls_connection(server_conn, fd, true)); + + /* base case */ + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_DISABLED_FOR_TEST); + + server_conn->ktls_send_enabled = false; /* reset ktls enable connection */ + server_conn->secure->cipher_suite = &ktls_temp_unsupported_cipher_suite; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_UNSUPPORTED_CONN); + }; + + /* drain buffer prior to enabling kTLS */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + int fd = 1; + EXPECT_OK(s2n_test_configure_mock_ktls_connection(server_conn, fd, true)); + + /* base case */ + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_DISABLED_FOR_TEST); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), S2N_ERR_KTLS_DISABLED_FOR_TEST); + + uint8_t write_byte = 8; + uint8_t read_byte = 0; + /* write to conn->out buffer and assert error */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server_conn->out, &write_byte, 1)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); + /* drain conn->out buffer and assert base case */ + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&server_conn->out, &read_byte, 1)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_DISABLED_FOR_TEST); + + /* write to conn->in buffer and assert error */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server_conn->in, &write_byte, 1)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); + /* drain conn->in buffer and assert base case */ + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&server_conn->in, &read_byte, 1)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), S2N_ERR_KTLS_DISABLED_FOR_TEST); + }; + + /* managed_send_io */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + int fd = 1; + EXPECT_OK(s2n_test_configure_mock_ktls_connection(server_conn, fd, true)); + + /* base case */ + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_DISABLED_FOR_TEST); + + server_conn->ktls_send_enabled = false; /* reset ktls enable connection */ + /* expect failure if connection is using custom IO */ + server_conn->managed_send_io = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_MANAGED_IO); + + server_conn->ktls_send_enabled = false; /* reset ktls enable connection */ + /* expect success if connection is NOT using custom IO */ + server_conn->managed_send_io = true; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_DISABLED_FOR_TEST); + }; + + /* managed_recv_io */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + int fd = 1; + EXPECT_OK(s2n_test_configure_mock_ktls_connection(server_conn, fd, true)); + + /* base case */ + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), S2N_ERR_KTLS_DISABLED_FOR_TEST); + + server_conn->ktls_recv_enabled = false; /* reset ktls enable connection */ + /* recv managed io */ + server_conn->managed_recv_io = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), S2N_ERR_KTLS_MANAGED_IO); + + server_conn->ktls_recv_enabled = false; /* reset ktls enable connection */ + /* expect success if connection is NOT using custom IO */ + server_conn->managed_recv_io = true; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), S2N_ERR_KTLS_DISABLED_FOR_TEST); + }; + + /* s2n_ktls_retrieve_file_descriptor */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + int write_fd_orig = 1; + int read_fd_orig = 2; + int fd_ret = 0; + + EXPECT_SUCCESS(s2n_connection_set_write_fd(server_conn, write_fd_orig)); + EXPECT_OK(s2n_ktls_retrieve_file_descriptor(server_conn, S2N_KTLS_MODE_SEND, &fd_ret)); + EXPECT_EQUAL(write_fd_orig, fd_ret); + + EXPECT_SUCCESS(s2n_connection_set_read_fd(server_conn, read_fd_orig)); + EXPECT_OK(s2n_ktls_retrieve_file_descriptor(server_conn, S2N_KTLS_MODE_RECV, &fd_ret)); + EXPECT_EQUAL(read_fd_orig, fd_ret); + }; + } + END_TEST(); } diff --git a/tests/unit/s2n_shutdown_test.c b/tests/unit/s2n_shutdown_test.c index e0f1e09c2a3..91efa34020a 100644 --- a/tests/unit/s2n_shutdown_test.c +++ b/tests/unit/s2n_shutdown_test.c @@ -21,15 +21,6 @@ #define ALERT_LEN (sizeof(uint16_t)) -static S2N_RESULT s2n_skip_handshake(struct s2n_connection *conn) -{ - conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; - while (!s2n_handshake_is_complete(conn)) { - conn->handshake.message_number++; - } - return S2N_RESULT_OK; -} - int main(int argc, char **argv) { BEGIN_TEST(); diff --git a/tls/s2n_ktls.c b/tls/s2n_ktls.c index 2900337a83f..6a77eda2b3a 100644 --- a/tls/s2n_ktls.c +++ b/tls/s2n_ktls.c @@ -15,11 +15,176 @@ #include "tls/s2n_ktls.h" +#include + +#include "utils/s2n_socket.h" + +/* These variables are used to disable ktls mechanisms during testing. */ +static bool disable_ktls_socket_config_for_testing = false; + bool s2n_ktls_is_supported_on_platform() { -#if defined(__linux__) +#if S2N_KTLS_SUPPORTED return true; #else return false; #endif } + +S2N_RESULT s2n_ktls_validate(struct s2n_connection *conn, s2n_ktls_mode ktls_mode) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(conn->secure); + RESULT_ENSURE_REF(conn->secure->cipher_suite); + RESULT_ENSURE_REF(conn->secure->cipher_suite->record_alg); + const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; + RESULT_ENSURE_REF(cipher); + + /* kTLS enable should only be called once the handshake has completed. */ + if (!is_handshake_complete(conn)) { + RESULT_BAIL(S2N_ERR_KTLS_HANDSHAKE_NOT_COMPLETE); + } + + /* TODO support TLS 1.3 + * + * TLS 1.3 support requires sending the KeyUpdate message when the cryptographic + * KeyLimits are met. However, this is currently only possible by applying a + * kernel patch to support this functionality. + */ + RESULT_ENSURE(conn->actual_protocol_version == S2N_TLS12, S2N_ERR_KTLS_UNSUPPORTED_CONN); + + /* Check if the cipher supports kTLS */ + RESULT_ENSURE(cipher->ktls_supported, S2N_ERR_KTLS_UNSUPPORTED_CONN); + + /* kTLS I/O functionality is managed by s2n-tls. kTLS cannot be enabled if the + * application sets custom I/O (managed_send_io == false means application has + * set custom I/O). + */ + switch (ktls_mode) { + case S2N_KTLS_MODE_SEND: + RESULT_ENSURE_REF(conn->send_io_context); + RESULT_ENSURE(conn->managed_send_io, S2N_ERR_KTLS_MANAGED_IO); + + /* The output stuffer should be empty before enabling kTLS. */ + RESULT_ENSURE(s2n_stuffer_data_available(&conn->out) == 0, S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); + + break; + case S2N_KTLS_MODE_RECV: + RESULT_ENSURE_REF(conn->recv_io_context); + RESULT_ENSURE(conn->managed_recv_io, S2N_ERR_KTLS_MANAGED_IO); + + /* The input stuffer should be empty before enabling kTLS. */ + RESULT_ENSURE(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); + + break; + } + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_ktls_retrieve_file_descriptor(struct s2n_connection *conn, s2n_ktls_mode ktls_mode, int *fd) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(fd); + + if (ktls_mode == S2N_KTLS_MODE_RECV) { + /* retrieve the receive fd */ + RESULT_ENSURE_REF(conn->recv_io_context); + const struct s2n_socket_read_io_context *peer_socket_ctx = conn->recv_io_context; + *fd = peer_socket_ctx->fd; + } else if (ktls_mode == S2N_KTLS_MODE_SEND) { + /* retrieve the send fd */ + RESULT_ENSURE_REF(conn->send_io_context); + const struct s2n_socket_write_io_context *peer_socket_ctx = conn->send_io_context; + *fd = peer_socket_ctx->fd; + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_ktls_configure_socket(struct s2n_connection *conn, s2n_ktls_mode ktls_mode) +{ + RESULT_ENSURE_REF(conn); + + /* If already enabled then return success */ + if (ktls_mode == S2N_KTLS_MODE_SEND && conn->ktls_send_enabled) { + RESULT_BAIL(S2N_ERR_KTLS_ALREADY_ENABLED); + } + if (ktls_mode == S2N_KTLS_MODE_RECV && conn->ktls_recv_enabled) { + RESULT_BAIL(S2N_ERR_KTLS_ALREADY_ENABLED); + } + + int fd = 0; + RESULT_GUARD(s2n_ktls_retrieve_file_descriptor(conn, ktls_mode, &fd)); + + /* Calls to setsockopt require a real socket, which is not used in unit tests. */ + RESULT_ENSURE(!disable_ktls_socket_config_for_testing, S2N_ERR_KTLS_DISABLED_FOR_TEST); + + /* Enable 'tls' ULP for the socket. https://lwn.net/Articles/730207 */ + int ret = setsockopt(fd, S2N_SOL_TCP, S2N_TCP_ULP, S2N_TLS_ULP_NAME, S2N_TLS_ULP_NAME_SIZE); + + if (ret != 0) { + /* EEXIST: https://man7.org/linux/man-pages/man3/errno.3.html + * + * TCP_ULP has already been enabled on the socket so the operation is a noop. + * Since its possible to call this twice, once for TX and once for RX, consider + * the noop a success. */ + if (errno != EEXIST) { + RESULT_BAIL(S2N_ERR_KTLS_ULP); + } + } + + return S2N_RESULT_OK; +} + +/* + * Since kTLS is an optimization, it is possible to continue operation + * by using userspace TLS if kTLS is not supported. Upon successfully + * enabling kTLS, we set connection->ktls_send_enabled (and recv) to true. + * + * kTLS configuration errors are recoverable since calls to setsockopt are + * non-destructive and its possible to fallback to userspace. + */ +int s2n_connection_ktls_enable_send(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + if (!s2n_ktls_is_supported_on_platform()) { + POSIX_BAIL(S2N_ERR_KTLS_UNSUPPORTED_PLATFORM); + } + + POSIX_GUARD_RESULT(s2n_ktls_validate(conn, S2N_KTLS_MODE_SEND)); + + POSIX_GUARD_RESULT(s2n_ktls_configure_socket(conn, S2N_KTLS_MODE_SEND)); + + return S2N_SUCCESS; +} + +int s2n_connection_ktls_enable_recv(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + + if (!s2n_ktls_is_supported_on_platform()) { + POSIX_BAIL(S2N_ERR_KTLS_UNSUPPORTED_PLATFORM); + } + + POSIX_GUARD_RESULT(s2n_ktls_validate(conn, S2N_KTLS_MODE_RECV)); + + POSIX_GUARD_RESULT(s2n_ktls_configure_socket(conn, S2N_KTLS_MODE_RECV)); + + return S2N_SUCCESS; +} + +/* Use for testing only. + * + * This function disables the setsockopt call to enable ULP. Calls to setsockopt + * require a real socket, which is not used in unit tests. + */ +S2N_RESULT s2n_disable_ktls_socket_config_for_testing(void) +{ + RESULT_ENSURE(s2n_in_unit_test(), S2N_ERR_NOT_IN_UNIT_TEST); + + disable_ktls_socket_config_for_testing = true; + + return S2N_RESULT_OK; +} diff --git a/tls/s2n_ktls.h b/tls/s2n_ktls.h index 5e2de31955f..ba2151a7e50 100644 --- a/tls/s2n_ktls.h +++ b/tls/s2n_ktls.h @@ -17,19 +17,30 @@ #include "tls/s2n_config.h" +/* Define headers needed to enable and use kTLS. + * * + * * The inline header definitions are required to compile kTLS specific code. + * * kTLS has been tested on linux. For all other platforms, kTLS is marked as + * * unsuppored, and will return an un-supported error. + * */ +#if defined(__linux__) + #define S2N_KTLS_SUPPORTED true + #include "tls/s2n_ktls_linux.h" +#else + #define S2N_KTLS_SUPPORTED false + #include "tls/s2n_ktls_unsupported.h" +#endif + /* A set of kTLS configurations representing the combination of sending * and receiving. */ typedef enum { - /* Disable kTLS. */ - S2N_KTLS_MODE_DISABLED, /* Enable kTLS for the send socket. */ S2N_KTLS_MODE_SEND, /* Enable kTLS for the receive socket. */ S2N_KTLS_MODE_RECV, - /* Enable kTLS for both receive and send sockets. */ - S2N_KTLS_MODE_DUPLEX, } s2n_ktls_mode; -int s2n_config_set_ktls_mode(struct s2n_config *config, s2n_ktls_mode ktls_mode); bool s2n_ktls_is_supported_on_platform(); +int s2n_connection_ktls_enable_send(struct s2n_connection *conn); +int s2n_connection_ktls_enable_recv(struct s2n_connection *conn); diff --git a/tls/s2n_ktls_linux.h b/tls/s2n_ktls_linux.h new file mode 100644 index 00000000000..cd1268db347 --- /dev/null +++ b/tls/s2n_ktls_linux.h @@ -0,0 +1,31 @@ +/* + * 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. + */ + +#pragma once + +/* + * Linux doesn't expose kTLS headers in its uapi. Its possible to get these headers + * via glibc but support can vary depending on the version of glibc on the host. + * Instead we define linux specific values inline. + * + * - https://elixir.bootlin.com/linux/v6.3.8/A/ident/TCP_ULP + * - https://elixir.bootlin.com/linux/v6.3.8/A/ident/SOL_TCP + */ + +/* socket definitions */ +#define S2N_TLS_ULP_NAME "tls" +#define S2N_TLS_ULP_NAME_SIZE sizeof(S2N_TLS_ULP_NAME) +#define S2N_TCP_ULP 31 /* Attach a ULP to a TCP connection. */ +#define S2N_SOL_TCP 6 /* TCP level */ diff --git a/tls/s2n_ktls_unsupported.h b/tls/s2n_ktls_unsupported.h new file mode 100644 index 00000000000..5e30931e4e6 --- /dev/null +++ b/tls/s2n_ktls_unsupported.h @@ -0,0 +1,26 @@ +/* + * 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. + */ + +#pragma once + +/* + * For unsupported platforms 0-init all values. + */ + +/* socket definitions */ +#define S2N_TLS_ULP_NAME "tls" +#define S2N_TLS_ULP_NAME_SIZE sizeof(S2N_TLS_ULP_NAME) +#define S2N_TCP_ULP 0 +#define S2N_SOL_TCP 0