S2N_RESULT s2n_post_handshake_message_recv()

in tls/s2n_post_handshake.c [65:158]


S2N_RESULT s2n_post_handshake_message_recv(struct s2n_connection *conn)
{
    RESULT_ENSURE_REF(conn);

    struct s2n_stuffer *in = &conn->in;
    struct s2n_stuffer *message = &conn->post_handshake.in;
    uint8_t message_type = 0;
    uint32_t message_len = 0;

    /* We always start reading from the beginning of the message.
     * Reset the read progress, but keep the write progress since
     * there may already be a partial message stored in `message`.
     */
    RESULT_GUARD_POSIX(s2n_stuffer_reread(message));

    /* At minimum, the message stuffer needs to have enough space to read the header.
     * For small messages like KeyUpdate and HelloRequest, this is all the space we will need.
     */
    if (s2n_stuffer_is_freed(message)) {
        struct s2n_blob b = { 0 };
        RESULT_GUARD_POSIX(s2n_blob_init(&b, conn->post_handshake.header_in,
                sizeof(conn->post_handshake.header_in)));
        RESULT_GUARD_POSIX(s2n_stuffer_init(message, &b));
    }

    /* Try to copy the header into the message stuffer.
     * The message stuffer may already contain some or all of the header if
     * we have read fragments of this message from previous records.
     */
    if (s2n_stuffer_data_available(message) < TLS_HANDSHAKE_HEADER_LENGTH) {
        uint32_t remaining = TLS_HANDSHAKE_HEADER_LENGTH - s2n_stuffer_data_available(message);
        uint32_t to_read = MIN(remaining, s2n_stuffer_data_available(in));
        RESULT_GUARD_POSIX(s2n_stuffer_copy(in, message, to_read));
    }
    RESULT_ENSURE(s2n_stuffer_data_available(message) >= TLS_HANDSHAKE_HEADER_LENGTH, S2N_ERR_IO_BLOCKED);

    /* Parse the header */
    RESULT_GUARD(s2n_handshake_parse_header(message, &message_type, &message_len));
    RESULT_ENSURE(message_len == 0 || s2n_stuffer_data_available(in), S2N_ERR_IO_BLOCKED);
    RESULT_ENSURE(message_len <= S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE);

    /* If the message body is not fragmented, just process it directly from conn->in.
     * This will be the most common case, and does not require us to allocate any new memory.
     */
    if (s2n_stuffer_data_available(message) == 0 && s2n_stuffer_data_available(in) >= message_len) {
        struct s2n_stuffer full_message = { 0 };
        struct s2n_blob full_message_blob = { 0 };
        RESULT_GUARD_POSIX(s2n_blob_init(&full_message_blob, s2n_stuffer_raw_read(in, message_len), message_len));
        RESULT_GUARD_POSIX(s2n_stuffer_init(&full_message, &full_message_blob));
        RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&full_message, message_len));
        RESULT_GUARD(s2n_post_handshake_process(conn, &full_message, message_type));
        return S2N_RESULT_OK;
    }

    /* If the message body is fragmented, then the current fragment will be wiped from conn->in
     * in order to read the next record. So the message stuffer needs enough space to store
     * the full message as we reconstruct it from multiple records.
     * For large messages like NewSessionTicket, this will require allocating new memory.
     */
    if (s2n_stuffer_space_remaining(message) < message_len) {
        /* We want to avoid servers allocating memory in response to post-handshake messages
         * to avoid a potential DDOS / resource exhaustion attack.
         *
         * Currently, s2n-tls servers only support the KeyUpdate message,
         * which should never require additional memory to parse.
         */
        RESULT_ENSURE(conn->mode == S2N_CLIENT, S2N_ERR_BAD_MESSAGE);

        uint32_t total_size = message_len + TLS_HANDSHAKE_HEADER_LENGTH;
        if (message->alloced) {
            RESULT_GUARD_POSIX(s2n_stuffer_resize(message, total_size));
        } else {
            /* Manually convert our static stuffer to a growable stuffer */
            RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(message, total_size));
            RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(message, conn->post_handshake.header_in, TLS_HANDSHAKE_HEADER_LENGTH));
            RESULT_GUARD_POSIX(s2n_stuffer_skip_read(message, TLS_HANDSHAKE_HEADER_LENGTH));
        }
    }

    /* Try to copy the message body into the message stuffer.
     * The message stuffer may already contain some of the message body if
     * we have already read fragments from previous records.
     */
    if (s2n_stuffer_data_available(message) < message_len) {
        uint32_t remaining = message_len - s2n_stuffer_data_available(message);
        uint32_t to_read = MIN(remaining, s2n_stuffer_data_available(in));
        RESULT_GUARD_POSIX(s2n_stuffer_copy(in, message, to_read));
    }
    RESULT_ENSURE(s2n_stuffer_data_available(message) == message_len, S2N_ERR_IO_BLOCKED);

    /* Now that the full message body is available, process it. */
    RESULT_GUARD(s2n_post_handshake_process(conn, message, message_type));
    return S2N_RESULT_OK;
}