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 : 1259255 : { 56 [ + + ][ + + ]: 1259258 : return (conn && conn->quic_enabled) || (conn && conn->config && conn->config->quic_enabled); [ + - ][ + + ] [ + + ] 57 : 1259255 : } 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 : 45 : { 106 [ + + ][ + - ]: 45 : POSIX_ENSURE_REF(conn); 107 : : 108 : 44 : *blocked = S2N_BLOCKED_ON_READ; 109 : : 110 : 44 : 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 [ + + ]: 44 : 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 [ + + ][ + - ]: 23 : POSIX_ENSURE(message_type == TLS_SERVER_NEW_SESSION_TICKET, S2N_ERR_UNSUPPORTED_WITH_QUIC); 122 [ - + ]: 22 : POSIX_GUARD_RESULT(s2n_post_handshake_process(conn, &conn->in, message_type)); 123 : : 124 : 22 : *blocked = S2N_NOT_BLOCKED; 125 : : 126 : 22 : return S2N_SUCCESS; 127 : 22 : } 128 : : 129 : : /* When using QUIC, S2N reads unencrypted handshake messages instead of encrypted records. 130 : : * This method sets up the S2N input buffers to match the results of using s2n_read_full_record. 131 : : */ 132 : : S2N_RESULT s2n_quic_read_handshake_message(struct s2n_connection *conn, uint8_t *message_type) 133 : 245 : { 134 [ + + ][ + - ]: 245 : RESULT_ENSURE_REF(conn); 135 : : /* The use of handshake.io here would complicate recv_buffering, and there's 136 : : * no real use case for recv_buffering when QUIC is handling the IO. 137 : : */ 138 [ # # ][ - + ]: 244 : RESULT_ENSURE(!conn->recv_buffering, S2N_ERR_INVALID_STATE); 139 : : 140 : : /* Allocate stuffer space now so that we don't have to realloc later in the handshake. */ 141 [ + + ]: 244 : RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_EXPECTED_QUIC_MESSAGE_SIZE)); 142 : : 143 [ + + ]: 243 : RESULT_GUARD(s2n_read_in_bytes(conn, &conn->handshake.io, TLS_HANDSHAKE_HEADER_LENGTH)); 144 : : 145 : 209 : uint32_t message_len = 0; 146 [ - + ]: 209 : RESULT_GUARD(s2n_handshake_parse_header(&conn->handshake.io, message_type, &message_len)); 147 [ - + ]: 209 : RESULT_GUARD_POSIX(s2n_stuffer_reread(&conn->handshake.io)); 148 : : 149 [ + - ][ + + ]: 209 : RESULT_ENSURE(message_len < S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH, S2N_ERR_BAD_MESSAGE); 150 [ + + ]: 206 : RESULT_GUARD(s2n_read_in_bytes(conn, &conn->buffer_in, message_len)); 151 : : 152 : : /* Although we call s2n_read_in_bytes, recv_greedy is always disabled for quic. 153 : : * Therefore buffer_in will always contain exactly message_len bytes of data. 154 : : * So we don't need to handle the possibility of extra data in buffer_in. 155 : : */ 156 [ - + ][ # # ]: 183 : RESULT_ENSURE_EQ(s2n_stuffer_data_available(&conn->buffer_in), message_len); 157 [ - + ]: 183 : RESULT_GUARD(s2n_recv_in_init(conn, message_len, message_len)); 158 : 183 : return S2N_RESULT_OK; 159 : 183 : } 160 : : 161 : : /* When using QUIC, S2N writes unencrypted handshake messages instead of encrypted records. 162 : : * This method sets up the S2N output buffer to match the result of using s2n_record_write. 163 : : */ 164 : : S2N_RESULT s2n_quic_write_handshake_message(struct s2n_connection *conn) 165 : 52 : { 166 [ + - ][ + + ]: 52 : RESULT_ENSURE_REF(conn); 167 : : 168 : : /* Allocate stuffer space now so that we don't have to realloc later in the handshake. */ 169 [ - + ]: 51 : RESULT_GUARD_POSIX(s2n_stuffer_resize_if_empty(&conn->out, S2N_EXPECTED_QUIC_MESSAGE_SIZE)); 170 : : 171 [ - + ]: 51 : RESULT_GUARD_POSIX(s2n_stuffer_copy(&conn->handshake.io, &conn->out, 172 : 51 : s2n_stuffer_data_available(&conn->handshake.io))); 173 : 51 : return S2N_RESULT_OK; 174 : 51 : }