/* * 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 "s2n_test.h" #include "testlib/s2n_testlib.h" #include "tls/s2n_alerts.h" #include "tls/s2n_tls.h" #define ERROR_ALERTS_COUNT (UINT16_MAX) #define END_OF_DATA 0 #define MODE_COUNT 2 int s2n_test_ch_cb(struct s2n_connection *conn, void *context) { return S2N_FAILURE; } typedef enum { S2N_TEST_DURING_HANDSHAKE, S2N_TEST_AFTER_HANDSHAKE, S2N_TEST_TYPE_COUNT } s2n_test_type; int main(int argc, char **argv) { BEGIN_TEST(); if (!s2n_is_tls13_fully_supported()) { return 0; } uint8_t alert_header[] = { /* type */ TLS_ALERT, /* legacy_record_version */ 0x03, 0x03, /* length */ 0x00, 0x02 }; uint8_t close_notify[] = { 1, S2N_TLS_ALERT_CLOSE_NOTIFY }; uint8_t data[] = "hello"; /** *= https://tools.ietf.org/rfc/rfc8446#section-6 *= type=test *# Unknown Alert types MUST be treated as error alerts. */ uint8_t test_alert_levels[] = { 0, 1, 2, 3, 10, UINT8_MAX }; /** *= https://tools.ietf.org/rfc/rfc8446#section-6 *= type=test *# All the alerts listed in Section 6.2 MUST be sent with *# AlertLevel=fatal and MUST be treated as error alerts when received *# regardless of the AlertLevel in the message. */ uint8_t error_alerts[ERROR_ALERTS_COUNT][2] = { 0 }; size_t error_alerts_count = 0; for (size_t level_i = 0; level_i < s2n_array_len(test_alert_levels); level_i++) { for (size_t alert_code = 0; alert_code <= UINT8_MAX; alert_code++) { /* Skip closure alerts */ if (alert_code == S2N_TLS_ALERT_CLOSE_NOTIFY) { continue; } if (alert_code == S2N_TLS_ALERT_USER_CANCELED) { continue; } /* To speed up the test, let's exclude a chunk of the unassigned values. */ if (alert_code > 120 && alert_code < 250) { continue; } EXPECT_TRUE(error_alerts_count < ERROR_ALERTS_COUNT); error_alerts[error_alerts_count][0] = test_alert_levels[level_i]; error_alerts[error_alerts_count][1] = alert_code; error_alerts_count++; } } DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); DEFER_CLEANUP(struct s2n_config *ecdsa_config = s2n_config_new(), s2n_config_ptr_free); EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(ecdsa_config)); EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(ecdsa_config, ecdsa_chain_and_key)); /* Test: Receiving an error alert */ for (size_t i = 0; i < error_alerts_count; i++) { for (s2n_test_type type = 0; type < S2N_TEST_TYPE_COUNT; type++) { for (uint8_t mode = 0; mode < MODE_COUNT; mode++) { DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); EXPECT_SUCCESS(s2n_connection_set_config(server, config)); DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); EXPECT_SUCCESS(s2n_connection_set_config(client, config)); DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); if (type == S2N_TEST_DURING_HANDSHAKE) { /* Partially perform handshake */ EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_CERT)); } else { /* Complete handshake */ EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); } EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); struct s2n_connection *receiver = server; struct s2n_connection *sender = client; if (mode == S2N_CLIENT) { receiver = client; sender = server; } /* Send alert */ struct s2n_blob alert = { 0 }; s2n_blocked_status blocked = S2N_NOT_BLOCKED; EXPECT_SUCCESS(s2n_blob_init(&alert, error_alerts[i], sizeof(error_alerts[0]))); EXPECT_OK(s2n_record_write(sender, TLS_ALERT, &alert)); EXPECT_SUCCESS(s2n_flush(sender, &blocked)); /** *= https://tools.ietf.org/rfc/rfc8446#section-6 *= type=test *# Upon receiving an error alert, the TLS implementation *# SHOULD indicate an error to the application and MUST NOT allow any *# further data to be sent or received on the connection. */ if (type == S2N_TEST_DURING_HANDSHAKE) { EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(receiver, &blocked), S2N_ERR_ALERT); EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(receiver, &blocked), S2N_ERR_CLOSED); } else { EXPECT_FAILURE_WITH_ERRNO(s2n_recv(receiver, data, sizeof(data), &blocked), S2N_ERR_ALERT); EXPECT_FAILURE_WITH_ERRNO(s2n_recv(receiver, data, sizeof(data), &blocked), S2N_ERR_CLOSED); EXPECT_FAILURE_WITH_ERRNO(s2n_send(receiver, data, sizeof(data), &blocked), S2N_ERR_CLOSED); } /** *= https://tools.ietf.org/rfc/rfc8446#section-6.2 *= type=test *# Upon transmission or *# receipt of a fatal alert message, both parties MUST immediately close *# the connection. */ EXPECT_TRUE(s2n_connection_check_io_status(receiver, S2N_IO_CLOSED)); /** *= https://tools.ietf.org/rfc/rfc8446#section-6.2 *= type=test *# The implementation SHOULD provide a way to facilitate logging the sending *# and receiving of alerts. */ EXPECT_EQUAL(error_alerts[i][1], s2n_connection_get_alert(receiver)); } } }; /* Test: Sending an error alert */ { /* Testing a variety of alerts is more difficult sending than receiving. * We can't trigger every possible fatal error, so just choose some common ones. */ int test_errors[] = { /* handshake errors without blinding */ S2N_ERR_CANCELLED, S2N_ERR_CIPHER_NOT_SUPPORTED, /* handshake errors with blinding */ S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED, S2N_ERR_CERT_UNTRUSTED, /* application data error */ S2N_ERR_DECRYPT, }; DEFER_CLEANUP(struct s2n_config *bad_cb_config = s2n_config_new(), s2n_config_ptr_free); EXPECT_SUCCESS(s2n_config_set_client_hello_cb(bad_cb_config, s2n_test_ch_cb, NULL)); DEFER_CLEANUP(struct s2n_config *untrusted_config = s2n_config_new(), s2n_config_ptr_free); for (size_t i = 0; i < s2n_array_len(test_errors); i++) { DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); EXPECT_SUCCESS(s2n_connection_set_config(server, config)); DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); EXPECT_SUCCESS(s2n_connection_set_config(client, config)); DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); s2n_blocked_status blocked = S2N_NOT_BLOCKED; uint8_t expected_alert = S2N_TLS_ALERT_CLOSE_NOTIFY; struct s2n_connection *failed_conn = server; struct s2n_connection *closed_conn = client; switch (test_errors[i]) { case S2N_ERR_CANCELLED: /* Error triggered by callback failure during handshake */ EXPECT_SUCCESS(s2n_connection_set_config(server, bad_cb_config)); EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server, client), S2N_ERR_CANCELLED); expected_alert = S2N_TLS_ALERT_HANDSHAKE_FAILURE; break; case S2N_ERR_CIPHER_NOT_SUPPORTED: /* Error triggered if no valid cipher suites. * Use a security policy that only supports RSA certs for the client, * and only set an EC cert for the server. */ EXPECT_SUCCESS(s2n_connection_set_config(server, ecdsa_config)); EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client, "20170210")); EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server, client), S2N_ERR_CIPHER_NOT_SUPPORTED); break; case S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED: /* Remove TLS1.3 support before the ClientHello by setting * a security policy that doesn't support TLS1.3, then add * TLS1.3 support back after the ClientHello. */ EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client, "20170210")); EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(client, &blocked), S2N_ERR_IO_BLOCKED); EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server, &blocked), S2N_ERR_IO_BLOCKED); client->client_protocol_version = S2N_TLS13; EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server, client), S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); failed_conn = client; closed_conn = server; break; case S2N_ERR_CERT_UNTRUSTED: EXPECT_SUCCESS(s2n_connection_set_config(client, untrusted_config)); EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server, client), S2N_ERR_CERT_UNTRUSTED); failed_conn = client; closed_conn = server; break; case S2N_ERR_DECRYPT: EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); /* The server will expect the original sequence number, * not our updated sequence number, so fail to decrypt. */ client->secure->client_sequence_number[0] = 0xFF; EXPECT_EQUAL(s2n_send(client, data, sizeof(data), &blocked), sizeof(data)); EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server, data, sizeof(data), &blocked), S2N_ERR_DECRYPT); break; default: FAIL_MSG("Fatal error not implemented for test"); } /* Remove any blinding so that we can immediately call shutdown */ failed_conn->delay = 0; /* The alert will not be sent until we attempt to shutdown */ EXPECT_SUCCESS(s2n_shutdown_send(failed_conn, &blocked)); EXPECT_TRUE(failed_conn->alert_sent); /** *= https://tools.ietf.org/rfc/rfc8446#section-6.2 *= type=test *# Upon transmission or *# receipt of a fatal alert message, both parties MUST immediately close *# the connection. */ EXPECT_TRUE(s2n_connection_check_io_status(failed_conn, S2N_IO_CLOSED)); /** *= https://tools.ietf.org/rfc/rfc8446#section-6.2 *= type=test *# Whenever an implementation encounters a fatal error condition, it *# SHOULD send an appropriate fatal alert */ if (expected_alert == S2N_TLS_ALERT_CLOSE_NOTIFY) { EXPECT_EQUAL(s2n_recv(closed_conn, data, sizeof(data), &blocked), END_OF_DATA); EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); } else { EXPECT_FAILURE_WITH_ERRNO(s2n_recv(closed_conn, data, sizeof(data), &blocked), S2N_ERR_ALERT); } EXPECT_EQUAL(expected_alert, s2n_connection_get_alert(closed_conn)); /** *= https://tools.ietf.org/rfc/rfc8446#section-6.2 *= type=test *# and MUST close the connection *# without sending or receiving any additional data. */ EXPECT_FAILURE_WITH_ERRNO(s2n_send(failed_conn, data, sizeof(data), &blocked), S2N_ERR_CLOSED); EXPECT_FAILURE_WITH_ERRNO(s2n_recv(failed_conn, data, sizeof(data), &blocked), S2N_ERR_CLOSED); } }; /* Test: Receiving a closure alert * *= https://tools.ietf.org/rfc/rfc8446#section-6.1 *= type=test *# Either party MAY initiate a close of its write side of the connection *# by sending a "close_notify" alert. Any data received after a closure *# alert has been received MUST be ignored. */ for (s2n_test_type type = 0; type < S2N_TEST_TYPE_COUNT; type++) { for (uint8_t mode = 0; mode < MODE_COUNT; mode++) { DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); EXPECT_SUCCESS(s2n_connection_set_config(server, config)); DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); EXPECT_SUCCESS(s2n_connection_set_config(client, config)); DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); if (type == S2N_TEST_DURING_HANDSHAKE) { /* Partially perform handshake */ EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_CERT)); } else { /* Complete handshake */ EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); } struct s2n_connection *receiver = server; struct s2n_stuffer *input = &io_pair.server_in; if (mode == S2N_CLIENT) { receiver = client; input = &io_pair.client_in; } /* Send close_notify */ EXPECT_SUCCESS(s2n_stuffer_write_bytes(input, alert_header, sizeof(alert_header))); EXPECT_SUCCESS(s2n_stuffer_write_bytes(input, close_notify, sizeof(close_notify))); /* Receive close_notify */ s2n_blocked_status blocked = S2N_NOT_BLOCKED; if (type == S2N_TEST_DURING_HANDSHAKE) { EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(receiver, &blocked), S2N_ERR_CLOSED); } else { EXPECT_EQUAL(s2n_recv(receiver, data, sizeof(data), &blocked), END_OF_DATA); EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); } EXPECT_FALSE(s2n_connection_check_io_status(receiver, S2N_IO_READABLE)); /* *= https://tools.ietf.org/rfc/rfc8446#section-6.1 *= type=test *# Any data received after a closure alert has been received MUST be ignored. */ EXPECT_SUCCESS(s2n_stuffer_write_bytes(input, data, sizeof(data))); if (type == S2N_TEST_DURING_HANDSHAKE) { EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(receiver, &blocked), S2N_ERR_CLOSED); } else { EXPECT_EQUAL(s2n_recv(receiver, data, sizeof(data), &blocked), END_OF_DATA); EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); } }; }; /* Test: Sending a closure alert * *= https://tools.ietf.org/rfc/rfc8446#section-6.1 *= type=test *# Each party MUST send a "close_notify" alert before closing its write *# side of the connection, unless it has already sent some error alert. *# This does not have any effect on its read side of the connection. */ for (uint8_t mode = 0; mode < MODE_COUNT; mode++) { DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); EXPECT_SUCCESS(s2n_connection_set_config(server, config)); DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); EXPECT_SUCCESS(s2n_connection_set_config(client, config)); DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); /* Perform handshake */ EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); struct s2n_connection *sender = server; struct s2n_connection *receiver = client; if (mode == S2N_CLIENT) { sender = client; receiver = server; } /* Send close_notify */ s2n_blocked_status blocked = S2N_NOT_BLOCKED; EXPECT_SUCCESS(s2n_shutdown_send(sender, &blocked)); EXPECT_FALSE(s2n_connection_check_io_status(sender, S2N_IO_WRITABLE)); /* Receive close_notify * *= https://tools.ietf.org/rfc/rfc8446#section-6 *= type=test *# The "close_notify" alert is used to indicate orderly closure of one *# direction of the connection. Upon receiving such an alert, the TLS *# implementation SHOULD indicate end-of-data to the application. */ EXPECT_EQUAL(s2n_recv(receiver, data, sizeof(data), &blocked), END_OF_DATA); EXPECT_FALSE(s2n_connection_check_io_status(receiver, S2N_IO_READABLE)); EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); /* Read side is NOT affected. * The receiver of the close_notify can continue to send, and the sender * should continue to read. */ EXPECT_EQUAL(s2n_send(receiver, data, sizeof(data), &blocked), sizeof(data)); EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); EXPECT_EQUAL(s2n_recv(sender, data, sizeof(data), &blocked), sizeof(data)); EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); /* Respond with close_notify */ EXPECT_SUCCESS(s2n_shutdown(receiver, &blocked)); EXPECT_SUCCESS(s2n_shutdown(sender, &blocked)); }; /* Test: Closure alerts in TLS1.2 * *= https://tools.ietf.org/rfc/rfc8446#section-6.1 *= type=test *# Note that this is a change from versions of TLS prior to TLS 1.3 in *# which implementations were required to react to a "close_notify" by *# discarding pending writes and sending an immediate "close_notify" *# alert of their own. */ for (uint8_t mode = 0; mode < MODE_COUNT; mode++) { DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); EXPECT_SUCCESS(s2n_connection_set_config(server, config)); EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server, "default")); DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); EXPECT_SUCCESS(s2n_connection_set_config(client, config)); EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client, "default")); DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); /* Perform handshake */ EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); struct s2n_connection *sender = server; struct s2n_connection *receiver = client; if (mode == S2N_CLIENT) { sender = client; receiver = server; } /* Send close_notify */ s2n_blocked_status blocked = S2N_NOT_BLOCKED; EXPECT_SUCCESS(s2n_shutdown_send(sender, &blocked)); EXPECT_TRUE(s2n_connection_check_io_status(sender, S2N_IO_CLOSED)); /* Receive close_notify */ EXPECT_EQUAL(s2n_recv(receiver, data, sizeof(data), &blocked), END_OF_DATA); EXPECT_TRUE(s2n_connection_check_io_status(receiver, S2N_IO_CLOSED)); EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); /* Read side is affected. * The receiver should discard writes and just send a close_notify. */ EXPECT_FAILURE_WITH_ERRNO(s2n_send(receiver, data, sizeof(data), &blocked), S2N_ERR_CLOSED); /* Respond with close_notify */ EXPECT_SUCCESS(s2n_shutdown(receiver, &blocked)); EXPECT_SUCCESS(s2n_shutdown(sender, &blocked)); }; /* Test: End-of-Data * *= https://tools.ietf.org/rfc/rfc8446#6.1 *= type=test *# If a transport-level close *# is received prior to a "close_notify", the receiver cannot know that *# all the data that was sent has been received. * *= https://tools.ietf.org/rfc/rfc8446#6.1 *= type=test *# If the application protocol using TLS provides that any data may be *# carried over the underlying transport after the TLS connection is *# closed, the TLS implementation MUST receive a "close_notify" alert *# before indicating end-of-data to the application layer. */ for (uint8_t mode = 0; mode < MODE_COUNT; mode++) { /* Test: Without partial read */ { DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); EXPECT_SUCCESS(s2n_connection_set_config(server, config)); DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); EXPECT_SUCCESS(s2n_connection_set_config(client, config)); DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); /* Perform handshake */ EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); /* Close one end of pipe */ EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, mode)); struct s2n_connection *receiver = client; if (mode == S2N_CLIENT) { receiver = server; } /* Subsequent reads should NOT report END_OF_DATA, but instead an error */ s2n_blocked_status blocked = S2N_NOT_BLOCKED; for (size_t i = 0; i < 5; i++) { EXPECT_FAILURE_WITH_ERRNO(s2n_recv(receiver, data, sizeof(data), &blocked), S2N_ERR_CLOSED); EXPECT_FALSE(s2n_connection_check_io_status(receiver, S2N_IO_READABLE)); } }; /* Test: With partial read */ { /* In order to trigger a partial read, we need to encounter end-of-data * when some data has already been successfully read. For that to be true, * we need to read multiple records (one successfully, then end-of-data). */ struct s2n_config partial_write_config_copy = *config; partial_write_config_copy.recv_multi_record = true; DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); EXPECT_SUCCESS(s2n_connection_set_config(server, &partial_write_config_copy)); DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); EXPECT_SUCCESS(s2n_connection_set_config(client, &partial_write_config_copy)); DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); /* Perform handshake */ EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); struct s2n_connection *sender = server; struct s2n_connection *receiver = client; if (mode == S2N_CLIENT) { sender = client; receiver = server; } /* Send some data */ const uint8_t partial_write = sizeof(data) / 2; s2n_blocked_status blocked = S2N_NOT_BLOCKED; EXPECT_EQUAL(s2n_send(sender, data, partial_write, &blocked), partial_write); EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); /* Close one end of pipe */ EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, mode)); /* First read should report a partial read, but also close the connection */ EXPECT_EQUAL(s2n_recv(receiver, data, sizeof(data), &blocked), partial_write); EXPECT_FALSE(s2n_connection_check_io_status(receiver, S2N_IO_READABLE)); EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); /* Subsequent reads should NOT report END_OF_DATA, but instead an error */ for (size_t i = 0; i < 5; i++) { EXPECT_FAILURE_WITH_ERRNO(s2n_recv(receiver, data, sizeof(data), &blocked), S2N_ERR_CLOSED); } }; }; END_TEST(); }