1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
/*
* 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 "tls/s2n_quic_support.h"
#include "tls/s2n_connection.h"
#include "tls/s2n_tls.h"
#include "tls/s2n_tls13.h"
#include "utils/s2n_mem.h"
#include "utils/s2n_safety.h"
/* When reading and writing records with TCP, S2N sets its input and output buffers
* to the maximum record fragment size to prevent resizing those buffers later.
*
* However, because S2N with QUIC reads and writes messages instead of records,
* the "maximum size" for the input and output buffers would be the maximum message size: 64k.
* Since most messages are MUCH smaller than that (<3k), setting the buffer that large is wasteful.
*
* Instead, we intentionally choose a smaller size and accept that an abnormally large message
* could cause the buffer to resize. */
#define S2N_EXPECTED_QUIC_MESSAGE_SIZE S2N_DEFAULT_FRAGMENT_LENGTH
S2N_RESULT s2n_read_in_bytes(struct s2n_connection *conn, struct s2n_stuffer *output, uint32_t length);
int s2n_config_enable_quic(struct s2n_config *config)
{
POSIX_ENSURE_REF(config);
config->quic_enabled = true;
return S2N_SUCCESS;
}
int s2n_connection_enable_quic(struct s2n_connection *conn)
{
POSIX_ENSURE_REF(conn);
POSIX_GUARD_RESULT(s2n_connection_validate_tls13_support(conn));
conn->quic_enabled = true;
return S2N_SUCCESS;
}
bool s2n_connection_is_quic_enabled(struct s2n_connection *conn)
{
return (conn && conn->quic_enabled) || (conn && conn->config && conn->config->quic_enabled);
}
bool s2n_connection_are_session_tickets_enabled(struct s2n_connection *conn)
{
return conn && conn->config && conn->config->use_tickets;
}
int s2n_connection_set_quic_transport_parameters(struct s2n_connection *conn,
const uint8_t *data_buffer, uint16_t data_len)
{
POSIX_ENSURE_REF(conn);
POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters));
POSIX_GUARD(s2n_alloc(&conn->our_quic_transport_parameters, data_len));
POSIX_CHECKED_MEMCPY(conn->our_quic_transport_parameters.data, data_buffer, data_len);
return S2N_SUCCESS;
}
int s2n_connection_get_quic_transport_parameters(struct s2n_connection *conn,
const uint8_t **data_buffer, uint16_t *data_len)
{
POSIX_ENSURE_REF(conn);
POSIX_ENSURE_REF(data_buffer);
POSIX_ENSURE_REF(data_len);
*data_buffer = conn->peer_quic_transport_parameters.data;
*data_len = conn->peer_quic_transport_parameters.size;
return S2N_SUCCESS;
}
int s2n_connection_set_secret_callback(struct s2n_connection *conn, s2n_secret_cb cb_func, void *ctx)
{
POSIX_ENSURE_REF(conn);
POSIX_ENSURE_REF(cb_func);
conn->secret_cb = cb_func;
conn->secret_cb_context = ctx;
return S2N_SUCCESS;
}
/* Currently we need an API that quic can call to process post-handshake messages. Ideally
* we could re-use the s2n_recv API but that function needs to be refactored to support quic.
* For now we just call this API.
*/
int s2n_recv_quic_post_handshake_message(struct s2n_connection *conn, s2n_blocked_status *blocked)
{
POSIX_ENSURE_REF(conn);
*blocked = S2N_BLOCKED_ON_READ;
uint8_t message_type = 0;
/* This function uses the stuffer conn->handshake.io to read in the header. This stuffer is also used
* for sending post-handshake messages. This could cause a concurrency issue if we start both sending
* and receiving post-handshake messages while quic is enabled. Currently there's no post-handshake
* message that is both sent and received in quic (servers only send session tickets
* and clients only receive session tickets.) Therefore it is safe for us
* to use the stuffer here.
*/
POSIX_GUARD_RESULT(s2n_quic_read_handshake_message(conn, &message_type));
/* The only post-handshake messages we support from QUIC currently are session tickets */
POSIX_ENSURE(message_type == TLS_SERVER_NEW_SESSION_TICKET, S2N_ERR_UNSUPPORTED_WITH_QUIC);
POSIX_GUARD_RESULT(s2n_post_handshake_process(conn, &conn->in, message_type));
*blocked = S2N_NOT_BLOCKED;
return S2N_SUCCESS;
}
/* When using QUIC, S2N reads unencrypted handshake messages instead of encrypted records.
* This method sets up the S2N input buffers to match the results of using s2n_read_full_record.
*/
S2N_RESULT s2n_quic_read_handshake_message(struct s2n_connection *conn, uint8_t *message_type)
{
RESULT_ENSURE_REF(conn);
/* Allocate stuffer space now so that we don't have to realloc later in the handshake. */
RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->in, S2N_EXPECTED_QUIC_MESSAGE_SIZE));
RESULT_GUARD(s2n_read_in_bytes(conn, &conn->handshake.io, TLS_HANDSHAKE_HEADER_LENGTH));
uint32_t message_len;
RESULT_GUARD(s2n_handshake_parse_header(&conn->handshake.io, message_type, &message_len));
RESULT_GUARD_POSIX(s2n_stuffer_reread(&conn->handshake.io));
RESULT_ENSURE(message_len < S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE);
RESULT_GUARD(s2n_read_in_bytes(conn, &conn->in, message_len));
return S2N_RESULT_OK;
}
/* When using QUIC, S2N writes unencrypted handshake messages instead of encrypted records.
* This method sets up the S2N output buffer to match the result of using s2n_record_write.
*/
S2N_RESULT s2n_quic_write_handshake_message(struct s2n_connection *conn)
{
RESULT_ENSURE_REF(conn);
/* Allocate stuffer space now so that we don't have to realloc later in the handshake. */
RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->out, S2N_EXPECTED_QUIC_MESSAGE_SIZE));
RESULT_GUARD_POSIX(s2n_stuffer_copy(&conn->handshake.io, &conn->out,
s2n_stuffer_data_available(&conn->handshake.io)));
return S2N_RESULT_OK;
}
|