123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- /*
- * 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.
- */
- #include "tls/s2n_ktls.h"
- #include "crypto/s2n_ktls_crypto.h"
- #include "tls/s2n_prf.h"
- #include "tls/s2n_tls.h"
- /* Used for overriding setsockopt calls in testing */
- s2n_setsockopt_fn s2n_setsockopt = setsockopt;
- S2N_RESULT s2n_ktls_set_setsockopt_cb(s2n_setsockopt_fn cb)
- {
- RESULT_ENSURE(s2n_in_test(), S2N_ERR_NOT_IN_TEST);
- s2n_setsockopt = cb;
- return S2N_RESULT_OK;
- }
- bool s2n_ktls_is_supported_on_platform()
- {
- #if defined(S2N_KTLS_SUPPORTED)
- return true;
- #else
- return false;
- #endif
- }
- static int s2n_ktls_disabled_read(void *io_context, uint8_t *buf, uint32_t len)
- {
- POSIX_BAIL(S2N_ERR_IO);
- }
- static S2N_RESULT s2n_ktls_validate(struct s2n_connection *conn, s2n_ktls_mode ktls_mode)
- {
- RESULT_ENSURE_REF(conn);
- const struct s2n_config *config = conn->config;
- RESULT_ENSURE_REF(config);
- RESULT_ENSURE(s2n_ktls_is_supported_on_platform(), S2N_ERR_KTLS_UNSUPPORTED_PLATFORM);
- /* kTLS enable should only be called once the handshake has completed. */
- RESULT_ENSURE(is_handshake_complete(conn), S2N_ERR_HANDSHAKE_NOT_COMPLETE);
- /* TODO support TLS 1.3
- *
- * TLS 1.3 support requires sending the KeyUpdate message when the cryptographic
- * key usage limits 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 */
- const struct s2n_cipher *cipher = NULL;
- RESULT_GUARD(s2n_connection_get_secure_cipher(conn, &cipher));
- RESULT_ENSURE_REF(cipher);
- RESULT_ENSURE(cipher->set_ktls_info, S2N_ERR_KTLS_UNSUPPORTED_CONN);
- /* Renegotiation requires updating the keys, which kTLS doesn't currently support.
- *
- * Setting the renegotiation callback doesn't guarantee that a client will
- * attempt to renegotiate. The callback can also be used to send warning alerts
- * signaling that renegotiation was rejected. However, we can provide applications
- * with a clearer signal earlier by preventing them from enabling ktls on a
- * connection that MIGHT require renegotiation. We can relax this restriction
- * later if necessary.
- */
- bool may_receive_hello_request = s2n_result_is_ok(s2n_client_hello_request_validate(conn));
- bool may_renegotiate = may_receive_hello_request && config->renegotiate_request_cb;
- RESULT_ENSURE(!may_renegotiate, S2N_ERR_KTLS_RENEG);
- /* 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(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(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;
- default:
- RESULT_BAIL(S2N_ERR_SAFETY);
- break;
- }
- return S2N_RESULT_OK;
- }
- /* Enabling kTLS preserves the original *io_context; making this functions
- * safe to call even after kTLS has been enabled on the connection.
- *
- * Retrieving fd assumes that the connection is using socket IO and has the
- * send_io_context set. While kTLS overrides IO and essentially disables
- * the socket conn->send function callback, it doesn't modify the
- * send_io_context. */
- S2N_RESULT s2n_ktls_get_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) {
- RESULT_GUARD_POSIX(s2n_connection_get_read_fd(conn, fd));
- } else if (ktls_mode == S2N_KTLS_MODE_SEND) {
- RESULT_GUARD_POSIX(s2n_connection_get_write_fd(conn, fd));
- }
- return S2N_RESULT_OK;
- }
- static S2N_RESULT s2n_ktls_get_io_mode(s2n_ktls_mode ktls_mode, int *tls_tx_rx_mode)
- {
- RESULT_ENSURE_REF(tls_tx_rx_mode);
- if (ktls_mode == S2N_KTLS_MODE_SEND) {
- *tls_tx_rx_mode = S2N_TLS_TX;
- } else {
- *tls_tx_rx_mode = S2N_TLS_RX;
- }
- return S2N_RESULT_OK;
- }
- static S2N_RESULT s2n_ktls_crypto_info_init(struct s2n_connection *conn, s2n_ktls_mode ktls_mode,
- struct s2n_ktls_crypto_info *crypto_info)
- {
- RESULT_ENSURE_REF(conn);
- struct s2n_crypto_parameters *secure = conn->secure;
- RESULT_ENSURE_REF(secure);
- /* In order to avoid storing the encryption keys on the connection, we instead
- * regenerate them when required by ktls.
- *
- * s2n_key_material also includes an IV, but we should use the IV stored
- * on the connection instead. Some record algorithms (like CBC) mutate the
- * "implicit_iv" when writing records, so the IV may change after generation.
- */
- struct s2n_key_material key_material = { 0 };
- RESULT_GUARD(s2n_prf_generate_key_material(conn, &key_material));
- bool is_sending_key = (ktls_mode == S2N_KTLS_MODE_SEND);
- s2n_mode key_mode = (is_sending_key) ? conn->mode : S2N_PEER_MODE(conn->mode);
- struct s2n_ktls_crypto_info_inputs inputs = { 0 };
- if (key_mode == S2N_CLIENT) {
- inputs.key = key_material.client_key;
- RESULT_GUARD_POSIX(s2n_blob_init(&inputs.iv,
- secure->client_implicit_iv, sizeof(secure->client_implicit_iv)));
- RESULT_GUARD_POSIX(s2n_blob_init(&inputs.seq,
- secure->client_sequence_number, sizeof(secure->client_sequence_number)));
- } else {
- inputs.key = key_material.server_key;
- RESULT_GUARD_POSIX(s2n_blob_init(&inputs.iv,
- secure->server_implicit_iv, sizeof(secure->server_implicit_iv)));
- RESULT_GUARD_POSIX(s2n_blob_init(&inputs.seq,
- secure->server_sequence_number, sizeof(secure->server_sequence_number)));
- }
- const struct s2n_cipher *cipher = NULL;
- RESULT_GUARD(s2n_connection_get_secure_cipher(conn, &cipher));
- RESULT_ENSURE_REF(cipher);
- RESULT_ENSURE_REF(cipher->set_ktls_info);
- RESULT_GUARD(cipher->set_ktls_info(&inputs, crypto_info));
- return S2N_RESULT_OK;
- }
- /* This method intentionally returns void because it may NOT perform any fallible
- * operations. See s2n_connection_ktls_enable.
- */
- void s2n_ktls_configure_connection(struct s2n_connection *conn, s2n_ktls_mode ktls_mode)
- {
- if (conn == NULL) {
- return;
- }
- if (ktls_mode == S2N_KTLS_MODE_SEND) {
- conn->ktls_send_enabled = true;
- conn->send = s2n_ktls_send_cb;
- } else {
- conn->ktls_recv_enabled = true;
- conn->recv = s2n_ktls_disabled_read;
- }
- }
- static S2N_RESULT s2n_connection_ktls_enable(struct s2n_connection *conn, s2n_ktls_mode ktls_mode)
- {
- RESULT_ENSURE_REF(conn);
- RESULT_GUARD(s2n_ktls_validate(conn, ktls_mode));
- int fd = 0;
- RESULT_GUARD(s2n_ktls_get_file_descriptor(conn, ktls_mode, &fd));
- /* This call doesn't actually enable ktls or modify the IO behavior of the socket.
- * Instead, this is just a prerequisite for calling setsockopt with SOL_TLS.
- *
- * We intentionally ignore the result of this call. It may fail because ktls
- * is not supported, but it might also fail because ktls has already been enabled
- * for the socket. If SOL_TLS isn't enabled on the socket, our next call to
- * setsockopt with SOL_TLS will also fail, and we DO check that result.
- */
- s2n_setsockopt(fd, S2N_SOL_TCP, S2N_TCP_ULP, S2N_TLS_ULP_NAME, S2N_TLS_ULP_NAME_SIZE);
- int tls_tx_rx_mode = 0;
- RESULT_GUARD(s2n_ktls_get_io_mode(ktls_mode, &tls_tx_rx_mode));
- struct s2n_ktls_crypto_info crypto_info = { 0 };
- RESULT_GUARD(s2n_ktls_crypto_info_init(conn, ktls_mode, &crypto_info));
- /* If this call succeeds, then ktls is enabled for that io mode and will be offloaded */
- int ret = s2n_setsockopt(fd, S2N_SOL_TLS, tls_tx_rx_mode, crypto_info.value.data, crypto_info.value.size);
- RESULT_ENSURE(ret == 0, S2N_ERR_KTLS_ENABLE);
- /* At this point, ktls is enabled on the socket for the requested IO mode.
- * No further fallible operations may be performed, or else the caller may
- * incorrectly assume that enabling ktls failed and they should therefore
- * fall back to using application layer TLS.
- *
- * That means no calls to RESULT_ENSURE, RESULT_GUARD, etc. after this point.
- */
- s2n_ktls_configure_connection(conn, ktls_mode);
- return S2N_RESULT_OK;
- }
- int s2n_connection_ktls_enable_send(struct s2n_connection *conn)
- {
- POSIX_ENSURE_REF(conn);
- /* If already enabled then return success */
- if (conn->ktls_send_enabled) {
- return S2N_SUCCESS;
- }
- POSIX_GUARD_RESULT(s2n_connection_ktls_enable(conn, S2N_KTLS_MODE_SEND));
- return S2N_SUCCESS;
- }
- int s2n_connection_ktls_enable_recv(struct s2n_connection *conn)
- {
- POSIX_ENSURE_REF(conn);
- /* If already enabled then return success */
- if (conn->ktls_recv_enabled) {
- return S2N_SUCCESS;
- }
- POSIX_GUARD_RESULT(s2n_connection_ktls_enable(conn, S2N_KTLS_MODE_RECV));
- return S2N_SUCCESS;
- }
|