in tls/s2n_handshake_io.c [1394:1551]
static int s2n_handshake_read_io(struct s2n_connection *conn)
{
uint8_t record_type = 0;
uint8_t message_type = 0;
int isSSLv2 = 0;
/* Fill conn->in stuffer necessary for the handshake.
* If using TCP, read a record. If using QUIC, read a message. */
if (s2n_connection_is_quic_enabled(conn)) {
record_type = TLS_HANDSHAKE;
POSIX_GUARD_RESULT(s2n_quic_read_handshake_message(conn, &message_type));
} else {
int r = s2n_read_full_record(conn, &record_type, &isSSLv2);
/**
*= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.10
*# If the client attempts a 0-RTT handshake but the server
*# rejects it, the server will generally not have the 0-RTT record
*# protection keys and must instead use trial decryption (either with
*# the 1-RTT handshake keys or by looking for a cleartext ClientHello in
*# the case of a HelloRetryRequest) to find the first non-0-RTT message.
*#
*# If the server chooses to accept the "early_data" extension, then it
*# MUST comply with the same error-handling requirements specified for
*# all records when processing early data records. Specifically, if the
*# server fails to decrypt a 0-RTT record following an accepted
*# "early_data" extension, it MUST terminate the connection with a
*# "bad_record_mac" alert as per Section 5.2.
*/
if ((r < S2N_SUCCESS) && (s2n_errno == S2N_ERR_EARLY_DATA_TRIAL_DECRYPT)) {
POSIX_GUARD(s2n_stuffer_reread(&conn->in));
POSIX_GUARD_RESULT(s2n_early_data_record_bytes(conn, s2n_stuffer_data_available(&conn->in)));
POSIX_GUARD_RESULT(s2n_record_wipe(conn));
return S2N_SUCCESS;
}
POSIX_GUARD(r);
}
if (isSSLv2) {
S2N_ERROR_IF(record_type != SSLv2_CLIENT_HELLO, S2N_ERR_BAD_MESSAGE);
POSIX_GUARD(s2n_handshake_handle_sslv2(conn));
}
/* Now we have a record, but it could be a partial fragment of a message, or it might
* contain several messages.
*/
if (record_type == TLS_APPLICATION_DATA) {
POSIX_GUARD_RESULT(s2n_handshake_app_data_recv(conn));
} else if (record_type == TLS_CHANGE_CIPHER_SPEC) {
/* TLS1.3 can receive unexpected CCS messages at any point in the handshake
* due to a peer operating in middlebox compatibility mode.
* However, when operating in QUIC mode, S2N should not accept ANY CCS messages,
* including these unexpected ones.*/
if (!IS_TLS13_HANDSHAKE(conn) || s2n_connection_is_quic_enabled(conn)) {
POSIX_ENSURE(EXPECTED_RECORD_TYPE(conn) == TLS_CHANGE_CIPHER_SPEC, S2N_ERR_BAD_MESSAGE);
POSIX_ENSURE(!CONNECTION_IS_WRITER(conn), S2N_ERR_BAD_MESSAGE);
}
S2N_ERROR_IF(s2n_stuffer_data_available(&conn->in) != 1, S2N_ERR_BAD_MESSAGE);
POSIX_GUARD(s2n_stuffer_copy(&conn->in, &conn->handshake.io, s2n_stuffer_data_available(&conn->in)));
POSIX_GUARD(CCS_STATE(conn).handler[conn->mode](conn));
POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io));
/* We're done with the record, wipe it */
POSIX_GUARD_RESULT(s2n_record_wipe(conn));
/* Advance the state machine if this was an expected message */
if (EXPECTED_RECORD_TYPE(conn) == TLS_CHANGE_CIPHER_SPEC && !CONNECTION_IS_WRITER(conn)) {
POSIX_GUARD(s2n_advance_message(conn));
}
return S2N_SUCCESS;
} else if (record_type != TLS_HANDSHAKE) {
if (record_type == TLS_ALERT) {
POSIX_GUARD(s2n_process_alert_fragment(conn));
}
/* Ignore record types that we don't support */
/* We're done with the record, wipe it */
POSIX_GUARD_RESULT(s2n_record_wipe(conn));
return S2N_SUCCESS;
}
/* Record is a handshake message */
S2N_ERROR_IF(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_BAD_MESSAGE);
while (s2n_stuffer_data_available(&conn->in)) {
/* We're done with negotiating but we have trailing data in this record. Bail on the handshake. */
S2N_ERROR_IF(EXPECTED_RECORD_TYPE(conn) == TLS_APPLICATION_DATA, S2N_ERR_BAD_MESSAGE);
int r = 0;
POSIX_GUARD((r = s2n_read_full_handshake_message(conn, &message_type)));
/* Do we need more data? This happens for message fragmentation */
if (r == 1) {
/* Break out of this inner loop, but since we're not changing the state, the
* outer loop in s2n_handshake_io() will read another record.
*/
POSIX_GUARD_RESULT(s2n_record_wipe(conn));
return S2N_SUCCESS;
}
if (conn->mode == S2N_CLIENT) {
s2n_cert_auth_type client_cert_auth_type = { 0 };
POSIX_GUARD(s2n_connection_get_client_auth_type(conn, &client_cert_auth_type));
/* If client auth is optional, we initially assume it will not be requested.
* If we received a request, switch to a client auth handshake.
*/
if (client_cert_auth_type != S2N_CERT_AUTH_REQUIRED && message_type == TLS_CERT_REQ) {
POSIX_ENSURE(client_cert_auth_type == S2N_CERT_AUTH_OPTIONAL, S2N_ERR_UNEXPECTED_CERT_REQUEST);
POSIX_ENSURE(IS_FULL_HANDSHAKE(conn), S2N_ERR_HANDSHAKE_STATE);
POSIX_GUARD_RESULT(s2n_handshake_type_set_flag(conn, CLIENT_AUTH));
}
/* According to rfc6066 section 8, the server may choose not to send a "CertificateStatus"
* message even if it has sent a "status_request" extension in the ServerHello message.
*/
if (EXPECTED_MESSAGE_TYPE(conn) == TLS_SERVER_CERT_STATUS
&& message_type != TLS_SERVER_CERT_STATUS) {
POSIX_GUARD_RESULT(s2n_handshake_type_unset_tls12_flag(conn, OCSP_STATUS));
}
}
/*
*= https://www.rfc-editor.org/rfc/rfc5246#section-7.4
*# The one message that is not bound by these ordering rules
*# is the HelloRequest message, which can be sent at any time, but which
*# SHOULD be ignored by the client if it arrives in the middle of a handshake.
*/
if (message_type == TLS_HELLO_REQUEST) {
POSIX_GUARD_RESULT(s2n_client_hello_request_validate(conn));
POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io));
continue;
}
/* Check for missing Certificate Requests to surface a more specific error */
if (EXPECTED_MESSAGE_TYPE(conn) == TLS_CERT_REQ) {
POSIX_ENSURE(message_type == TLS_CERT_REQ,
S2N_ERR_MISSING_CERT_REQUEST);
}
POSIX_ENSURE(record_type == EXPECTED_RECORD_TYPE(conn), S2N_ERR_BAD_MESSAGE);
POSIX_ENSURE(message_type == EXPECTED_MESSAGE_TYPE(conn), S2N_ERR_BAD_MESSAGE);
POSIX_ENSURE(!CONNECTION_IS_WRITER(conn), S2N_ERR_BAD_MESSAGE);
/* Call the relevant handler */
WITH_ERROR_BLINDING(conn, POSIX_GUARD(ACTIVE_STATE(conn).handler[conn->mode](conn)));
/* Advance the state machine */
POSIX_GUARD_RESULT(s2n_finish_read(conn));
}
/* We're done with the record, wipe it */
POSIX_GUARD_RESULT(s2n_record_wipe(conn));
return S2N_SUCCESS;
}