1 | /*************************************************************************** |
2 | * _ _ ____ _ |
3 | * Project ___| | | | _ \| | |
4 | * / __| | | | |_) | | |
5 | * | (__| |_| | _ <| |___ |
6 | * \___|\___/|_| \_\_____| |
7 | * |
8 | * Copyright (C) Jacob Hoffman-Andrews, |
9 | * <github@hoffman-andrews.com> |
10 | * |
11 | * This software is licensed as described in the file COPYING, which |
12 | * you should have received as part of this distribution. The terms |
13 | * are also available at https://curl.se/docs/copyright.html. |
14 | * |
15 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
16 | * copies of the Software, and permit persons to whom the Software is |
17 | * furnished to do so, under the terms of the COPYING file. |
18 | * |
19 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
20 | * KIND, either express or implied. |
21 | * |
22 | * SPDX-License-Identifier: curl |
23 | * |
24 | ***************************************************************************/ |
25 | #include "curl_setup.h" |
26 | |
27 | #ifdef USE_RUSTLS |
28 | |
29 | #include "curl_printf.h" |
30 | |
31 | #include <errno.h> |
32 | #include <rustls.h> |
33 | |
34 | #include "inet_pton.h" |
35 | #include "urldata.h" |
36 | #include "sendf.h" |
37 | #include "vtls.h" |
38 | #include "vtls_int.h" |
39 | #include "select.h" |
40 | #include "strerror.h" |
41 | #include "multiif.h" |
42 | |
43 | struct rustls_ssl_backend_data |
44 | { |
45 | const struct rustls_client_config *config; |
46 | struct rustls_connection *conn; |
47 | bool data_pending; |
48 | }; |
49 | |
50 | /* For a given rustls_result error code, return the best-matching CURLcode. */ |
51 | static CURLcode map_error(rustls_result r) |
52 | { |
53 | if(rustls_result_is_cert_error(r)) { |
54 | return CURLE_PEER_FAILED_VERIFICATION; |
55 | } |
56 | switch(r) { |
57 | case RUSTLS_RESULT_OK: |
58 | return CURLE_OK; |
59 | case RUSTLS_RESULT_NULL_PARAMETER: |
60 | return CURLE_BAD_FUNCTION_ARGUMENT; |
61 | default: |
62 | return CURLE_READ_ERROR; |
63 | } |
64 | } |
65 | |
66 | static bool |
67 | cr_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data) |
68 | { |
69 | struct ssl_connect_data *ctx = cf->ctx; |
70 | struct rustls_ssl_backend_data *backend; |
71 | |
72 | (void)data; |
73 | DEBUGASSERT(ctx && ctx->backend); |
74 | backend = (struct rustls_ssl_backend_data *)ctx->backend; |
75 | return backend->data_pending; |
76 | } |
77 | |
78 | static CURLcode |
79 | cr_connect(struct Curl_cfilter *cf UNUSED_PARAM, |
80 | struct Curl_easy *data UNUSED_PARAM) |
81 | { |
82 | infof(data, "rustls_connect: unimplemented" ); |
83 | return CURLE_SSL_CONNECT_ERROR; |
84 | } |
85 | |
86 | struct io_ctx { |
87 | struct Curl_cfilter *cf; |
88 | struct Curl_easy *data; |
89 | }; |
90 | |
91 | static int |
92 | read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n) |
93 | { |
94 | struct io_ctx *io_ctx = userdata; |
95 | CURLcode result; |
96 | int ret = 0; |
97 | ssize_t nread = Curl_conn_cf_recv(io_ctx->cf->next, io_ctx->data, |
98 | (char *)buf, len, &result); |
99 | if(nread < 0) { |
100 | nread = 0; |
101 | if(CURLE_AGAIN == result) |
102 | ret = EAGAIN; |
103 | else |
104 | ret = EINVAL; |
105 | } |
106 | *out_n = (int)nread; |
107 | return ret; |
108 | } |
109 | |
110 | static int |
111 | write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n) |
112 | { |
113 | struct io_ctx *io_ctx = userdata; |
114 | CURLcode result; |
115 | int ret = 0; |
116 | ssize_t nwritten = Curl_conn_cf_send(io_ctx->cf->next, io_ctx->data, |
117 | (const char *)buf, len, &result); |
118 | if(nwritten < 0) { |
119 | nwritten = 0; |
120 | if(CURLE_AGAIN == result) |
121 | ret = EAGAIN; |
122 | else |
123 | ret = EINVAL; |
124 | } |
125 | *out_n = (int)nwritten; |
126 | /* |
127 | CURL_TRC_CFX(io_ctx->data, io_ctx->cf, "cf->next send(len=%zu) -> %zd, %d", |
128 | len, nwritten, result)); |
129 | */ |
130 | return ret; |
131 | } |
132 | |
133 | static ssize_t tls_recv_more(struct Curl_cfilter *cf, |
134 | struct Curl_easy *data, CURLcode *err) |
135 | { |
136 | struct ssl_connect_data *const connssl = cf->ctx; |
137 | struct rustls_ssl_backend_data *const backend = |
138 | (struct rustls_ssl_backend_data *)connssl->backend; |
139 | struct io_ctx io_ctx; |
140 | size_t tls_bytes_read = 0; |
141 | rustls_io_result io_error; |
142 | rustls_result rresult = 0; |
143 | |
144 | io_ctx.cf = cf; |
145 | io_ctx.data = data; |
146 | io_error = rustls_connection_read_tls(backend->conn, read_cb, &io_ctx, |
147 | &tls_bytes_read); |
148 | if(io_error == EAGAIN || io_error == EWOULDBLOCK) { |
149 | *err = CURLE_AGAIN; |
150 | return -1; |
151 | } |
152 | else if(io_error) { |
153 | char buffer[STRERROR_LEN]; |
154 | failf(data, "reading from socket: %s" , |
155 | Curl_strerror(io_error, buffer, sizeof(buffer))); |
156 | *err = CURLE_READ_ERROR; |
157 | return -1; |
158 | } |
159 | |
160 | rresult = rustls_connection_process_new_packets(backend->conn); |
161 | if(rresult != RUSTLS_RESULT_OK) { |
162 | char errorbuf[255]; |
163 | size_t errorlen; |
164 | rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen); |
165 | failf(data, "rustls_connection_process_new_packets: %.*s" , |
166 | errorlen, errorbuf); |
167 | *err = map_error(rresult); |
168 | return -1; |
169 | } |
170 | |
171 | backend->data_pending = TRUE; |
172 | *err = CURLE_OK; |
173 | return (ssize_t)tls_bytes_read; |
174 | } |
175 | |
176 | /* |
177 | * On each run: |
178 | * - Read a chunk of bytes from the socket into rustls' TLS input buffer. |
179 | * - Tell rustls to process any new packets. |
180 | * - Read out as many plaintext bytes from rustls as possible, until hitting |
181 | * error, EOF, or EAGAIN/EWOULDBLOCK, or plainbuf/plainlen is filled up. |
182 | * |
183 | * It's okay to call this function with plainbuf == NULL and plainlen == 0. |
184 | * In that case, it will copy bytes from the socket into rustls' TLS input |
185 | * buffer, and process packets, but won't consume bytes from rustls' plaintext |
186 | * output buffer. |
187 | */ |
188 | static ssize_t |
189 | cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data, |
190 | char *plainbuf, size_t plainlen, CURLcode *err) |
191 | { |
192 | struct ssl_connect_data *const connssl = cf->ctx; |
193 | struct rustls_ssl_backend_data *const backend = |
194 | (struct rustls_ssl_backend_data *)connssl->backend; |
195 | struct rustls_connection *rconn = NULL; |
196 | size_t n = 0; |
197 | size_t plain_bytes_copied = 0; |
198 | rustls_result rresult = 0; |
199 | ssize_t nread; |
200 | bool eof = FALSE; |
201 | |
202 | DEBUGASSERT(backend); |
203 | rconn = backend->conn; |
204 | |
205 | while(plain_bytes_copied < plainlen) { |
206 | if(!backend->data_pending) { |
207 | if(tls_recv_more(cf, data, err) < 0) { |
208 | if(*err != CURLE_AGAIN) { |
209 | nread = -1; |
210 | goto out; |
211 | } |
212 | break; |
213 | } |
214 | } |
215 | |
216 | rresult = rustls_connection_read(rconn, |
217 | (uint8_t *)plainbuf + plain_bytes_copied, |
218 | plainlen - plain_bytes_copied, |
219 | &n); |
220 | if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) { |
221 | backend->data_pending = FALSE; |
222 | } |
223 | else if(rresult == RUSTLS_RESULT_UNEXPECTED_EOF) { |
224 | failf(data, "rustls: peer closed TCP connection " |
225 | "without first closing TLS connection" ); |
226 | *err = CURLE_READ_ERROR; |
227 | nread = -1; |
228 | goto out; |
229 | } |
230 | else if(rresult != RUSTLS_RESULT_OK) { |
231 | /* n always equals 0 in this case, don't need to check it */ |
232 | char errorbuf[255]; |
233 | size_t errorlen; |
234 | rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen); |
235 | failf(data, "rustls_connection_read: %.*s" , errorlen, errorbuf); |
236 | *err = CURLE_READ_ERROR; |
237 | nread = -1; |
238 | goto out; |
239 | } |
240 | else if(n == 0) { |
241 | /* n == 0 indicates clean EOF, but we may have read some other |
242 | plaintext bytes before we reached this. Break out of the loop |
243 | so we can figure out whether to return success or EOF. */ |
244 | eof = TRUE; |
245 | break; |
246 | } |
247 | else { |
248 | plain_bytes_copied += n; |
249 | } |
250 | } |
251 | |
252 | if(plain_bytes_copied) { |
253 | *err = CURLE_OK; |
254 | nread = (ssize_t)plain_bytes_copied; |
255 | } |
256 | else if(eof) { |
257 | *err = CURLE_OK; |
258 | nread = 0; |
259 | } |
260 | else { |
261 | *err = CURLE_AGAIN; |
262 | nread = -1; |
263 | } |
264 | |
265 | out: |
266 | CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d" , |
267 | plainlen, nread, *err); |
268 | return nread; |
269 | } |
270 | |
271 | /* |
272 | * On each call: |
273 | * - Copy `plainlen` bytes into rustls' plaintext input buffer (if > 0). |
274 | * - Fully drain rustls' plaintext output buffer into the socket until |
275 | * we get either an error or EAGAIN/EWOULDBLOCK. |
276 | * |
277 | * It's okay to call this function with plainbuf == NULL and plainlen == 0. |
278 | * In that case, it won't read anything into rustls' plaintext input buffer. |
279 | * It will only drain rustls' plaintext output buffer into the socket. |
280 | */ |
281 | static ssize_t |
282 | cr_send(struct Curl_cfilter *cf, struct Curl_easy *data, |
283 | const void *plainbuf, size_t plainlen, CURLcode *err) |
284 | { |
285 | struct ssl_connect_data *const connssl = cf->ctx; |
286 | struct rustls_ssl_backend_data *const backend = |
287 | (struct rustls_ssl_backend_data *)connssl->backend; |
288 | struct rustls_connection *rconn = NULL; |
289 | struct io_ctx io_ctx; |
290 | size_t plainwritten = 0; |
291 | size_t tlswritten = 0; |
292 | size_t tlswritten_total = 0; |
293 | rustls_result rresult; |
294 | rustls_io_result io_error; |
295 | char errorbuf[256]; |
296 | size_t errorlen; |
297 | |
298 | DEBUGASSERT(backend); |
299 | rconn = backend->conn; |
300 | |
301 | CURL_TRC_CF(data, cf, "cf_send: %ld plain bytes" , plainlen); |
302 | |
303 | io_ctx.cf = cf; |
304 | io_ctx.data = data; |
305 | |
306 | if(plainlen > 0) { |
307 | rresult = rustls_connection_write(rconn, plainbuf, plainlen, |
308 | &plainwritten); |
309 | if(rresult != RUSTLS_RESULT_OK) { |
310 | rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen); |
311 | failf(data, "rustls_connection_write: %.*s" , errorlen, errorbuf); |
312 | *err = CURLE_WRITE_ERROR; |
313 | return -1; |
314 | } |
315 | else if(plainwritten == 0) { |
316 | failf(data, "rustls_connection_write: EOF" ); |
317 | *err = CURLE_WRITE_ERROR; |
318 | return -1; |
319 | } |
320 | } |
321 | |
322 | while(rustls_connection_wants_write(rconn)) { |
323 | io_error = rustls_connection_write_tls(rconn, write_cb, &io_ctx, |
324 | &tlswritten); |
325 | if(io_error == EAGAIN || io_error == EWOULDBLOCK) { |
326 | CURL_TRC_CF(data, cf, "cf_send: EAGAIN after %zu bytes" , |
327 | tlswritten_total); |
328 | *err = CURLE_AGAIN; |
329 | return -1; |
330 | } |
331 | else if(io_error) { |
332 | char buffer[STRERROR_LEN]; |
333 | failf(data, "writing to socket: %s" , |
334 | Curl_strerror(io_error, buffer, sizeof(buffer))); |
335 | *err = CURLE_WRITE_ERROR; |
336 | return -1; |
337 | } |
338 | if(tlswritten == 0) { |
339 | failf(data, "EOF in swrite" ); |
340 | *err = CURLE_WRITE_ERROR; |
341 | return -1; |
342 | } |
343 | CURL_TRC_CF(data, cf, "cf_send: wrote %zu TLS bytes" , tlswritten); |
344 | tlswritten_total += tlswritten; |
345 | } |
346 | |
347 | return plainwritten; |
348 | } |
349 | |
350 | /* A server certificate verify callback for rustls that always returns |
351 | RUSTLS_RESULT_OK, or in other words disable certificate verification. */ |
352 | static enum rustls_result |
353 | cr_verify_none(void *userdata UNUSED_PARAM, |
354 | const rustls_verify_server_cert_params *params UNUSED_PARAM) |
355 | { |
356 | return RUSTLS_RESULT_OK; |
357 | } |
358 | |
359 | static bool |
360 | cr_hostname_is_ip(const char *hostname) |
361 | { |
362 | struct in_addr in; |
363 | #ifdef ENABLE_IPV6 |
364 | struct in6_addr in6; |
365 | if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) { |
366 | return true; |
367 | } |
368 | #endif /* ENABLE_IPV6 */ |
369 | if(Curl_inet_pton(AF_INET, hostname, &in) > 0) { |
370 | return true; |
371 | } |
372 | return false; |
373 | } |
374 | |
375 | static CURLcode |
376 | cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data, |
377 | struct rustls_ssl_backend_data *const backend) |
378 | { |
379 | struct ssl_connect_data *connssl = cf->ctx; |
380 | struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf); |
381 | struct rustls_connection *rconn = NULL; |
382 | struct rustls_client_config_builder *config_builder = NULL; |
383 | struct rustls_root_cert_store *roots = NULL; |
384 | const struct curl_blob *ca_info_blob = conn_config->ca_info_blob; |
385 | const char * const ssl_cafile = |
386 | /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */ |
387 | (ca_info_blob ? NULL : conn_config->CAfile); |
388 | const bool verifypeer = conn_config->verifypeer; |
389 | const char *hostname = connssl->hostname; |
390 | char errorbuf[256]; |
391 | size_t errorlen; |
392 | int result; |
393 | |
394 | DEBUGASSERT(backend); |
395 | rconn = backend->conn; |
396 | |
397 | config_builder = rustls_client_config_builder_new(); |
398 | if(connssl->alpn) { |
399 | struct alpn_proto_buf proto; |
400 | rustls_slice_bytes alpn[ALPN_ENTRIES_MAX]; |
401 | size_t i; |
402 | |
403 | for(i = 0; i < connssl->alpn->count; ++i) { |
404 | alpn[i].data = (const uint8_t *)connssl->alpn->entries[i]; |
405 | alpn[i].len = strlen(connssl->alpn->entries[i]); |
406 | } |
407 | rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, |
408 | connssl->alpn->count); |
409 | Curl_alpn_to_proto_str(&proto, connssl->alpn); |
410 | infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data); |
411 | } |
412 | if(!verifypeer) { |
413 | rustls_client_config_builder_dangerous_set_certificate_verifier( |
414 | config_builder, cr_verify_none); |
415 | /* rustls doesn't support IP addresses (as of 0.19.0), and will reject |
416 | * connections created with an IP address, even when certificate |
417 | * verification is turned off. Set a placeholder hostname and disable |
418 | * SNI. */ |
419 | if(cr_hostname_is_ip(hostname)) { |
420 | rustls_client_config_builder_set_enable_sni(config_builder, false); |
421 | hostname = "example.invalid" ; |
422 | } |
423 | } |
424 | else if(ca_info_blob) { |
425 | roots = rustls_root_cert_store_new(); |
426 | |
427 | /* Enable strict parsing only if verification isn't disabled. */ |
428 | result = rustls_root_cert_store_add_pem(roots, ca_info_blob->data, |
429 | ca_info_blob->len, verifypeer); |
430 | if(result != RUSTLS_RESULT_OK) { |
431 | failf(data, "rustls: failed to parse trusted certificates from blob" ); |
432 | rustls_root_cert_store_free(roots); |
433 | rustls_client_config_free( |
434 | rustls_client_config_builder_build(config_builder)); |
435 | return CURLE_SSL_CACERT_BADFILE; |
436 | } |
437 | |
438 | result = rustls_client_config_builder_use_roots(config_builder, roots); |
439 | rustls_root_cert_store_free(roots); |
440 | if(result != RUSTLS_RESULT_OK) { |
441 | failf(data, "rustls: failed to load trusted certificates" ); |
442 | rustls_client_config_free( |
443 | rustls_client_config_builder_build(config_builder)); |
444 | return CURLE_SSL_CACERT_BADFILE; |
445 | } |
446 | } |
447 | else if(ssl_cafile) { |
448 | result = rustls_client_config_builder_load_roots_from_file( |
449 | config_builder, ssl_cafile); |
450 | if(result != RUSTLS_RESULT_OK) { |
451 | failf(data, "rustls: failed to load trusted certificates" ); |
452 | rustls_client_config_free( |
453 | rustls_client_config_builder_build(config_builder)); |
454 | return CURLE_SSL_CACERT_BADFILE; |
455 | } |
456 | } |
457 | |
458 | backend->config = rustls_client_config_builder_build(config_builder); |
459 | DEBUGASSERT(rconn == NULL); |
460 | { |
461 | char *snihost = Curl_ssl_snihost(data, hostname, NULL); |
462 | if(!snihost) { |
463 | failf(data, "rustls: failed to get SNI" ); |
464 | return CURLE_SSL_CONNECT_ERROR; |
465 | } |
466 | result = rustls_client_connection_new(backend->config, snihost, &rconn); |
467 | } |
468 | if(result != RUSTLS_RESULT_OK) { |
469 | rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen); |
470 | failf(data, "rustls_client_connection_new: %.*s" , errorlen, errorbuf); |
471 | return CURLE_COULDNT_CONNECT; |
472 | } |
473 | rustls_connection_set_userdata(rconn, backend); |
474 | backend->conn = rconn; |
475 | return CURLE_OK; |
476 | } |
477 | |
478 | static void |
479 | cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data, |
480 | const struct rustls_connection *rconn) |
481 | { |
482 | const uint8_t *protocol = NULL; |
483 | size_t len = 0; |
484 | |
485 | rustls_connection_get_alpn_protocol(rconn, &protocol, &len); |
486 | Curl_alpn_set_negotiated(cf, data, protocol, len); |
487 | } |
488 | |
489 | static CURLcode |
490 | cr_connect_nonblocking(struct Curl_cfilter *cf, |
491 | struct Curl_easy *data, bool *done) |
492 | { |
493 | struct ssl_connect_data *const connssl = cf->ctx; |
494 | curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); |
495 | struct rustls_ssl_backend_data *const backend = |
496 | (struct rustls_ssl_backend_data *)connssl->backend; |
497 | struct rustls_connection *rconn = NULL; |
498 | CURLcode tmperr = CURLE_OK; |
499 | int result; |
500 | int what; |
501 | bool wants_read; |
502 | bool wants_write; |
503 | curl_socket_t writefd; |
504 | curl_socket_t readfd; |
505 | |
506 | DEBUGASSERT(backend); |
507 | |
508 | if(ssl_connection_none == connssl->state) { |
509 | result = cr_init_backend(cf, data, |
510 | (struct rustls_ssl_backend_data *)connssl->backend); |
511 | if(result != CURLE_OK) { |
512 | return result; |
513 | } |
514 | connssl->state = ssl_connection_negotiating; |
515 | } |
516 | |
517 | rconn = backend->conn; |
518 | |
519 | /* Read/write data until the handshake is done or the socket would block. */ |
520 | for(;;) { |
521 | /* |
522 | * Connection has been established according to rustls. Set send/recv |
523 | * handlers, and update the state machine. |
524 | */ |
525 | if(!rustls_connection_is_handshaking(rconn)) { |
526 | infof(data, "Done handshaking" ); |
527 | /* Done with the handshake. Set up callbacks to send/receive data. */ |
528 | connssl->state = ssl_connection_complete; |
529 | |
530 | cr_set_negotiated_alpn(cf, data, rconn); |
531 | |
532 | *done = TRUE; |
533 | return CURLE_OK; |
534 | } |
535 | |
536 | wants_read = rustls_connection_wants_read(rconn); |
537 | wants_write = rustls_connection_wants_write(rconn); |
538 | DEBUGASSERT(wants_read || wants_write); |
539 | writefd = wants_write?sockfd:CURL_SOCKET_BAD; |
540 | readfd = wants_read?sockfd:CURL_SOCKET_BAD; |
541 | |
542 | what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, 0); |
543 | if(what < 0) { |
544 | /* fatal error */ |
545 | failf(data, "select/poll on SSL socket, errno: %d" , SOCKERRNO); |
546 | return CURLE_SSL_CONNECT_ERROR; |
547 | } |
548 | if(0 == what) { |
549 | infof(data, "Curl_socket_check: %s would block" , |
550 | wants_read&&wants_write ? "writing and reading" : |
551 | wants_write ? "writing" : "reading" ); |
552 | *done = FALSE; |
553 | return CURLE_OK; |
554 | } |
555 | /* socket is readable or writable */ |
556 | |
557 | if(wants_write) { |
558 | infof(data, "rustls_connection wants us to write_tls." ); |
559 | cr_send(cf, data, NULL, 0, &tmperr); |
560 | if(tmperr == CURLE_AGAIN) { |
561 | infof(data, "writing would block" ); |
562 | /* fall through */ |
563 | } |
564 | else if(tmperr != CURLE_OK) { |
565 | return tmperr; |
566 | } |
567 | } |
568 | |
569 | if(wants_read) { |
570 | infof(data, "rustls_connection wants us to read_tls." ); |
571 | |
572 | if(tls_recv_more(cf, data, &tmperr) < 0) { |
573 | if(tmperr == CURLE_AGAIN) { |
574 | infof(data, "reading would block" ); |
575 | /* fall through */ |
576 | } |
577 | else if(tmperr == CURLE_READ_ERROR) { |
578 | return CURLE_SSL_CONNECT_ERROR; |
579 | } |
580 | else { |
581 | return tmperr; |
582 | } |
583 | } |
584 | } |
585 | } |
586 | |
587 | /* We should never fall through the loop. We should return either because |
588 | the handshake is done or because we can't read/write without blocking. */ |
589 | DEBUGASSERT(false); |
590 | } |
591 | |
592 | /* returns a bitmap of flags for this connection's first socket indicating |
593 | whether we want to read or write */ |
594 | static int |
595 | cr_get_select_socks(struct Curl_cfilter *cf, struct Curl_easy *data, |
596 | curl_socket_t *socks) |
597 | { |
598 | struct ssl_connect_data *const connssl = cf->ctx; |
599 | curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data); |
600 | struct rustls_ssl_backend_data *const backend = |
601 | (struct rustls_ssl_backend_data *)connssl->backend; |
602 | struct rustls_connection *rconn = NULL; |
603 | |
604 | (void)data; |
605 | DEBUGASSERT(backend); |
606 | rconn = backend->conn; |
607 | |
608 | if(rustls_connection_wants_write(rconn)) { |
609 | socks[0] = sockfd; |
610 | return GETSOCK_WRITESOCK(0); |
611 | } |
612 | if(rustls_connection_wants_read(rconn)) { |
613 | socks[0] = sockfd; |
614 | return GETSOCK_READSOCK(0); |
615 | } |
616 | |
617 | return GETSOCK_BLANK; |
618 | } |
619 | |
620 | static void * |
621 | cr_get_internals(struct ssl_connect_data *connssl, |
622 | CURLINFO info UNUSED_PARAM) |
623 | { |
624 | struct rustls_ssl_backend_data *backend = |
625 | (struct rustls_ssl_backend_data *)connssl->backend; |
626 | DEBUGASSERT(backend); |
627 | return &backend->conn; |
628 | } |
629 | |
630 | static void |
631 | cr_close(struct Curl_cfilter *cf, struct Curl_easy *data) |
632 | { |
633 | struct ssl_connect_data *connssl = cf->ctx; |
634 | struct rustls_ssl_backend_data *backend = |
635 | (struct rustls_ssl_backend_data *)connssl->backend; |
636 | CURLcode tmperr = CURLE_OK; |
637 | ssize_t n = 0; |
638 | |
639 | DEBUGASSERT(backend); |
640 | |
641 | if(backend->conn) { |
642 | rustls_connection_send_close_notify(backend->conn); |
643 | n = cr_send(cf, data, NULL, 0, &tmperr); |
644 | if(n < 0) { |
645 | failf(data, "rustls: error sending close_notify: %d" , tmperr); |
646 | } |
647 | |
648 | rustls_connection_free(backend->conn); |
649 | backend->conn = NULL; |
650 | } |
651 | if(backend->config) { |
652 | rustls_client_config_free(backend->config); |
653 | backend->config = NULL; |
654 | } |
655 | } |
656 | |
657 | static size_t cr_version(char *buffer, size_t size) |
658 | { |
659 | struct rustls_str ver = rustls_version(); |
660 | return msnprintf(buffer, size, "%.*s" , (int)ver.len, ver.data); |
661 | } |
662 | |
663 | const struct Curl_ssl Curl_ssl_rustls = { |
664 | { CURLSSLBACKEND_RUSTLS, "rustls" }, |
665 | SSLSUPP_CAINFO_BLOB | /* supports */ |
666 | SSLSUPP_TLS13_CIPHERSUITES | |
667 | SSLSUPP_HTTPS_PROXY, |
668 | sizeof(struct rustls_ssl_backend_data), |
669 | |
670 | Curl_none_init, /* init */ |
671 | Curl_none_cleanup, /* cleanup */ |
672 | cr_version, /* version */ |
673 | Curl_none_check_cxn, /* check_cxn */ |
674 | Curl_none_shutdown, /* shutdown */ |
675 | cr_data_pending, /* data_pending */ |
676 | Curl_none_random, /* random */ |
677 | Curl_none_cert_status_request, /* cert_status_request */ |
678 | cr_connect, /* connect */ |
679 | cr_connect_nonblocking, /* connect_nonblocking */ |
680 | cr_get_select_socks, /* get_select_socks */ |
681 | cr_get_internals, /* get_internals */ |
682 | cr_close, /* close_one */ |
683 | Curl_none_close_all, /* close_all */ |
684 | Curl_none_session_free, /* session_free */ |
685 | Curl_none_set_engine, /* set_engine */ |
686 | Curl_none_set_engine_default, /* set_engine_default */ |
687 | Curl_none_engines_list, /* engines_list */ |
688 | Curl_none_false_start, /* false_start */ |
689 | NULL, /* sha256sum */ |
690 | NULL, /* associate_connection */ |
691 | NULL, /* disassociate_connection */ |
692 | NULL, /* free_multi_ssl_backend_data */ |
693 | cr_recv, /* recv decrypted data */ |
694 | cr_send, /* send data to encrypt */ |
695 | }; |
696 | |
697 | #endif /* USE_RUSTLS */ |
698 | |