static int s2n_handshake_read_io()

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