VirtualBox

source: vbox/trunk/src/libs/curl-7.83.1/lib/vtls/rustls.c@ 95312

Last change on this file since 95312 was 95312, checked in by vboxsync, 3 years ago

libs/{curl,libxml2}: OSE export fixes, bugref:8515

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette