Branch data Line data Source code
1 : : /* 2 : : * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 : : * 4 : : * Licensed under the Apache License, Version 2.0 (the "License"). 5 : : * You may not use this file except in compliance with the License. 6 : : * A copy of the License is located at 7 : : * 8 : : * http://aws.amazon.com/apache2.0 9 : : * 10 : : * or in the "license" file accompanying this file. This file is distributed 11 : : * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 12 : : * express or implied. See the License for the specific language governing 13 : : * permissions and limitations under the License. 14 : : */ 15 : : 16 : : #include "tls/s2n_quic_support.h" 17 : : 18 : : #include "tls/s2n_connection.h" 19 : : #include "tls/s2n_tls.h" 20 : : #include "tls/s2n_tls13.h" 21 : : #include "utils/s2n_mem.h" 22 : : #include "utils/s2n_safety.h" 23 : : 24 : : /* When reading and writing records with TCP, S2N sets its input and output buffers 25 : : * to the maximum record fragment size to prevent resizing those buffers later. 26 : : * 27 : : * However, because S2N with QUIC reads and writes messages instead of records, 28 : : * the "maximum size" for the input and output buffers would be the maximum message size: 64k. 29 : : * Since most messages are MUCH smaller than that (<3k), setting the buffer that large is wasteful. 30 : : * 31 : : * Instead, we intentionally choose a smaller size and accept that an abnormally large message 32 : : * could cause the buffer to resize. */ 33 : : #define S2N_EXPECTED_QUIC_MESSAGE_SIZE S2N_DEFAULT_FRAGMENT_LENGTH 34 : : 35 : : S2N_RESULT s2n_read_in_bytes(struct s2n_connection *conn, struct s2n_stuffer *output, uint32_t length); 36 : : 37 : : int s2n_config_enable_quic(struct s2n_config *config) 38 : 21 : { 39 [ + + ][ + - ]: 21 : POSIX_ENSURE_REF(config); 40 : 20 : config->quic_enabled = true; 41 : 20 : return S2N_SUCCESS; 42 : 21 : } 43 : : 44 : : int s2n_connection_enable_quic(struct s2n_connection *conn) 45 : 50 : { 46 [ + - ][ + + ]: 50 : POSIX_ENSURE_REF(conn); 47 [ - + ]: 49 : POSIX_GUARD_RESULT(s2n_connection_validate_tls13_support(conn)); 48 : : /* QUIC support is not currently compatible with recv_buffering */ 49 [ - + ][ # # ]: 49 : POSIX_ENSURE(!conn->recv_buffering, S2N_ERR_INVALID_STATE); 50 : 49 : conn->quic_enabled = true; 51 : 49 : return S2N_SUCCESS; 52 : 49 : } 53 : : 54 : : bool s2n_connection_is_quic_enabled(struct s2n_connection *conn) 55 : 1284081 : { 56 [ + - ][ + + ]: 1284081 : return (conn && conn->quic_enabled) || (conn && conn->config && conn->config->quic_enabled); [ + + ][ + + ] [ + + ] 57 : 1284081 : } 58 : : 59 : : bool s2n_connection_are_session_tickets_enabled(struct s2n_connection *conn) 60 : 0 : { 61 [ # # ][ # # ]: 0 : return conn && conn->config && conn->config->use_tickets; [ # # ] 62 : 0 : } 63 : : 64 : : int s2n_connection_set_quic_transport_parameters(struct s2n_connection *conn, 65 : : const uint8_t *data_buffer, uint16_t data_len) 66 : 9 : { 67 [ + - ][ + + ]: 9 : POSIX_ENSURE_REF(conn); 68 : : 69 [ - + ]: 8 : POSIX_GUARD(s2n_free(&conn->our_quic_transport_parameters)); 70 [ - + ]: 8 : POSIX_GUARD(s2n_alloc(&conn->our_quic_transport_parameters, data_len)); 71 [ + + ][ + - ]: 8 : POSIX_CHECKED_MEMCPY(conn->our_quic_transport_parameters.data, data_buffer, data_len); [ + + ] 72 : : 73 : 7 : return S2N_SUCCESS; 74 : 8 : } 75 : : 76 : : int s2n_connection_get_quic_transport_parameters(struct s2n_connection *conn, 77 : : const uint8_t **data_buffer, uint16_t *data_len) 78 : 7 : { 79 [ + + ][ + - ]: 7 : POSIX_ENSURE_REF(conn); 80 [ + + ][ + - ]: 6 : POSIX_ENSURE_REF(data_buffer); 81 [ + - ][ + + ]: 5 : POSIX_ENSURE_REF(data_len); 82 : : 83 : 4 : *data_buffer = conn->peer_quic_transport_parameters.data; 84 : 4 : *data_len = conn->peer_quic_transport_parameters.size; 85 : : 86 : 4 : return S2N_SUCCESS; 87 : 5 : } 88 : : 89 : : int s2n_connection_set_secret_callback(struct s2n_connection *conn, s2n_secret_cb cb_func, void *ctx) 90 : 312 : { 91 [ + + ][ + - ]: 312 : POSIX_ENSURE_REF(conn); 92 [ + + ][ + - ]: 311 : POSIX_ENSURE_REF(cb_func); 93 : : 94 : 310 : conn->secret_cb = cb_func; 95 : 310 : conn->secret_cb_context = ctx; 96 : : 97 : 310 : return S2N_SUCCESS; 98 : 311 : } 99 : : 100 : : /* Currently we need an API that quic can call to process post-handshake messages. Ideally 101 : : * we could re-use the s2n_recv API but that function needs to be refactored to support quic. 102 : : * For now we just call this API. 103 : : */ 104 : : int s2n_recv_quic_post_handshake_message(struct s2n_connection *conn, s2n_blocked_status *blocked) 105 : 53 : { 106 [ + - ][ + + ]: 53 : POSIX_ENSURE_REF(conn); 107 : : 108 : 52 : *blocked = S2N_BLOCKED_ON_READ; 109 : : 110 : 52 : uint8_t message_type = 0; 111 : : /* This function uses the stuffer conn->handshake.io to read in the header. This stuffer is also used 112 : : * for sending post-handshake messages. This could cause a concurrency issue if we start both sending 113 : : * and receiving post-handshake messages while quic is enabled. Currently there's no post-handshake 114 : : * message that is both sent and received in quic (servers only send session tickets 115 : : * and clients only receive session tickets.) Therefore it is safe for us 116 : : * to use the stuffer here. 117 : : */ 118 [ + + ]: 52 : POSIX_GUARD_RESULT(s2n_quic_read_handshake_message(conn, &message_type)); 119 : : 120 : : /* The only post-handshake messages we support from QUIC currently are session tickets */ 121 [ + - ][ + + ]: 31 : POSIX_ENSURE(message_type == TLS_SERVER_NEW_SESSION_TICKET, S2N_ERR_UNSUPPORTED_WITH_QUIC); 122 [ - + ]: 30 : POSIX_GUARD_RESULT(s2n_post_handshake_process(conn, &conn->in, message_type)); 123 : : 124 : : /* Successfully read the message, wipe the header and message buffer */ 125 [ - + ]: 30 : POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); 126 [ - + ]: 30 : POSIX_GUARD_RESULT(s2n_record_wipe(conn)); 127 : : 128 : 30 : *blocked = S2N_NOT_BLOCKED; 129 : : 130 : 30 : return S2N_SUCCESS; 131 : 30 : } 132 : : 133 : : /* When using QUIC, S2N reads unencrypted handshake messages instead of encrypted records. 134 : : * This method sets up the S2N input buffers to match the results of using s2n_read_full_record. 135 : : */ 136 : : S2N_RESULT s2n_quic_read_handshake_message(struct s2n_connection *conn, uint8_t *message_type) 137 : 253 : { 138 [ + + ][ + - ]: 253 : RESULT_ENSURE_REF(conn); 139 : : /* The use of handshake.io here would complicate recv_buffering, and there's 140 : : * no real use case for recv_buffering when QUIC is handling the IO. 141 : : */ 142 [ # # ][ - + ]: 252 : RESULT_ENSURE(!conn->recv_buffering, S2N_ERR_INVALID_STATE); 143 : : 144 : : /* Allocate stuffer space now so that we don't have to realloc later in the handshake. */ 145 [ + + ]: 252 : RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_EXPECTED_QUIC_MESSAGE_SIZE)); 146 : : 147 [ + + ]: 251 : RESULT_GUARD(s2n_read_in_bytes(conn, &conn->handshake.io, TLS_HANDSHAKE_HEADER_LENGTH)); 148 : : 149 : 217 : uint32_t message_len = 0; 150 [ - + ]: 217 : RESULT_GUARD(s2n_handshake_parse_header(&conn->handshake.io, message_type, &message_len)); 151 : : /* Reset the read cursor rather than wiping the stuffer. During the 152 : : * handshake, s2n_read_full_handshake_message expects the header to still 153 : : * be present in handshake.io so it can accumulate the full message. 154 : : */ 155 [ - + ]: 217 : RESULT_GUARD_POSIX(s2n_stuffer_reread(&conn->handshake.io)); 156 : : 157 [ + - ][ + + ]: 217 : RESULT_ENSURE(message_len < S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE); 158 [ + + ]: 214 : RESULT_GUARD(s2n_read_in_bytes(conn, &conn->buffer_in, message_len)); 159 : : 160 : : /* Although we call s2n_read_in_bytes, recv_greedy is always disabled for quic. 161 : : * Therefore buffer_in will always contain exactly message_len bytes of data. 162 : : * So we don't need to handle the possibility of extra data in buffer_in. 163 : : */ 164 [ - + ][ # # ]: 191 : RESULT_ENSURE_EQ(s2n_stuffer_data_available(&conn->buffer_in), message_len); 165 [ - + ]: 191 : RESULT_GUARD(s2n_recv_in_init(conn, message_len, message_len)); 166 : 191 : return S2N_RESULT_OK; 167 : 191 : } 168 : : 169 : : /* When using QUIC, S2N writes unencrypted handshake messages instead of encrypted records. 170 : : * This method sets up the S2N output buffer to match the result of using s2n_record_write. 171 : : */ 172 : : S2N_RESULT s2n_quic_write_handshake_message(struct s2n_connection *conn) 173 : 52 : { 174 [ + - ][ + + ]: 52 : RESULT_ENSURE_REF(conn); 175 : : 176 : : /* Allocate stuffer space now so that we don't have to realloc later in the handshake. */ 177 [ - + ]: 51 : RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->out, S2N_EXPECTED_QUIC_MESSAGE_SIZE)); 178 : : 179 [ - + ]: 51 : RESULT_GUARD_POSIX(s2n_stuffer_copy(&conn->handshake.io, &conn->out, 180 : 51 : s2n_stuffer_data_available(&conn->handshake.io))); 181 : 51 : return S2N_RESULT_OK; 182 : 51 : }