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 <errno.h>
17 : : #include <sys/param.h>
18 : :
19 : : #include "api/s2n.h"
20 : : #include "crypto/s2n_cipher.h"
21 : : #include "error/s2n_errno.h"
22 : : #include "stuffer/s2n_stuffer.h"
23 : : #include "tls/s2n_alerts.h"
24 : : #include "tls/s2n_cipher_suites.h"
25 : : #include "tls/s2n_connection.h"
26 : : #include "tls/s2n_handshake.h"
27 : : #include "tls/s2n_internal.h"
28 : : #include "tls/s2n_ktls.h"
29 : : #include "tls/s2n_post_handshake.h"
30 : : #include "tls/s2n_record.h"
31 : : #include "utils/s2n_blob.h"
32 : : #include "utils/s2n_io.h"
33 : : #include "utils/s2n_safety.h"
34 : :
35 : : /*
36 : : * Determine whether there is currently sufficient space in the send buffer to construct
37 : : * another record, or if we need to flush now.
38 : : *
39 : : * We only buffer multiple records when sending application data, NOT when
40 : : * sending handshake messages or alerts. If the next record is a post-handshake message
41 : : * or an alert, then the send buffer will be flushed regardless of the result of this method.
42 : : * Therefore we don't need to consider the size of any potential KeyUpdate messages,
43 : : * NewSessionTicket messages, or Alerts.
44 : : */
45 : : bool s2n_should_flush(struct s2n_connection *conn, ssize_t total_message_size)
46 : 146010 : {
47 : : /* Always flush if not buffering multiple records. */
48 [ + + ]: 146010 : if (!conn->multirecord_send) {
49 : 145979 : return true;
50 : 145979 : }
51 : :
52 : : /* Flush if all data has been sent. */
53 : 31 : ssize_t remaining_payload_size = total_message_size - conn->current_user_data_consumed;
54 [ + + ]: 31 : if (remaining_payload_size <= 0) {
55 : 10 : return true;
56 : 10 : }
57 : :
58 : 21 : uint16_t max_payload_size = 0;
59 [ - + ]: 21 : if (!s2n_result_is_ok(s2n_record_max_write_payload_size(conn, &max_payload_size))) {
60 : : /* When in doubt, flush */
61 : 0 : return true;
62 : 0 : }
63 : 21 : max_payload_size = MIN(max_payload_size, remaining_payload_size);
64 : :
65 : 21 : uint16_t max_write_size = 0;
66 [ - + ]: 21 : if (!s2n_result_is_ok(s2n_record_max_write_size(conn, max_payload_size, &max_write_size))) {
67 : : /* When in doubt, flush */
68 : 0 : return true;
69 : 0 : }
70 : :
71 : : /* Flush if the stuffer can't store the max possible record size without growing.
72 : : *
73 : : * However, the stuffer is allocated when the record is sent, so if the stuffer
74 : : * hasn't been allocated, assume it will have enough space.
75 : : */
76 : 21 : uint32_t available_space = s2n_stuffer_space_remaining(&conn->out);
77 [ + + ][ + + ]: 21 : if (available_space < max_write_size && !s2n_stuffer_is_freed(&conn->out)) {
78 : 5 : return true;
79 : 5 : }
80 : :
81 : 16 : return false;
82 : 21 : }
83 : :
84 : : int s2n_flush(struct s2n_connection *conn, s2n_blocked_status *blocked)
85 : 783123 : {
86 [ # # ][ - + ]: 783123 : POSIX_ENSURE_REF(conn);
87 [ # # ][ - + ]: 783123 : POSIX_ENSURE_REF(blocked);
88 : 783123 : *blocked = S2N_BLOCKED_ON_WRITE;
89 : :
90 : : /* Write any data that's already pending */
91 [ + + ]: 1036602 : while (s2n_stuffer_data_available(&conn->out)) {
92 : 485468 : errno = 0;
93 : 485468 : int w = s2n_connection_send_stuffer(&conn->out, conn, s2n_stuffer_data_available(&conn->out));
94 [ + + ]: 485468 : POSIX_GUARD_RESULT(s2n_io_check_write_result(w));
95 : 253479 : conn->wire_bytes_out += w;
96 : 253479 : }
97 [ - + ]: 551134 : POSIX_GUARD(s2n_stuffer_rewrite(&conn->out));
98 : :
99 [ + + ]: 551134 : if (conn->reader_warning_out) {
100 [ - + ]: 4 : POSIX_GUARD_RESULT(s2n_alerts_write_warning(conn));
101 : 4 : conn->reader_warning_out = 0;
102 [ - + ]: 4 : POSIX_GUARD(s2n_flush(conn, blocked));
103 : 4 : }
104 : :
105 : 551134 : *blocked = S2N_NOT_BLOCKED;
106 : 551134 : return 0;
107 : 551134 : }
108 : :
109 : : S2N_RESULT s2n_sendv_with_offset_total_size(const struct iovec *bufs, ssize_t count,
110 : : ssize_t offs, ssize_t *total_size_out)
111 : 72974 : {
112 [ + - ][ + + ]: 72974 : RESULT_ENSURE_REF(total_size_out);
113 [ + + ]: 72973 : if (count > 0) {
114 [ + + ][ + - ]: 72966 : RESULT_ENSURE_REF(bufs);
115 : 72966 : }
116 : :
117 : 72970 : size_t total_size = 0;
118 [ + + ]: 2014754 : for (ssize_t i = 0; i < count; i++) {
119 : 1941785 : size_t iov_len = bufs[i].iov_len;
120 : : /* Account for any offset */
121 [ + + ]: 1941785 : if (offs > 0) {
122 : 1764153 : size_t offs_consumed = MIN((size_t) offs, iov_len);
123 : 1764153 : iov_len -= offs_consumed;
124 : 1764153 : offs -= offs_consumed;
125 : 1764153 : }
126 [ + - ][ + + ]: 1941785 : RESULT_ENSURE(S2N_ADD_IS_OVERFLOW_SAFE(total_size, iov_len, SIZE_MAX),
[ + - ]
127 : 1941784 : S2N_ERR_INVALID_ARGUMENT);
128 : 1941784 : total_size += iov_len;
129 : 1941784 : }
130 : :
131 : : /* We must have fully accounted for the offset, or else the offset is larger
132 : : * than the available data and our inputs are invalid.
133 : : */
134 [ + - ][ + + ]: 72969 : RESULT_ENSURE(offs == 0, S2N_ERR_INVALID_ARGUMENT);
135 : :
136 [ + - ][ + + ]: 72961 : RESULT_ENSURE(total_size <= SSIZE_MAX, S2N_ERR_INVALID_ARGUMENT);
137 : 72960 : *total_size_out = total_size;
138 : 72960 : return S2N_RESULT_OK;
139 : 72961 : }
140 : :
141 : : ssize_t s2n_sendv_with_offset_impl(struct s2n_connection *conn, const struct iovec *bufs,
142 : : ssize_t count, ssize_t offs, s2n_blocked_status *blocked)
143 : 219263 : {
144 : 219263 : ssize_t user_data_sent = 0, total_size = 0;
145 : :
146 [ + + ][ + - ]: 219263 : POSIX_ENSURE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE), S2N_ERR_CLOSED);
147 [ + + ][ + - ]: 217735 : POSIX_ENSURE(!s2n_connection_is_quic_enabled(conn), S2N_ERR_UNSUPPORTED_WITH_QUIC);
148 : :
149 : : /* Flush any pending I/O */
150 [ + + ]: 217732 : POSIX_GUARD(s2n_flush(conn, blocked));
151 : :
152 [ + + ]: 23784 : if (conn->ktls_send_enabled) {
153 : 50 : return s2n_ktls_sendv_with_offset(conn, bufs, count, offs, blocked);
154 : 50 : }
155 : :
156 : : /* Acknowledge consumed and flushed user data as sent */
157 : 23734 : user_data_sent = conn->current_user_data_consumed;
158 : :
159 : 23734 : *blocked = S2N_BLOCKED_ON_WRITE;
160 : :
161 : 23734 : uint16_t max_payload_size = 0;
162 [ - + ]: 23734 : POSIX_GUARD_RESULT(s2n_record_max_write_payload_size(conn, &max_payload_size));
163 : :
164 : : /* TLS 1.0 and SSLv3 are vulnerable to the so-called Beast attack. Work
165 : : * around this by splitting messages into one byte records, and then
166 : : * the remainder can follow as usual.
167 : : */
168 : 23734 : int cbcHackUsed = 0;
169 : :
170 : 23734 : struct s2n_crypto_parameters *writer = conn->server;
171 [ + + ]: 23734 : if (conn->mode == S2N_CLIENT) {
172 : 16402 : writer = conn->client;
173 : 16402 : }
174 : :
175 [ - + ]: 23734 : POSIX_GUARD_RESULT(s2n_sendv_with_offset_total_size(bufs, count, offs, &total_size));
176 : : /* Defensive check against an invalid retry */
177 [ + + ][ + - ]: 23734 : POSIX_ENSURE(conn->current_user_data_consumed <= total_size, S2N_ERR_SEND_SIZE);
178 [ + + ]: 23733 : POSIX_GUARD_RESULT(s2n_early_data_validate_send(conn, total_size));
179 : :
180 [ + + ]: 23726 : if (conn->dynamic_record_timeout_threshold > 0) {
181 : 3284 : uint64_t elapsed = 0;
182 [ - + ]: 3284 : POSIX_GUARD_RESULT(s2n_timer_elapsed(conn->config, &conn->write_timer, &elapsed));
183 : : /* Reset record size back to a single segment after threshold seconds of inactivity */
184 [ + + ]: 3284 : if (elapsed - conn->last_write_elapsed > (uint64_t) conn->dynamic_record_timeout_threshold * 1000000000) {
185 : 10 : conn->active_application_bytes_consumed = 0;
186 : 10 : }
187 : 3284 : conn->last_write_elapsed = elapsed;
188 : 3284 : }
189 : :
190 : : /* Now write the data we were asked to send this round */
191 [ + + ]: 169178 : while (total_size - conn->current_user_data_consumed) {
192 : 147232 : ssize_t to_write = MIN(total_size - conn->current_user_data_consumed, max_payload_size);
193 : :
194 : : /* If dynamic record size is enabled,
195 : : * use small TLS records that fit into a single TCP segment for the threshold bytes of data
196 : : */
197 [ + + ]: 147232 : if (conn->active_application_bytes_consumed < (uint64_t) conn->dynamic_record_resize_threshold) {
198 : 317 : uint16_t min_payload_size = 0;
199 [ - + ]: 317 : POSIX_GUARD_RESULT(s2n_record_min_write_payload_size(conn, &min_payload_size));
200 : 317 : to_write = MIN(min_payload_size, to_write);
201 : 317 : }
202 : :
203 : : /* Don't split messages in server mode for interoperability with naive clients.
204 : : * Some clients may have expectations based on the amount of content in the first record.
205 : : */
206 [ + + ]: 147232 : if (conn->actual_protocol_version < S2N_TLS11
207 [ + + ][ + + ]: 147232 : && writer->cipher_suite->record_alg->cipher->type == S2N_CBC && conn->mode != S2N_SERVER) {
208 [ + - ][ + + ]: 22 : if (to_write > 1 && cbcHackUsed == 0) {
209 : 11 : to_write = 1;
210 : 11 : cbcHackUsed = 1;
211 : 11 : }
212 : 22 : }
213 : :
214 [ + + ]: 147232 : POSIX_GUARD(s2n_post_handshake_send(conn, blocked));
215 : :
216 : : /* Write and encrypt the record */
217 : 146001 : int written_to_record = s2n_record_writev(conn, TLS_APPLICATION_DATA, bufs, count,
218 : 146001 : conn->current_user_data_consumed + offs, to_write);
219 [ - + ]: 146001 : POSIX_GUARD(written_to_record);
220 : 146001 : conn->current_user_data_consumed += written_to_record;
221 : 146001 : conn->active_application_bytes_consumed += written_to_record;
222 : :
223 : : /* Send it, unless we're waiting for more records */
224 [ + + ]: 146001 : if (s2n_should_flush(conn, total_size)) {
225 [ + + ]: 145991 : if (s2n_flush(conn, blocked) < 0) {
226 [ + + ][ + + ]: 549 : if (s2n_errno == S2N_ERR_IO_BLOCKED && user_data_sent > 0) {
227 : : /* We successfully sent >0 user bytes on the wire, but not the full requested payload
228 : : * because we became blocked on I/O. Acknowledge the data sent. */
229 : :
230 : 505 : conn->current_user_data_consumed -= user_data_sent;
231 : 505 : return user_data_sent;
232 : 505 : } else {
233 : 44 : S2N_ERROR_PRESERVE_ERRNO();
234 : 44 : }
235 : 549 : }
236 : :
237 : : /* Acknowledge consumed and flushed user data as sent */
238 : 145442 : user_data_sent = conn->current_user_data_consumed;
239 : 145442 : }
240 : 146001 : }
241 : :
242 : : /* If everything has been written, then there's no user data pending */
243 : 21946 : conn->current_user_data_consumed = 0;
244 : :
245 : 21946 : *blocked = S2N_NOT_BLOCKED;
246 : 21946 : return total_size;
247 : 23726 : }
248 : :
249 : : ssize_t s2n_sendv_with_offset(struct s2n_connection *conn, const struct iovec *bufs, ssize_t count,
250 : : ssize_t offs, s2n_blocked_status *blocked)
251 : 219265 : {
252 [ + + ][ + - ]: 219265 : POSIX_ENSURE(!conn->send_in_use, S2N_ERR_REENTRANCY);
253 : 219263 : conn->send_in_use = true;
254 : :
255 : 219263 : ssize_t result = s2n_sendv_with_offset_impl(conn, bufs, count, offs, blocked);
256 [ - + ]: 219263 : POSIX_GUARD_RESULT(s2n_early_data_record_bytes(conn, result));
257 : :
258 [ - + ]: 219263 : POSIX_GUARD_RESULT(s2n_connection_dynamic_free_out_buffer(conn));
259 : :
260 : 219263 : conn->send_in_use = false;
261 : 219263 : return result;
262 : 219263 : }
263 : :
264 : : ssize_t s2n_sendv(struct s2n_connection *conn, const struct iovec *bufs, ssize_t count, s2n_blocked_status *blocked)
265 : 15 : {
266 : 15 : return s2n_sendv_with_offset(conn, bufs, count, 0, blocked);
267 : 15 : }
268 : :
269 : : ssize_t s2n_send(struct s2n_connection *conn, const void *buf, ssize_t size, s2n_blocked_status *blocked)
270 : 180122 : {
271 : 180122 : struct iovec iov;
272 : 180122 : iov.iov_base = (void *) (uintptr_t) buf;
273 : 180122 : iov.iov_len = size;
274 : 180122 : return s2n_sendv_with_offset(conn, &iov, 1, 0, blocked);
275 : 180122 : }
|