1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 2020 - 2022, Jacob Hoffman-Andrews,
9 * <github@hoffman-andrews.com>
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.
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.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 * SPDX-License-Identifier: curl
24 ***************************************************************************/
25 #include "curl_setup.h"
29 #include "curl_printf.h"
34 #include "inet_pton.h"
42 struct ssl_backend_data
44 const struct rustls_client_config *config;
45 struct rustls_connection *conn;
49 /* For a given rustls_result error code, return the best-matching CURLcode. */
50 static CURLcode map_error(rustls_result r)
52 if(rustls_result_is_cert_error(r)) {
53 return CURLE_PEER_FAILED_VERIFICATION;
56 case RUSTLS_RESULT_OK:
58 case RUSTLS_RESULT_NULL_PARAMETER:
59 return CURLE_BAD_FUNCTION_ARGUMENT;
61 return CURLE_READ_ERROR;
66 cr_data_pending(const struct connectdata *conn, int sockindex)
68 const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
69 struct ssl_backend_data *backend = connssl->backend;
71 return backend->data_pending;
75 cr_connect(struct Curl_easy *data UNUSED_PARAM,
76 struct connectdata *conn UNUSED_PARAM,
77 int sockindex UNUSED_PARAM)
79 infof(data, "rustls_connect: unimplemented");
80 return CURLE_SSL_CONNECT_ERROR;
84 read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
86 ssize_t n = sread(*(int *)userdata, buf, len);
95 write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n)
97 ssize_t n = swrite(*(int *)userdata, buf, len);
107 * - Read a chunk of bytes from the socket into rustls' TLS input buffer.
108 * - Tell rustls to process any new packets.
109 * - Read out as many plaintext bytes from rustls as possible, until hitting
110 * error, EOF, or EAGAIN/EWOULDBLOCK, or plainbuf/plainlen is filled up.
112 * It's okay to call this function with plainbuf == NULL and plainlen == 0.
113 * In that case, it will copy bytes from the socket into rustls' TLS input
114 * buffer, and process packets, but won't consume bytes from rustls' plaintext
118 cr_recv(struct Curl_easy *data, int sockindex,
119 char *plainbuf, size_t plainlen, CURLcode *err)
121 struct connectdata *conn = data->conn;
122 struct ssl_connect_data *const connssl = &conn->ssl[sockindex];
123 struct ssl_backend_data *const backend = connssl->backend;
124 struct rustls_connection *rconn = NULL;
127 size_t tls_bytes_read = 0;
128 size_t plain_bytes_copied = 0;
129 rustls_result rresult = 0;
131 rustls_io_result io_error;
133 DEBUGASSERT(backend);
134 rconn = backend->conn;
136 io_error = rustls_connection_read_tls(rconn, read_cb,
137 &conn->sock[sockindex], &tls_bytes_read);
138 if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
139 infof(data, "sread: EAGAIN or EWOULDBLOCK");
142 char buffer[STRERROR_LEN];
143 failf(data, "reading from socket: %s",
144 Curl_strerror(io_error, buffer, sizeof(buffer)));
145 *err = CURLE_READ_ERROR;
149 infof(data, "cr_recv read %ld bytes from the network", tls_bytes_read);
151 rresult = rustls_connection_process_new_packets(rconn);
152 if(rresult != RUSTLS_RESULT_OK) {
153 rustls_error(rresult, errorbuf, sizeof(errorbuf), &n);
154 failf(data, "%.*s", n, errorbuf);
155 *err = map_error(rresult);
159 backend->data_pending = TRUE;
161 while(plain_bytes_copied < plainlen) {
162 rresult = rustls_connection_read(rconn,
163 (uint8_t *)plainbuf + plain_bytes_copied,
164 plainlen - plain_bytes_copied,
166 if(rresult == RUSTLS_RESULT_PLAINTEXT_EMPTY) {
167 infof(data, "cr_recv got PLAINTEXT_EMPTY. will try again later.");
168 backend->data_pending = FALSE;
171 else if(rresult != RUSTLS_RESULT_OK) {
172 /* n always equals 0 in this case, don't need to check it */
173 failf(data, "error in rustls_connection_read: %d", rresult);
174 *err = CURLE_READ_ERROR;
178 /* n == 0 indicates clean EOF, but we may have read some other
179 plaintext bytes before we reached this. Break out of the loop
180 so we can figure out whether to return success or EOF. */
184 infof(data, "cr_recv copied out %ld bytes of plaintext", n);
185 plain_bytes_copied += n;
189 if(plain_bytes_copied) {
191 return plain_bytes_copied;
194 /* If we wrote out 0 plaintext bytes, that means either we hit a clean EOF,
195 OR we got a RUSTLS_RESULT_PLAINTEXT_EMPTY.
196 If the latter, return CURLE_AGAIN so curl doesn't treat this as EOF. */
197 if(!backend->data_pending) {
202 /* Zero bytes read, and no RUSTLS_RESULT_PLAINTEXT_EMPTY, means the TCP
203 connection was cleanly closed (with a close_notify alert). */
210 * - Copy `plainlen` bytes into rustls' plaintext input buffer (if > 0).
211 * - Fully drain rustls' plaintext output buffer into the socket until
212 * we get either an error or EAGAIN/EWOULDBLOCK.
214 * It's okay to call this function with plainbuf == NULL and plainlen == 0.
215 * In that case, it won't read anything into rustls' plaintext input buffer.
216 * It will only drain rustls' plaintext output buffer into the socket.
219 cr_send(struct Curl_easy *data, int sockindex,
220 const void *plainbuf, size_t plainlen, CURLcode *err)
222 struct connectdata *conn = data->conn;
223 struct ssl_connect_data *const connssl = &conn->ssl[sockindex];
224 struct ssl_backend_data *const backend = connssl->backend;
225 struct rustls_connection *rconn = NULL;
226 size_t plainwritten = 0;
227 size_t tlswritten = 0;
228 size_t tlswritten_total = 0;
229 rustls_result rresult;
230 rustls_io_result io_error;
232 DEBUGASSERT(backend);
233 rconn = backend->conn;
235 infof(data, "cr_send %ld bytes of plaintext", plainlen);
238 rresult = rustls_connection_write(rconn, plainbuf, plainlen,
240 if(rresult != RUSTLS_RESULT_OK) {
241 failf(data, "error in rustls_connection_write");
242 *err = CURLE_WRITE_ERROR;
245 else if(plainwritten == 0) {
246 failf(data, "EOF in rustls_connection_write");
247 *err = CURLE_WRITE_ERROR;
252 while(rustls_connection_wants_write(rconn)) {
253 io_error = rustls_connection_write_tls(rconn, write_cb,
254 &conn->sock[sockindex], &tlswritten);
255 if(io_error == EAGAIN || io_error == EWOULDBLOCK) {
256 infof(data, "swrite: EAGAIN after %ld bytes", tlswritten_total);
261 char buffer[STRERROR_LEN];
262 failf(data, "writing to socket: %s",
263 Curl_strerror(io_error, buffer, sizeof(buffer)));
264 *err = CURLE_WRITE_ERROR;
267 if(tlswritten == 0) {
268 failf(data, "EOF in swrite");
269 *err = CURLE_WRITE_ERROR;
272 infof(data, "cr_send wrote %ld bytes to network", tlswritten);
273 tlswritten_total += tlswritten;
279 /* A server certificate verify callback for rustls that always returns
280 RUSTLS_RESULT_OK, or in other words disable certificate verification. */
281 static enum rustls_result
282 cr_verify_none(void *userdata UNUSED_PARAM,
283 const rustls_verify_server_cert_params *params UNUSED_PARAM)
285 return RUSTLS_RESULT_OK;
289 cr_hostname_is_ip(const char *hostname)
294 if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) {
297 #endif /* ENABLE_IPV6 */
298 if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
305 cr_init_backend(struct Curl_easy *data, struct connectdata *conn,
306 struct ssl_backend_data *const backend)
308 struct rustls_connection *rconn = NULL;
309 struct rustls_client_config_builder *config_builder = NULL;
310 struct rustls_root_cert_store *roots = NULL;
311 const struct curl_blob *ca_info_blob = SSL_CONN_CONFIG(ca_info_blob);
312 const char * const ssl_cafile =
313 /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
314 (ca_info_blob ? NULL : SSL_CONN_CONFIG(CAfile));
315 const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
316 const char *hostname = conn->host.name;
320 rustls_slice_bytes alpn[2] = {
321 { (const uint8_t *)ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH },
322 { (const uint8_t *)ALPN_H2, ALPN_H2_LENGTH },
325 DEBUGASSERT(backend);
326 rconn = backend->conn;
328 config_builder = rustls_client_config_builder_new();
330 infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
331 rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 2);
333 rustls_client_config_builder_set_alpn_protocols(config_builder, alpn, 1);
335 infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
337 rustls_client_config_builder_dangerous_set_certificate_verifier(
338 config_builder, cr_verify_none);
339 /* rustls doesn't support IP addresses (as of 0.19.0), and will reject
340 * connections created with an IP address, even when certificate
341 * verification is turned off. Set a placeholder hostname and disable
343 if(cr_hostname_is_ip(hostname)) {
344 rustls_client_config_builder_set_enable_sni(config_builder, false);
345 hostname = "example.invalid";
348 else if(ca_info_blob) {
349 roots = rustls_root_cert_store_new();
351 /* Enable strict parsing only if verification isn't disabled. */
352 result = rustls_root_cert_store_add_pem(roots, ca_info_blob->data,
353 ca_info_blob->len, verifypeer);
354 if(result != RUSTLS_RESULT_OK) {
355 failf(data, "failed to parse trusted certificates from blob");
356 rustls_root_cert_store_free(roots);
357 rustls_client_config_free(
358 rustls_client_config_builder_build(config_builder));
359 return CURLE_SSL_CACERT_BADFILE;
362 result = rustls_client_config_builder_use_roots(config_builder, roots);
363 rustls_root_cert_store_free(roots);
364 if(result != RUSTLS_RESULT_OK) {
365 failf(data, "failed to load trusted certificates");
366 rustls_client_config_free(
367 rustls_client_config_builder_build(config_builder));
368 return CURLE_SSL_CACERT_BADFILE;
371 else if(ssl_cafile) {
372 result = rustls_client_config_builder_load_roots_from_file(
373 config_builder, ssl_cafile);
374 if(result != RUSTLS_RESULT_OK) {
375 failf(data, "failed to load trusted certificates");
376 rustls_client_config_free(
377 rustls_client_config_builder_build(config_builder));
378 return CURLE_SSL_CACERT_BADFILE;
382 backend->config = rustls_client_config_builder_build(config_builder);
383 DEBUGASSERT(rconn == NULL);
385 char *snihost = Curl_ssl_snihost(data, hostname, NULL);
387 failf(data, "Failed to set SNI");
388 return CURLE_SSL_CONNECT_ERROR;
390 result = rustls_client_connection_new(backend->config, snihost, &rconn);
392 if(result != RUSTLS_RESULT_OK) {
393 rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen);
394 failf(data, "rustls_client_connection_new: %.*s", errorlen, errorbuf);
395 return CURLE_COULDNT_CONNECT;
397 rustls_connection_set_userdata(rconn, backend);
398 backend->conn = rconn;
403 cr_set_negotiated_alpn(struct Curl_easy *data, struct connectdata *conn,
404 const struct rustls_connection *rconn)
406 const uint8_t *protocol = NULL;
409 rustls_connection_get_alpn_protocol(rconn, &protocol, &len);
411 infof(data, VTLS_INFOF_NO_ALPN);
416 if(len == ALPN_H2_LENGTH && 0 == memcmp(ALPN_H2, protocol, len)) {
417 infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_H2);
418 conn->alpn = CURL_HTTP_VERSION_2;
422 if(len == ALPN_HTTP_1_1_LENGTH &&
423 0 == memcmp(ALPN_HTTP_1_1, protocol, len)) {
424 infof(data, VTLS_INFOF_ALPN_ACCEPTED_1STR, ALPN_HTTP_1_1);
425 conn->alpn = CURL_HTTP_VERSION_1_1;
428 infof(data, "ALPN, negotiated an unrecognized protocol");
431 Curl_multiuse_state(data, conn->alpn == CURL_HTTP_VERSION_2 ?
432 BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
436 cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn,
437 int sockindex, bool *done)
439 struct ssl_connect_data *const connssl = &conn->ssl[sockindex];
440 curl_socket_t sockfd = conn->sock[sockindex];
441 struct ssl_backend_data *const backend = connssl->backend;
442 struct rustls_connection *rconn = NULL;
443 CURLcode tmperr = CURLE_OK;
448 curl_socket_t writefd;
449 curl_socket_t readfd;
451 DEBUGASSERT(backend);
453 if(ssl_connection_none == connssl->state) {
454 result = cr_init_backend(data, conn, connssl->backend);
455 if(result != CURLE_OK) {
458 connssl->state = ssl_connection_negotiating;
461 rconn = backend->conn;
463 /* Read/write data until the handshake is done or the socket would block. */
466 * Connection has been established according to rustls. Set send/recv
467 * handlers, and update the state machine.
469 if(!rustls_connection_is_handshaking(rconn)) {
470 infof(data, "Done handshaking");
471 /* Done with the handshake. Set up callbacks to send/receive data. */
472 connssl->state = ssl_connection_complete;
474 cr_set_negotiated_alpn(data, conn, rconn);
476 conn->recv[sockindex] = cr_recv;
477 conn->send[sockindex] = cr_send;
482 wants_read = rustls_connection_wants_read(rconn);
483 wants_write = rustls_connection_wants_write(rconn);
484 DEBUGASSERT(wants_read || wants_write);
485 writefd = wants_write?sockfd:CURL_SOCKET_BAD;
486 readfd = wants_read?sockfd:CURL_SOCKET_BAD;
488 what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, 0);
491 failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
492 return CURLE_SSL_CONNECT_ERROR;
495 infof(data, "Curl_socket_check: %s would block",
496 wants_read&&wants_write ? "writing and reading" :
497 wants_write ? "writing" : "reading");
501 /* socket is readable or writable */
504 infof(data, "rustls_connection wants us to write_tls.");
505 cr_send(data, sockindex, NULL, 0, &tmperr);
506 if(tmperr == CURLE_AGAIN) {
507 infof(data, "writing would block");
510 else if(tmperr != CURLE_OK) {
516 infof(data, "rustls_connection wants us to read_tls.");
518 cr_recv(data, sockindex, NULL, 0, &tmperr);
519 if(tmperr == CURLE_AGAIN) {
520 infof(data, "reading would block");
523 else if(tmperr != CURLE_OK) {
524 if(tmperr == CURLE_READ_ERROR) {
525 return CURLE_SSL_CONNECT_ERROR;
534 /* We should never fall through the loop. We should return either because
535 the handshake is done or because we can't read/write without blocking. */
539 /* returns a bitmap of flags for this connection's first socket indicating
540 whether we want to read or write */
542 cr_getsock(struct connectdata *conn, curl_socket_t *socks)
544 struct ssl_connect_data *const connssl = &conn->ssl[FIRSTSOCKET];
545 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
546 struct ssl_backend_data *const backend = connssl->backend;
547 struct rustls_connection *rconn = NULL;
549 DEBUGASSERT(backend);
550 rconn = backend->conn;
552 if(rustls_connection_wants_write(rconn)) {
554 return GETSOCK_WRITESOCK(0);
556 if(rustls_connection_wants_read(rconn)) {
558 return GETSOCK_READSOCK(0);
561 return GETSOCK_BLANK;
565 cr_get_internals(struct ssl_connect_data *connssl,
566 CURLINFO info UNUSED_PARAM)
568 struct ssl_backend_data *backend = connssl->backend;
569 DEBUGASSERT(backend);
570 return &backend->conn;
574 cr_close(struct Curl_easy *data, struct connectdata *conn,
577 struct ssl_connect_data *connssl = &conn->ssl[sockindex];
578 struct ssl_backend_data *backend = connssl->backend;
579 CURLcode tmperr = CURLE_OK;
582 DEBUGASSERT(backend);
585 rustls_connection_send_close_notify(backend->conn);
586 n = cr_send(data, sockindex, NULL, 0, &tmperr);
588 failf(data, "error sending close notify: %d", tmperr);
591 rustls_connection_free(backend->conn);
592 backend->conn = NULL;
594 if(backend->config) {
595 rustls_client_config_free(backend->config);
596 backend->config = NULL;
600 static size_t cr_version(char *buffer, size_t size)
602 struct rustls_str ver = rustls_version();
603 return msnprintf(buffer, size, "%.*s", (int)ver.len, ver.data);
606 const struct Curl_ssl Curl_ssl_rustls = {
607 { CURLSSLBACKEND_RUSTLS, "rustls" },
608 SSLSUPP_CAINFO_BLOB | /* supports */
609 SSLSUPP_TLS13_CIPHERSUITES,
610 sizeof(struct ssl_backend_data),
612 Curl_none_init, /* init */
613 Curl_none_cleanup, /* cleanup */
614 cr_version, /* version */
615 Curl_none_check_cxn, /* check_cxn */
616 Curl_none_shutdown, /* shutdown */
617 cr_data_pending, /* data_pending */
618 Curl_none_random, /* random */
619 Curl_none_cert_status_request, /* cert_status_request */
620 cr_connect, /* connect */
621 cr_connect_nonblocking, /* connect_nonblocking */
622 cr_getsock, /* cr_getsock */
623 cr_get_internals, /* get_internals */
624 cr_close, /* close_one */
625 Curl_none_close_all, /* close_all */
626 Curl_none_session_free, /* session_free */
627 Curl_none_set_engine, /* set_engine */
628 Curl_none_set_engine_default, /* set_engine_default */
629 Curl_none_engines_list, /* engines_list */
630 Curl_none_false_start, /* false_start */
631 NULL, /* sha256sum */
632 NULL, /* associate_connection */
633 NULL /* disassociate_connection */
636 #endif /* USE_RUSTLS */