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 : : #if defined(__FreeBSD__) || defined(__APPLE__)
17 : : /* https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_socket.h.html
18 : : * The POSIX standard does not define the CMSG_LEN and CMSG_SPACE macros. FreeBSD
19 : : * and APPLE check and disable these macros if the _POSIX_C_SOURCE flag is set.
20 : : *
21 : : * Since s2n-tls already unsets the _POSIX_C_SOURCE in other files and is not
22 : : * POSIX compliant, we continue the pattern here.
23 : : */
24 : : #undef _POSIX_C_SOURCE
25 : : #endif
26 : : #include <sys/socket.h>
27 : :
28 : : #ifdef S2N_LINUX_SENDFILE
29 : : #include <sys/sendfile.h>
30 : : #endif
31 : :
32 : : #include "crypto/s2n_sequence.h"
33 : : #include "error/s2n_errno.h"
34 : : #include "tls/s2n_ktls.h"
35 : : #include "tls/s2n_tls.h"
36 : : #include "utils/s2n_io.h"
37 : : #include "utils/s2n_result.h"
38 : : #include "utils/s2n_safety.h"
39 : : #include "utils/s2n_socket.h"
40 : :
41 : : /* record_type is of type uint8_t */
42 : : #define S2N_KTLS_RECORD_TYPE_SIZE (sizeof(uint8_t))
43 : : #define S2N_KTLS_CONTROL_BUFFER_SIZE (CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE))
44 : :
45 : : #define S2N_MAX_STACK_IOVECS 16
46 : : #define S2N_MAX_STACK_IOVECS_MEM (S2N_MAX_STACK_IOVECS * sizeof(struct iovec))
47 : :
48 : : /* Used to override sendmsg and recvmsg for testing. */
49 : : static ssize_t s2n_ktls_default_sendmsg(void *io_context, const struct msghdr *msg);
50 : : static ssize_t s2n_ktls_default_recvmsg(void *io_context, struct msghdr *msg);
51 : : s2n_ktls_sendmsg_fn s2n_sendmsg_fn = s2n_ktls_default_sendmsg;
52 : : s2n_ktls_recvmsg_fn s2n_recvmsg_fn = s2n_ktls_default_recvmsg;
53 : :
54 : : S2N_RESULT s2n_ktls_set_sendmsg_cb(struct s2n_connection *conn, s2n_ktls_sendmsg_fn send_cb,
55 : : void *send_ctx)
56 : 82007 : {
57 [ # # ][ - + ]: 82007 : RESULT_ENSURE_REF(conn);
58 [ - + ][ # # ]: 82007 : RESULT_ENSURE_REF(send_ctx);
59 [ - + ][ # # ]: 82007 : RESULT_ENSURE(s2n_in_test(), S2N_ERR_NOT_IN_TEST);
60 : 82007 : conn->send_io_context = send_ctx;
61 : 82007 : s2n_sendmsg_fn = send_cb;
62 : 82007 : return S2N_RESULT_OK;
63 : 82007 : }
64 : :
65 : : S2N_RESULT s2n_ktls_set_recvmsg_cb(struct s2n_connection *conn, s2n_ktls_recvmsg_fn recv_cb,
66 : : void *recv_ctx)
67 : 32833 : {
68 [ - + ][ # # ]: 32833 : RESULT_ENSURE_REF(conn);
69 [ - + ][ # # ]: 32833 : RESULT_ENSURE_REF(recv_ctx);
70 [ - + ][ # # ]: 32833 : RESULT_ENSURE(s2n_in_test(), S2N_ERR_NOT_IN_TEST);
71 : 32833 : conn->recv_io_context = recv_ctx;
72 : 32833 : s2n_recvmsg_fn = recv_cb;
73 : 32833 : return S2N_RESULT_OK;
74 : 32833 : }
75 : :
76 : : static ssize_t s2n_ktls_default_recvmsg(void *io_context, struct msghdr *msg)
77 : 1 : {
78 [ + - ][ + - ]: 1 : POSIX_ENSURE_REF(io_context);
79 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(msg);
80 : :
81 : 0 : const struct s2n_socket_read_io_context *peer_socket_ctx = io_context;
82 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(peer_socket_ctx);
83 : 0 : int fd = peer_socket_ctx->fd;
84 : :
85 : 0 : return recvmsg(fd, msg, 0);
86 : 0 : }
87 : :
88 : : static ssize_t s2n_ktls_default_sendmsg(void *io_context, const struct msghdr *msg)
89 : 1 : {
90 [ + - ][ + - ]: 1 : POSIX_ENSURE_REF(io_context);
91 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(msg);
92 : :
93 : 0 : const struct s2n_socket_write_io_context *peer_socket_ctx = io_context;
94 [ # # ][ # # ]: 0 : POSIX_ENSURE_REF(peer_socket_ctx);
95 : 0 : int fd = peer_socket_ctx->fd;
96 : :
97 : 0 : return sendmsg(fd, msg, 0);
98 : 0 : }
99 : :
100 : : S2N_RESULT s2n_ktls_set_control_data(struct msghdr *msg, char *buf, size_t buf_size,
101 : : int cmsg_type, uint8_t record_type)
102 : 82430 : {
103 [ + + ][ + - ]: 82430 : RESULT_ENSURE_REF(msg);
104 [ + + ][ + - ]: 82429 : RESULT_ENSURE_REF(buf);
105 : :
106 : : /*
107 : : * https://man7.org/linux/man-pages/man3/cmsg.3.html
108 : : * To create ancillary data, first initialize the msg_controllen
109 : : * member of the msghdr with the length of the control message
110 : : * buffer.
111 : : */
112 : 82428 : msg->msg_control = buf;
113 : 82428 : msg->msg_controllen = buf_size;
114 : :
115 : : /*
116 : : * https://man7.org/linux/man-pages/man3/cmsg.3.html
117 : : * Use CMSG_FIRSTHDR() on the msghdr to get the first
118 : : * control message and CMSG_NXTHDR() to get all subsequent ones.
119 : : */
120 : 82428 : struct cmsghdr *hdr = CMSG_FIRSTHDR(msg);
121 [ + - ][ + + ]: 82428 : RESULT_ENSURE_REF(hdr);
122 : :
123 : : /*
124 : : * https://man7.org/linux/man-pages/man3/cmsg.3.html
125 : : * In each control message, initialize cmsg_len (with CMSG_LEN()), the
126 : : * other cmsghdr header fields, and the data portion using
127 : : * CMSG_DATA().
128 : : */
129 : 82427 : hdr->cmsg_len = CMSG_LEN(S2N_KTLS_RECORD_TYPE_SIZE);
130 : 82427 : hdr->cmsg_level = S2N_SOL_TLS;
131 : 82427 : hdr->cmsg_type = cmsg_type;
132 : 82427 : *CMSG_DATA(hdr) = record_type;
133 : :
134 : : /*
135 : : * https://man7.org/linux/man-pages/man3/cmsg.3.html
136 : : * Finally, the msg_controllen field of the msghdr
137 : : * should be set to the sum of the CMSG_SPACE() of the length of all
138 : : * control messages in the buffer
139 : : */
140 [ # # ][ - + ]: 82427 : RESULT_ENSURE_GTE(msg->msg_controllen, CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE));
141 : 82427 : msg->msg_controllen = CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE);
142 : :
143 : 82427 : return S2N_RESULT_OK;
144 : 82427 : }
145 : :
146 : : /* Expect to receive a single cmsghdr containing the TLS record_type.
147 : : *
148 : : * s2n-tls allocates enough space to receive a single cmsghdr. Since this is
149 : : * used to get the record_type when receiving over kTLS (enabled via
150 : : * `s2n_connection_ktls_enable_recv`), the application should not configure
151 : : * the socket to receive additional control messages. In the event s2n-tls
152 : : * can not retrieve the record_type, it is safer to drop the record.
153 : : */
154 : : S2N_RESULT s2n_ktls_get_control_data(struct msghdr *msg, int cmsg_type, uint8_t *record_type)
155 : 82387 : {
156 [ + + ][ + - ]: 82387 : RESULT_ENSURE_REF(msg);
157 [ + - ][ + + ]: 82386 : RESULT_ENSURE_REF(record_type);
158 : :
159 : : /* https://man7.org/linux/man-pages/man3/recvmsg.3p.html
160 : : * MSG_CTRUNC Control data was truncated.
161 : : */
162 [ + + ]: 82385 : if (msg->msg_flags & MSG_CTRUNC) {
163 [ + - ]: 1 : RESULT_BAIL(S2N_ERR_KTLS_BAD_CMSG);
164 : 1 : }
165 : :
166 : : /*
167 : : * https://man7.org/linux/man-pages/man3/cmsg.3.html
168 : : * To create ancillary data, first initialize the msg_controllen
169 : : * member of the msghdr with the length of the control message
170 : : * buffer.
171 : : */
172 [ - + ][ # # ]: 82384 : RESULT_ENSURE(msg->msg_control, S2N_ERR_SAFETY);
173 [ - + ][ # # ]: 82384 : RESULT_ENSURE(msg->msg_controllen >= CMSG_SPACE(S2N_KTLS_RECORD_TYPE_SIZE), S2N_ERR_SAFETY);
174 : :
175 : : /* https://man7.org/linux/man-pages/man3/cmsg.3.html
176 : : * Use CMSG_FIRSTHDR() on the msghdr to get the first
177 : : * control message and CMSG_NXTHDR() to get all subsequent ones.
178 : : */
179 : 82384 : struct cmsghdr *hdr = CMSG_FIRSTHDR(msg);
180 [ - + ][ # # ]: 82384 : RESULT_ENSURE(hdr, S2N_ERR_KTLS_BAD_CMSG);
181 : :
182 : : /*
183 : : * https://man7.org/linux/man-pages/man3/cmsg.3.html
184 : : * In each control message, initialize cmsg_len (with CMSG_LEN()), the
185 : : * other cmsghdr header fields, and the data portion using
186 : : * CMSG_DATA().
187 : : */
188 [ - + ][ # # ]: 82384 : RESULT_ENSURE(hdr->cmsg_level == S2N_SOL_TLS, S2N_ERR_KTLS_BAD_CMSG);
189 [ + + ][ + - ]: 82384 : RESULT_ENSURE(hdr->cmsg_type == cmsg_type, S2N_ERR_KTLS_BAD_CMSG);
190 [ - + ][ # # ]: 82383 : RESULT_ENSURE(hdr->cmsg_len == CMSG_LEN(S2N_KTLS_RECORD_TYPE_SIZE), S2N_ERR_KTLS_BAD_CMSG);
191 : 82383 : *record_type = *CMSG_DATA(hdr);
192 : :
193 : 82383 : return S2N_RESULT_OK;
194 : 82383 : }
195 : :
196 : : S2N_RESULT s2n_ktls_sendmsg(void *io_context, uint8_t record_type, const struct iovec *msg_iov,
197 : : size_t msg_iovlen, s2n_blocked_status *blocked, size_t *bytes_written)
198 : 49508 : {
199 [ + + ][ + - ]: 49508 : RESULT_ENSURE_REF(bytes_written);
200 [ + + ][ + - ]: 49507 : RESULT_ENSURE_REF(blocked);
201 [ + - ][ + + ]: 49505 : RESULT_ENSURE(msg_iov != NULL || msg_iovlen == 0, S2N_ERR_NULL);
[ + + ]
202 : :
203 : 49504 : *blocked = S2N_BLOCKED_ON_WRITE;
204 : 49504 : *bytes_written = 0;
205 : :
206 : 49504 : struct msghdr msg = {
207 : : /* msghdr requires a non-const iovec. This is safe because s2n-tls does
208 : : * not modify msg_iov after this point.
209 : : */
210 : 49504 : .msg_iov = (struct iovec *) (uintptr_t) msg_iov,
211 : 49504 : .msg_iovlen = msg_iovlen,
212 : 49504 : };
213 : :
214 : 49504 : char control_data[S2N_KTLS_CONTROL_BUFFER_SIZE] = { 0 };
215 [ - + ]: 49504 : RESULT_GUARD(s2n_ktls_set_control_data(&msg, control_data, sizeof(control_data),
216 : 49504 : S2N_TLS_SET_RECORD_TYPE, record_type));
217 : :
218 : 49504 : ssize_t result = 0;
219 [ + + ][ - + ]: 49504 : S2N_IO_RETRY_EINTR(result, s2n_sendmsg_fn(io_context, &msg));
220 [ + + ]: 49504 : RESULT_GUARD(s2n_io_check_write_result(result));
221 : :
222 : 49487 : *blocked = S2N_NOT_BLOCKED;
223 : 49487 : *bytes_written = result;
224 : 49487 : return S2N_RESULT_OK;
225 : 49504 : }
226 : :
227 : : S2N_RESULT s2n_ktls_recvmsg(void *io_context, uint8_t *record_type, uint8_t *buf,
228 : : size_t buf_len, s2n_blocked_status *blocked, size_t *bytes_read)
229 : 147 : {
230 [ + + ][ + - ]: 147 : RESULT_ENSURE_REF(record_type);
231 [ + + ][ + - ]: 146 : RESULT_ENSURE_REF(bytes_read);
232 [ + - ][ + + ]: 145 : RESULT_ENSURE_REF(blocked);
233 [ + + ][ + - ]: 144 : RESULT_ENSURE_REF(buf);
234 : : /* Ensure that buf_len is > 0 since trying to receive 0 bytes does not
235 : : * make sense and a return value of `0` from recvmsg is treated as EOF.
236 : : */
237 [ + - ][ + + ]: 143 : RESULT_ENSURE_GT(buf_len, 0);
238 : :
239 : 142 : *blocked = S2N_BLOCKED_ON_READ;
240 : 142 : *record_type = 0;
241 : 142 : *bytes_read = 0;
242 : 142 : struct iovec msg_iov = {
243 : 142 : .iov_base = buf,
244 : 142 : .iov_len = buf_len
245 : 142 : };
246 : 142 : struct msghdr msg = {
247 : 142 : .msg_iov = &msg_iov,
248 : 142 : .msg_iovlen = 1,
249 : 142 : };
250 : :
251 : : /*
252 : : * https://man7.org/linux/man-pages/man3/cmsg.3.html
253 : : * To create ancillary data, first initialize the msg_controllen
254 : : * member of the msghdr with the length of the control message
255 : : * buffer.
256 : : */
257 : 142 : char control_data[S2N_KTLS_CONTROL_BUFFER_SIZE] = { 0 };
258 : 142 : msg.msg_controllen = sizeof(control_data);
259 : 142 : msg.msg_control = control_data;
260 : :
261 : 142 : ssize_t result = 0;
262 [ + + ][ - + ]: 142 : S2N_IO_RETRY_EINTR(result, s2n_recvmsg_fn(io_context, &msg));
263 [ + + ]: 142 : RESULT_GUARD(s2n_io_check_read_result(result));
264 : :
265 [ + + ]: 122 : RESULT_GUARD(s2n_ktls_get_control_data(&msg, S2N_TLS_GET_RECORD_TYPE, record_type));
266 : :
267 : 121 : *blocked = S2N_NOT_BLOCKED;
268 : 121 : *bytes_read = result;
269 : 121 : return S2N_RESULT_OK;
270 : 122 : }
271 : :
272 : : /* The RFC defines the encryption limits in terms of "full-size records" sent.
273 : : * We can estimate the number of "full-sized records" sent by assuming that
274 : : * all records are full-sized.
275 : : */
276 : : static S2N_RESULT s2n_ktls_estimate_records(size_t bytes, uint64_t *estimate)
277 : 79 : {
278 [ - + ][ # # ]: 79 : RESULT_ENSURE_REF(estimate);
279 : 79 : uint64_t records = bytes / S2N_TLS_MAXIMUM_FRAGMENT_LENGTH;
280 [ + + ]: 79 : if (bytes % S2N_TLS_MAXIMUM_FRAGMENT_LENGTH) {
281 : 65 : records++;
282 : 65 : }
283 : 79 : *estimate = records;
284 : 79 : return S2N_RESULT_OK;
285 : 79 : }
286 : :
287 : : /* ktls does not currently support updating keys, so we should kill the connection
288 : : * when the key encryption limit is reached. We could get the current record
289 : : * sequence number from the kernel with getsockopt, but that requires a surprisingly
290 : : * expensive syscall.
291 : : *
292 : : * Instead, we track the estimated sequence number and enforce the limit based
293 : : * on that estimate.
294 : : */
295 : : static S2N_RESULT s2n_ktls_check_estimated_record_limit(
296 : : struct s2n_connection *conn, size_t bytes_requested)
297 : 50703 : {
298 [ - + ][ # # ]: 50703 : RESULT_ENSURE_REF(conn);
299 [ + + ]: 50703 : if (conn->actual_protocol_version < S2N_TLS13) {
300 : 50660 : return S2N_RESULT_OK;
301 : 50660 : }
302 : :
303 : 43 : uint64_t new_records_sent = 0;
304 [ - + ]: 43 : RESULT_GUARD(s2n_ktls_estimate_records(bytes_requested, &new_records_sent));
305 : :
306 : 43 : uint64_t old_records_sent = 0;
307 : 43 : struct s2n_blob seq_num = { 0 };
308 [ - + ]: 43 : RESULT_GUARD(s2n_connection_get_sequence_number(conn, conn->mode, &seq_num));
309 [ - + ]: 43 : RESULT_GUARD_POSIX(s2n_sequence_number_to_uint64(&seq_num, &old_records_sent));
310 : :
311 [ # # ][ + - ]: 43 : RESULT_ENSURE(S2N_ADD_IS_OVERFLOW_SAFE(old_records_sent, new_records_sent, UINT64_MAX),
[ + - ]
312 : 43 : S2N_ERR_KTLS_KEY_LIMIT);
313 : 43 : uint64_t total_records_sent = old_records_sent + new_records_sent;
314 : :
315 [ # # ][ - + ]: 43 : RESULT_ENSURE_REF(conn->secure);
316 [ - + ][ # # ]: 43 : RESULT_ENSURE_REF(conn->secure->cipher_suite);
317 [ - + ][ # # ]: 43 : RESULT_ENSURE_REF(conn->secure->cipher_suite->record_alg);
318 : 43 : uint64_t encryption_limit = conn->secure->cipher_suite->record_alg->encryption_limit;
319 [ + + ][ + - ]: 43 : RESULT_ENSURE(total_records_sent <= encryption_limit, S2N_ERR_KTLS_KEY_LIMIT);
320 : 36 : return S2N_RESULT_OK;
321 : 43 : }
322 : :
323 : : static S2N_RESULT s2n_ktls_set_estimated_sequence_number(
324 : : struct s2n_connection *conn, size_t bytes_written)
325 : 50691 : {
326 [ - + ][ # # ]: 50691 : RESULT_ENSURE_REF(conn);
327 [ + + ]: 50691 : if (conn->actual_protocol_version < S2N_TLS13) {
328 : 50655 : return S2N_RESULT_OK;
329 : 50655 : }
330 : :
331 : 36 : uint64_t new_records_sent = 0;
332 [ - + ]: 36 : RESULT_GUARD(s2n_ktls_estimate_records(bytes_written, &new_records_sent));
333 : :
334 : 36 : struct s2n_blob seq_num = { 0 };
335 [ - + ]: 36 : RESULT_GUARD(s2n_connection_get_sequence_number(conn, conn->mode, &seq_num));
336 : :
337 [ + + ]: 90 : for (size_t i = 0; i < new_records_sent; i++) {
338 [ - + ]: 54 : RESULT_GUARD_POSIX(s2n_increment_sequence_number(&seq_num));
339 : 54 : }
340 : 36 : return S2N_RESULT_OK;
341 : 36 : }
342 : :
343 : : /* The iovec array `bufs` is constant and owned by the application.
344 : : *
345 : : * However, we need to apply the given offset to `bufs`. That may involve
346 : : * updating the iov_base and iov_len of entries in `bufs` to reflect the bytes
347 : : * already sent. Because `bufs` is constant, we need to instead copy `bufs` and
348 : : * modify the copy.
349 : : *
350 : : * Since one of the primary benefits of kTLS is that we avoid buffering application
351 : : * data and can pass application data as-is to the kernel, we try to limit the
352 : : * situations where we need to copy `bufs` and use stack memory where possible.
353 : : *
354 : : * Note: We are copying an array of iovecs here, NOT the scattered application
355 : : * data the iovecs reference. On Linux, the maximum data copied would be
356 : : * 1024 (IOV_MAX on Linux) * 16 (sizeof(struct iovec)) = ~16KB.
357 : : *
358 : : * To avoid any copies when using a large number of iovecs, applications should
359 : : * call s2n_sendv instead of s2n_sendv_with_offset.
360 : : */
361 : : static S2N_RESULT s2n_ktls_update_bufs_with_offset(const struct iovec **bufs, size_t *count,
362 : : size_t offs, struct s2n_blob *mem)
363 : 32776 : {
364 [ - + ][ # # ]: 32776 : RESULT_ENSURE_REF(bufs);
365 [ # # ][ - + ]: 32776 : RESULT_ENSURE_REF(count);
366 [ # # ][ + - ]: 32776 : RESULT_ENSURE(*bufs != NULL || *count == 0, S2N_ERR_NULL);
[ # # ]
367 [ - + ][ # # ]: 32776 : RESULT_ENSURE_REF(mem);
368 : :
369 : 32776 : size_t skipped = 0;
370 [ + + ]: 1763849 : while (offs > 0) {
371 : : /* If we need to skip more iovecs than actually exist,
372 : : * then the offset is too large and therefore invalid.
373 : : */
374 [ - + ][ # # ]: 1763832 : RESULT_ENSURE(skipped < *count, S2N_ERR_INVALID_ARGUMENT);
375 : :
376 : 1763832 : size_t iov_len = (*bufs)[skipped].iov_len;
377 : :
378 : : /* This is the last iovec affected by the offset. */
379 [ + + ]: 1763832 : if (offs < iov_len) {
380 : 32759 : break;
381 : 32759 : }
382 : :
383 : 1731073 : offs -= iov_len;
384 : 1731073 : skipped++;
385 : 1731073 : }
386 : :
387 : 32776 : *count = (*count) - skipped;
388 [ + + ]: 32776 : if (*count == 0) {
389 : 2 : return S2N_RESULT_OK;
390 : 2 : }
391 : :
392 : 32774 : *bufs = &(*bufs)[skipped];
393 [ + + ]: 32774 : if (offs == 0) {
394 : 15 : return S2N_RESULT_OK;
395 : 15 : }
396 : :
397 : 32759 : size_t size = (*count) * (sizeof(struct iovec));
398 : : /* If possible, use the existing stack memory in `mem` for the copy.
399 : : * Otherwise, we need to allocate sufficient new heap memory. */
400 [ + + ]: 32759 : if (size > mem->size) {
401 [ - + ]: 222 : RESULT_GUARD_POSIX(s2n_alloc(mem, size));
402 : 222 : }
403 : :
404 : 32759 : struct iovec *new_bufs = (struct iovec *) (void *) mem->data;
405 [ - + ][ # # ]: 32759 : RESULT_CHECKED_MEMCPY(new_bufs, *bufs, size);
[ + - ]
406 : 32759 : new_bufs[0].iov_base = (uint8_t *) new_bufs[0].iov_base + offs;
407 : 32759 : new_bufs[0].iov_len = new_bufs[0].iov_len - offs;
408 : 32759 : *bufs = new_bufs;
409 : :
410 : 32759 : return S2N_RESULT_OK;
411 : 32759 : }
412 : :
413 : : ssize_t s2n_ktls_sendv_with_offset(struct s2n_connection *conn, const struct iovec *bufs,
414 : : ssize_t count_in, ssize_t offs_in, s2n_blocked_status *blocked)
415 : 49224 : {
416 [ + - ][ + + ]: 49224 : POSIX_ENSURE_REF(conn);
417 [ + + ][ + - ]: 49223 : POSIX_ENSURE(count_in >= 0, S2N_ERR_INVALID_ARGUMENT);
418 : 49222 : size_t count = count_in;
419 [ + + ][ + - ]: 49222 : POSIX_ENSURE(offs_in >= 0, S2N_ERR_INVALID_ARGUMENT);
420 : 49221 : size_t offs = offs_in;
421 : :
422 : 49221 : ssize_t total_bytes = 0;
423 [ + + ]: 49221 : POSIX_GUARD_RESULT(s2n_sendv_with_offset_total_size(bufs, count_in, offs_in, &total_bytes));
424 [ + + ]: 49218 : POSIX_GUARD_RESULT(s2n_ktls_check_estimated_record_limit(conn, total_bytes));
425 : :
426 : : /* The order of new_bufs and new_bufs_mem matters. See https://github.com/aws/s2n-tls/issues/4354 */
427 : 49212 : uint8_t new_bufs_mem[S2N_MAX_STACK_IOVECS_MEM] = { 0 };
428 : 49212 : DEFER_CLEANUP(struct s2n_blob new_bufs = { 0 }, s2n_free_or_wipe);
429 [ - + ]: 49212 : POSIX_GUARD(s2n_blob_init(&new_bufs, new_bufs_mem, sizeof(new_bufs_mem)));
430 [ + + ]: 49212 : if (offs > 0) {
431 [ - + ]: 32776 : POSIX_GUARD_RESULT(s2n_ktls_update_bufs_with_offset(&bufs, &count, offs, &new_bufs));
432 : 32776 : }
433 : :
434 : 49212 : size_t bytes_written = 0;
435 [ + + ]: 49212 : POSIX_GUARD_RESULT(s2n_ktls_sendmsg(conn->send_io_context, TLS_APPLICATION_DATA,
436 : 49209 : bufs, count, blocked, &bytes_written));
437 : :
438 [ - + ]: 49209 : POSIX_GUARD_RESULT(s2n_ktls_set_estimated_sequence_number(conn, bytes_written));
439 : 49209 : return bytes_written;
440 : 49209 : }
441 : :
442 : : int s2n_ktls_send_cb(void *io_context, const uint8_t *buf, uint32_t len)
443 : 19 : {
444 [ + + ][ + - ]: 19 : POSIX_ENSURE_REF(io_context);
445 [ + + ][ + - ]: 18 : POSIX_ENSURE_REF(buf);
446 : :
447 : : /* For now, all control records are assumed to be alerts.
448 : : * We can set the record_type on the io_context in the future.
449 : : */
450 : 17 : const uint8_t record_type = TLS_ALERT;
451 : :
452 : 17 : const struct iovec iov = {
453 : 17 : .iov_base = (void *) (uintptr_t) buf,
454 : 17 : .iov_len = len,
455 : 17 : };
456 : 17 : s2n_blocked_status blocked = S2N_NOT_BLOCKED;
457 : 17 : size_t bytes_written = 0;
458 : :
459 [ + + ]: 17 : POSIX_GUARD_RESULT(s2n_ktls_sendmsg(io_context, record_type, &iov, 1,
460 : 11 : &blocked, &bytes_written));
461 : :
462 [ - + ][ # # ]: 11 : POSIX_ENSURE_LTE(bytes_written, len);
463 : 11 : return bytes_written;
464 : 11 : }
465 : :
466 : : int s2n_ktls_record_writev(struct s2n_connection *conn, uint8_t content_type,
467 : : const struct iovec *in, int in_count, size_t offs, size_t to_write)
468 : 13 : {
469 [ + - ][ + + ]: 13 : POSIX_ENSURE_REF(conn);
470 [ + + ][ + - ]: 12 : POSIX_ENSURE(in_count > 0, S2N_ERR_INVALID_ARGUMENT);
471 : 11 : size_t count = in_count;
472 [ + + ][ + - ]: 11 : POSIX_ENSURE_REF(in);
473 : :
474 : : /* Currently, ktls only supports sending alerts.
475 : : * To also support handshake messages, we would need a way to track record_type.
476 : : * We could add a field to the send io context.
477 : : */
478 [ + + ][ + - ]: 10 : POSIX_ENSURE(content_type == TLS_ALERT, S2N_ERR_UNIMPLEMENTED);
479 : :
480 : : /* When stuffers automatically resize, they allocate a potentially large
481 : : * chunk of memory to avoid repeated resizes.
482 : : * Since ktls only uses conn->out for control messages (alerts and eventually
483 : : * handshake messages), we expect infrequent small writes with conn->out
484 : : * freed in between. Since we're therefore more concerned with the size of
485 : : * the allocation than the frequency, use a more accurate size for each write.
486 : : */
487 [ - + ]: 9 : POSIX_GUARD(s2n_stuffer_resize_if_empty(&conn->out, to_write));
488 : :
489 [ - + ]: 9 : POSIX_GUARD(s2n_stuffer_writev_bytes(&conn->out, in, count, offs, to_write));
490 : 9 : return to_write;
491 : 9 : }
492 : :
493 : : int s2n_sendfile(struct s2n_connection *conn, int in_fd, off_t offset, size_t count,
494 : : size_t *bytes_written, s2n_blocked_status *blocked)
495 : 1488 : {
496 [ + + ][ + - ]: 1488 : POSIX_ENSURE_REF(blocked);
497 : 1487 : *blocked = S2N_BLOCKED_ON_WRITE;
498 [ + + ][ + - ]: 1487 : POSIX_ENSURE_REF(bytes_written);
499 : 1486 : *bytes_written = 0;
500 [ + + ][ + - ]: 1486 : POSIX_ENSURE_REF(conn);
501 [ # # ][ - + ]: 1485 : POSIX_ENSURE(conn->ktls_send_enabled, S2N_ERR_KTLS_UNSUPPORTED_CONN);
502 [ + + ]: 1485 : POSIX_GUARD_RESULT(s2n_ktls_check_estimated_record_limit(conn, count));
503 : :
504 : 1484 : int out_fd = 0;
505 [ - + ]: 1484 : POSIX_GUARD_RESULT(s2n_ktls_get_file_descriptor(conn, S2N_KTLS_MODE_SEND, &out_fd));
506 : :
507 : 1484 : #ifdef S2N_LINUX_SENDFILE
508 : : /* https://man7.org/linux/man-pages/man2/sendfile.2.html */
509 : 1484 : ssize_t result = 0;
510 [ + + ][ - + ]: 1484 : S2N_IO_RETRY_EINTR(result, sendfile(out_fd, in_fd, &offset, count));
511 [ + + ]: 1484 : POSIX_GUARD_RESULT(s2n_io_check_write_result(result));
512 : 1482 : *bytes_written = result;
513 : : #else
514 : : POSIX_BAIL(S2N_ERR_UNIMPLEMENTED);
515 : : #endif
516 : :
517 [ - + ]: 1482 : POSIX_GUARD_RESULT(s2n_ktls_set_estimated_sequence_number(conn, *bytes_written));
518 : 1482 : *blocked = S2N_NOT_BLOCKED;
519 : 1482 : return S2N_SUCCESS;
520 : 1482 : }
521 : :
522 : : int s2n_ktls_read_full_record(struct s2n_connection *conn, uint8_t *record_type)
523 : 128 : {
524 [ + + ][ + - ]: 128 : POSIX_ENSURE_REF(conn);
525 [ + + ][ + - ]: 127 : POSIX_ENSURE_REF(record_type);
526 : :
527 : : /* If any unread data remains in conn->in, it must be application data that
528 : : * couldn't be returned due to the size of the application's provided buffer.
529 : : */
530 [ + + ]: 126 : if (s2n_stuffer_data_available(&conn->in)) {
531 : 2 : *record_type = TLS_APPLICATION_DATA;
532 : 2 : return S2N_SUCCESS;
533 : 2 : }
534 : :
535 [ - + ]: 124 : POSIX_GUARD(s2n_stuffer_resize_if_empty(&conn->buffer_in, S2N_DEFAULT_FRAGMENT_LENGTH));
536 : :
537 : 124 : struct s2n_stuffer record_stuffer = conn->buffer_in;
538 : 124 : size_t len = s2n_stuffer_space_remaining(&record_stuffer);
539 : 124 : uint8_t *buf = s2n_stuffer_raw_write(&record_stuffer, len);
540 [ - + ][ # # ]: 124 : POSIX_ENSURE_REF(buf);
541 : :
542 : 124 : s2n_blocked_status blocked = S2N_NOT_BLOCKED;
543 : 124 : size_t bytes_read = 0;
544 : :
545 : : /* Since recvmsg is responsible for decrypting the record in ktls,
546 : : * we apply blinding to the recvmsg call.
547 : : */
548 : 124 : s2n_result result = s2n_ktls_recvmsg(conn->recv_io_context, record_type,
549 : 124 : buf, len, &blocked, &bytes_read);
550 [ + + ][ + - ]: 124 : WITH_ERROR_BLINDING(conn, POSIX_GUARD_RESULT(result));
551 : :
552 [ - + ]: 119 : POSIX_GUARD(s2n_stuffer_skip_write(&conn->buffer_in, bytes_read));
553 : :
554 : : /* We don't care about returning a full fragment because we don't need to decrypt.
555 : : * kTLS handled decryption already.
556 : : * So we can always set conn->in equal to the full buffer_in.
557 : : */
558 [ - + ]: 119 : POSIX_GUARD_RESULT(s2n_recv_in_init(conn, bytes_read, bytes_read));
559 : 119 : return S2N_SUCCESS;
560 : 119 : }
|