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