diff options
-rwxr-xr-x | configure.ac | 97 | ||||
-rw-r--r-- | docs/FAQ | 4 | ||||
-rw-r--r-- | docs/libcurl/symbols-in-versions | 1 | ||||
-rw-r--r-- | include/curl/curl.h | 3 | ||||
-rw-r--r-- | lib/Makefile.inc | 2 | ||||
-rw-r--r-- | lib/curl_setup.h | 2 | ||||
-rw-r--r-- | lib/http.c | 2 | ||||
-rw-r--r-- | lib/multi.c | 2 | ||||
-rw-r--r-- | lib/vtls/rustls.c | 496 | ||||
-rw-r--r-- | lib/vtls/rustls.h | 32 | ||||
-rw-r--r-- | lib/vtls/vtls.c | 5 | ||||
-rw-r--r-- | lib/vtls/vtls.h | 1 |
12 files changed, 639 insertions, 8 deletions
diff --git a/configure.ac b/configure.ac index b7aef5ba3..cf4b5278f 100755 --- a/configure.ac +++ b/configure.ac @@ -143,7 +143,7 @@ AC_SUBST(PKGADD_VENDOR) dnl dnl initialize all the info variables - curl_ssl_msg="no (--with-{ssl,gnutls,nss,mbedtls,wolfssl,schannel,secure-transport,mesalink,amissl,bearssl} )" + curl_ssl_msg="no (--with-{ssl,gnutls,nss,mbedtls,wolfssl,schannel,secure-transport,mesalink,amissl,bearssl,rustls} )" curl_ssh_msg="no (--with-{libssh,libssh2})" curl_zlib_msg="no (--with-zlib)" curl_brotli_msg="no (--with-brotli)" @@ -2699,6 +2699,98 @@ if test -z "$ssl_backends" -o "x$OPT_BEARSSL" != xno; then fi dnl ---------------------------------------------------- +dnl check for rustls +dnl ---------------------------------------------------- + +OPT_RUSTLS=no + +_cppflags=$CPPFLAGS +_ldflags=$LDFLAGS +AC_ARG_WITH(rustls,dnl +AC_HELP_STRING([--with-rustls=PATH],[where to look for rustls, PATH points to the installation root]) +AC_HELP_STRING([--without-rustls], [disable rustls detection]), + OPT_RUSTLS=$withval) + +if test -z "$ssl_backends" -o "x$OPT_RUSTLS" != xno; then + ssl_msg= + + if test X"$OPT_RUSTLS" != Xno; then + + if test "$OPT_RUSTLS" = "yes"; then + OPT_RUSTLS="" + fi + + if test -z "$OPT_RUSTLS" ; then + dnl check for lib first without setting any new path + + AC_CHECK_LIB(crustls, rustls_client_session_read, + dnl libcrustls found, set the variable + [ + AC_DEFINE(USE_RUSTLS, 1, [if rustls is enabled]) + AC_SUBST(USE_RUSTLS, [1]) + RUSTLS_ENABLED=1 + USE_RUSTLS="yes" + ssl_msg="rustls" + test rustls != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes + ], [], -lpthread -ldl) + fi + + addld="" + addlib="-lpthread" + addcflags="" + bearssllib="" + + if test "x$USE_RUSTLS" != "xyes"; then + dnl add the path and test again + addld=-L$OPT_RUSTLS/lib$libsuff + addcflags=-I$OPT_RUSTLS/include + rustlslib=$OPT_RUSTLS/lib$libsuff + + LDFLAGS="$LDFLAGS $addld" + if test "$addcflags" != "-I/usr/include"; then + CPPFLAGS="$CPPFLAGS $addcflags" + fi + + AC_CHECK_LIB(crustls, rustls_client_session_read, + [ + AC_DEFINE(USE_RUSTLS, 1, [if rustls is enabled]) + AC_SUBST(USE_RUSTLS, [1]) + RUSTLS_ENABLED=1 + USE_RUSTLS="yes" + ssl_msg="rustls" + test rustls != "$DEFAULT_SSL_BACKEND" || VALID_DEFAULT_SSL_BACKEND=yes + ], + [ + CPPFLAGS=$_cppflags + LDFLAGS=$_ldflags + ], -lpthread -ldl) + fi + + if test "x$USE_RUSTLS" = "xyes"; then + AC_MSG_NOTICE([detected rustls]) + check_for_ca_bundle=1 + + LIBS="-lcrustls -lpthread -ldl $LIBS" + + if test -n "$rustlslib"; then + dnl when shared libs were found in a path that the run-time + dnl linker doesn't search through, we need to add it to + dnl CURL_LIBRARY_PATH to prevent further configure tests to fail + dnl due to this + if test "x$cross_compiling" != "xyes"; then + CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$rustlslib" + export CURL_LIBRARY_PATH + AC_MSG_NOTICE([Added $rustlslib to CURL_LIBRARY_PATH]) + fi + fi + fi + + fi dnl rustls not disabled + + test -z "$ssl_msg" || ssl_backends="${ssl_backends:+$ssl_backends, }$ssl_msg" +fi + +dnl ---------------------------------------------------- dnl NSS. Only check if GnuTLS and OpenSSL are not enabled dnl ---------------------------------------------------- @@ -2828,7 +2920,8 @@ if test -z "$ssl_backends" -o "x$OPT_NSS" != xno; then test -z "$ssl_msg" || ssl_backends="${ssl_backends:+$ssl_backends, }$ssl_msg" fi -case "x$OPENSSL_ENABLED$GNUTLS_ENABLED$NSS_ENABLED$MBEDTLS_ENABLED$WOLFSSL_ENABLED$SCHANNEL_ENABLED$SECURETRANSPORT_ENABLED$MESALINK_ENABLED$BEARSSL_ENABLED$AMISSL_ENABLED" in +case "x$OPENSSL_ENABLED$GNUTLS_ENABLED$NSS_ENABLED$MBEDTLS_ENABLED$WOLFSSL_ENABLED$SCHANNEL_ENABLED$SECURETRANSPORT_ENABLED$MESALINK_ENABLED$BEARSSL_ENABLED$AMISSL_ENABLED$RUSTLS_ENABLED" +in x) AC_MSG_WARN([SSL disabled, you will not be able to use HTTPS, FTPS, NTLM and more.]) AC_MSG_WARN([Use --with-ssl, --with-gnutls, --with-wolfssl, --with-mbedtls, --with-nss, --with-schannel, --with-secure-transport, --with-mesalink, --with-amissl or --with-bearssl to address this.]) @@ -417,8 +417,8 @@ FAQ curl can be built to use one of the following SSL alternatives: OpenSSL, libressl, BoringSSL, GnuTLS, wolfSSL, NSS, mbedTLS, MesaLink, Secure Transport (native iOS/OS X), Schannel (native Windows), GSKit (native IBM - i), or BearSSL. They all have their pros and cons, and we try to maintain a - comparison of them here: https://curl.se/docs/ssl-compared.html + i), BearSSL, or Rustls. They all have their pros and cons, and we try to + maintain a comparison of them here: https://curl.se/docs/ssl-compared.html 2.4 Does curl support SOCKS (RFC 1928) ? diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 0b633e41c..66c36c4ce 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -806,6 +806,7 @@ CURLSSLBACKEND_NONE 7.34.0 CURLSSLBACKEND_NSS 7.34.0 CURLSSLBACKEND_OPENSSL 7.34.0 CURLSSLBACKEND_POLARSSL 7.34.0 7.69.0 +CURLSSLBACKEND_RUSTLS 7.76.0 CURLSSLBACKEND_QSOSSL 7.34.0 - 7.38.1 CURLSSLBACKEND_SCHANNEL 7.34.0 CURLSSLBACKEND_SECURETRANSPORT 7.64.1 diff --git a/include/curl/curl.h b/include/curl/curl.h index 71204ee32..46503178d 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -155,7 +155,8 @@ typedef enum { CURLSSLBACKEND_AXTLS = 10, /* never used since 7.63.0 */ CURLSSLBACKEND_MBEDTLS = 11, CURLSSLBACKEND_MESALINK = 12, - CURLSSLBACKEND_BEARSSL = 13 + CURLSSLBACKEND_BEARSSL = 13, + CURLSSLBACKEND_RUSTLS = 14 } curl_sslbackend; /* aliases for library clones and renames */ diff --git a/lib/Makefile.inc b/lib/Makefile.inc index dddb4a491..90ec6aa05 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -50,6 +50,7 @@ LIB_VTLS_CFILES = \ vtls/mesalink.c \ vtls/nss.c \ vtls/openssl.c \ + vtls/rustls.c \ vtls/schannel.c \ vtls/schannel_verify.c \ vtls/sectransp.c \ @@ -66,6 +67,7 @@ LIB_VTLS_HFILES = \ vtls/mesalink.h \ vtls/nssg.h \ vtls/openssl.h \ + vtls/rustls.h \ vtls/schannel.h \ vtls/sectransp.h \ vtls/vtls.h \ diff --git a/lib/curl_setup.h b/lib/curl_setup.h index 9cef5f7d0..6c9e2d738 100644 --- a/lib/curl_setup.h +++ b/lib/curl_setup.h @@ -616,7 +616,7 @@ int netware_init(void); defined(USE_MBEDTLS) || \ defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \ defined(USE_SECTRANSP) || defined(USE_GSKIT) || defined(USE_MESALINK) || \ - defined(USE_BEARSSL) + defined(USE_BEARSSL) || defined(USE_RUSTLS) #define USE_SSL /* SSL support has been enabled */ #endif diff --git a/lib/http.c b/lib/http.c index 72aa96297..806f639dc 100644 --- a/lib/http.c +++ b/lib/http.c @@ -1552,7 +1552,7 @@ static int https_getsock(struct Curl_easy *data, { (void)data; if(conn->handler->flags & PROTOPT_SSL) - return Curl_ssl_getsock(conn, socks); + return Curl_ssl->getsock(conn, socks); return GETSOCK_BLANK; } #endif /* USE_SSL */ diff --git a/lib/multi.c b/lib/multi.c index 85707a1af..e0862551d 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -905,7 +905,7 @@ static int waitconnect_getsock(struct connectdata *conn, #ifdef USE_SSL #ifndef CURL_DISABLE_PROXY if(CONNECT_FIRSTSOCKET_PROXY_SSL()) - return Curl_ssl_getsock(conn, sock); + return Curl_ssl->getsock(conn, sock); #endif #endif diff --git a/lib/vtls/rustls.c b/lib/vtls/rustls.c new file mode 100644 index 000000000..7e00ce7c0 --- /dev/null +++ b/lib/vtls/rustls.c @@ -0,0 +1,496 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2020, Jacob Hoffman-Andrews, <github@hoffman-andrews.com> + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#include "curl_setup.h" + +#ifdef USE_RUSTLS + +#include "curl_printf.h" + +#include <errno.h> +#include <crustls.h> + +#include "urldata.h" +#include "sendf.h" +#include "vtls.h" +#include "select.h" + +#include "multiif.h" + +struct ssl_backend_data +{ + const struct rustls_client_config *config; + struct rustls_client_session *session; + bool data_pending; +}; + +/* For a given rustls_result error code, return the best-matching CURLcode. */ +static CURLcode map_error(rustls_result r) +{ + if(rustls_result_is_cert_error(r)) { + return CURLE_PEER_FAILED_VERIFICATION; + } + switch(r) { + case RUSTLS_RESULT_OK: + return CURLE_OK; + case RUSTLS_RESULT_NULL_PARAMETER: + return CURLE_BAD_FUNCTION_ARGUMENT; + default: + return CURLE_READ_ERROR; + } +} + +static bool +cr_data_pending(const struct connectdata *conn, int sockindex) +{ + struct Curl_easy *data = conn->data; + const struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + infof(data, "rustls_data_pending %d\n", backend->data_pending); + return backend->data_pending; +} + +static CURLcode +cr_connect(struct Curl_easy *data UNUSED_PARAM, + struct connectdata *conn UNUSED_PARAM, + int sockindex UNUSED_PARAM) +{ + infof(data, "rustls_connect: unimplemented\n"); + return CURLE_SSL_CONNECT_ERROR; +} + +/* + * On each run: + * - Read a chunk of bytes from the socket into rustls' TLS input buffer. + * - Tell rustls to process any new packets. + * - Read out as many plaintext bytes from rustls as possible, until hitting + * error, EOF, or EAGAIN/EWOULDBLOCK, or plainbuf/plainlen is filled up. + * + * It's okay to call this function with plainbuf == NULL and plainlen == 0. + * In that case, it will copy bytes from the socket into rustls' TLS input + * buffer, and process packets, but won't consume bytes from rustls' plaintext + * output buffer. + */ +static ssize_t +cr_recv(struct Curl_easy *data, int sockindex, + char *plainbuf, size_t plainlen, CURLcode *err) +{ + struct connectdata *conn = data->conn; + struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *const backend = connssl->backend; + struct rustls_client_session *const session = backend->session; + curl_socket_t sockfd = conn->sock[sockindex]; + /* Per https://www.bearssl.org/api1.html, max TLS record size plus max + per-record overhead. */ + uint8_t tlsbuf[16384 + 325]; + size_t n = 0; + ssize_t tls_bytes_read = 0; + size_t tls_bytes_processed = 0; + size_t plain_bytes_copied = 0; + rustls_result rresult = 0; + char errorbuf[255]; + + tls_bytes_read = sread(sockfd, tlsbuf, sizeof(tlsbuf)); + if(tls_bytes_read == 0) { + failf(data, "EOF in sread"); + *err = CURLE_READ_ERROR; + return -1; + } + else if(tls_bytes_read < 0) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { + infof(data, "sread: EAGAIN or EWOULDBLOCK\n"); + *err = CURLE_AGAIN; + return -1; + } + failf(data, "reading from socket: %s", strerror(SOCKERRNO)); + *err = CURLE_READ_ERROR; + return -1; + } + + /* + * Now pull those bytes from the buffer into ClientSession. + */ + DEBUGASSERT(tls_bytes_read > 0); + while(tls_bytes_processed < (size_t)tls_bytes_read) { + rresult = rustls_client_session_read_tls(session, + (uint8_t *)tlsbuf + tls_bytes_processed, + tls_bytes_read - tls_bytes_processed, + &n); + if(rresult != RUSTLS_RESULT_OK) { + failf(data, "error in rustls_client_session_read_tls"); + *err = CURLE_READ_ERROR; + return -1; + } + else if(n == 0) { + infof(data, "EOF from rustls_client_session_read_tls\n"); + break; + } + + rresult = rustls_client_session_process_new_packets(session); + if(rresult != RUSTLS_RESULT_OK) { + rustls_error(rresult, errorbuf, sizeof(errorbuf), &n); + failf(data, "%.*s", n, errorbuf); + *err = map_error(rresult); + return -1; + } + + tls_bytes_processed += n; + backend->data_pending = TRUE; + } + + while(plain_bytes_copied < plainlen) { + rresult = rustls_client_session_read(session, + (uint8_t *)plainbuf + plain_bytes_copied, + plainlen - plain_bytes_copied, + &n); + if(rresult != RUSTLS_RESULT_OK) { + failf(data, "error in rustls_client_session_read"); + *err = CURLE_READ_ERROR; + return -1; + } + else if(n == 0) { + /* rustls returns 0 from client_session_read to mean "all currently + available data has been read." If we bring in more ciphertext with + read_tls, more plaintext will become available. So don't tell curl + this is an EOF. Instead, say "come back later." */ + infof(data, "EOF from rustls_client_session_read\n"); + backend->data_pending = FALSE; + break; + } + else { + plain_bytes_copied += n; + } + } + + /* If we wrote out 0 plaintext bytes, it might just mean we haven't yet + read a full TLS record. Return CURLE_AGAIN so curl doesn't treat this + as EOF. */ + if(plain_bytes_copied == 0) { + *err = CURLE_AGAIN; + return -1; + } + + return plain_bytes_copied; +} + +/* + * On each call: + * - Copy `plainlen` bytes into rustls' plaintext input buffer (if > 0). + * - Fully drain rustls' plaintext output buffer into the socket until + * we get either an error or EAGAIN/EWOULDBLOCK. + * + * It's okay to call this function with plainbuf == NULL and plainlen == 0. + * In that case, it won't read anything into rustls' plaintext input buffer. + * It will only drain rustls' plaintext output buffer into the socket. + */ +static ssize_t +cr_send(struct Curl_easy *data, int sockindex, + const void *plainbuf, size_t plainlen, CURLcode *err) +{ + struct connectdata *conn = data->conn; + struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *const backend = connssl->backend; + struct rustls_client_session *const session = backend->session; + curl_socket_t sockfd = conn->sock[sockindex]; + ssize_t n = 0; + size_t plainwritten = 0; + size_t tlslen = 0; + size_t tlswritten = 0; + /* Max size of a TLS message, plus some space for TLS framing overhead. */ + uint8_t tlsbuf[16384 + 325]; + rustls_result rresult; + + if(plainlen > 0) { + rresult = rustls_client_session_write(session, + plainbuf, plainlen, &plainwritten); + if(rresult != RUSTLS_RESULT_OK) { + failf(data, "error in rustls_client_session_write"); + *err = CURLE_WRITE_ERROR; + return -1; + } + else if(plainwritten == 0) { + failf(data, "EOF in rustls_client_session_write"); + *err = CURLE_WRITE_ERROR; + return -1; + } + } + + while(rustls_client_session_wants_write(session)) { + rresult = rustls_client_session_write_tls( + session, tlsbuf, sizeof(tlsbuf), &tlslen); + if(rresult != RUSTLS_RESULT_OK) { + failf(data, "error in rustls_client_session_write_tls"); + *err = CURLE_WRITE_ERROR; + return -1; + } + else if(tlslen == 0) { + failf(data, "EOF in rustls_client_session_write_tls"); + *err = CURLE_WRITE_ERROR; + return -1; + } + + tlswritten = 0; + + while(tlswritten < tlslen) { + n = swrite(sockfd, tlsbuf + tlswritten, tlslen - tlswritten); + if(n < 0) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { + /* Since recv is called from poll, there should be room to + write at least some bytes before hitting EAGAIN. */ + infof(data, "swrite: EAGAIN after %ld bytes\n", tlswritten); + DEBUGASSERT(tlswritten > 0); + break; + } + failf(data, "error in swrite"); + *err = CURLE_WRITE_ERROR; + return -1; + } + if(n == 0) { + failf(data, "EOF in swrite"); + *err = CURLE_WRITE_ERROR; + return -1; + } + tlswritten += n; + } + + DEBUGASSERT(tlswritten <= tlslen); + } + + return plainwritten; +} + +static CURLcode +cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn, + int sockindex, bool *done) +{ + struct ssl_connect_data *const connssl = &conn->ssl[sockindex]; + curl_socket_t sockfd = conn->sock[sockindex]; + struct ssl_backend_data *const backend = connssl->backend; + struct rustls_client_session *session = backend->session; + struct rustls_client_config_builder *config_builder = NULL; + const char *const ssl_cafile = SSL_CONN_CONFIG(CAfile); + CURLcode tmperr = CURLE_OK; + int result; + int what; + bool wants_read; + bool wants_write; + curl_socket_t writefd; + curl_socket_t readfd; + char errorbuf[256]; + size_t errorlen; + + if(ssl_connection_none == connssl->state) { + config_builder = rustls_client_config_builder_new(); + if(ssl_cafile) { + result = rustls_client_config_builder_load_roots_from_file( + config_builder, ssl_cafile); + if(result != RUSTLS_RESULT_OK) { + failf(data, "failed to load trusted certificates"); + rustls_client_config_free( + rustls_client_config_builder_build(config_builder)); + return CURLE_SSL_CACERT_BADFILE; + } + } + else { + result = rustls_client_config_builder_load_native_roots(config_builder); + if(result != RUSTLS_RESULT_OK) { + failf(data, "failed to load trusted certificates"); + rustls_client_config_free( + rustls_client_config_builder_build(config_builder)); + return CURLE_SSL_CACERT_BADFILE; + } + } + + backend->config = rustls_client_config_builder_build(config_builder); + DEBUGASSERT(session == NULL); + result = rustls_client_session_new( + backend->config, conn->host.name, &session); + if(result != RUSTLS_RESULT_OK) { + rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen); + failf(data, "failed to create client session: %.*s", errorlen, errorbuf); + return CURLE_COULDNT_CONNECT; + } + backend->session = session; + connssl->state = ssl_connection_negotiating; + } + + /* Read/write data until the handshake is done or the socket would block. */ + for(;;) { + /* + * Connection has been established according to rustls. Set send/recv + * handlers, and update the state machine. + * This check has to come last because is_handshaking starts out false, + * then becomes true when we first write data, then becomes false again + * once the handshake is done. + */ + if(!rustls_client_session_is_handshaking(session)) { + infof(data, "Done handshaking\n"); + /* Done with the handshake. Set up callbacks to send/receive data. */ + connssl->state = ssl_connection_complete; + conn->recv[sockindex] = cr_recv; + conn->send[sockindex] = cr_send; + *done = TRUE; + return CURLE_OK; + } + + wants_read = rustls_client_session_wants_read(session); + wants_write = rustls_client_session_wants_write(session); + DEBUGASSERT(wants_read || wants_write); + writefd = wants_write?sockfd:CURL_SOCKET_BAD; + readfd = wants_read?sockfd:CURL_SOCKET_BAD; + + what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, 0); + if(what < 0) { + /* fatal error */ + failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO); + return CURLE_SSL_CONNECT_ERROR; + } + if(0 == what) { + infof(data, "Curl_socket_check: %s would block\n", + wants_read&&wants_write ? + "writing and reading" : + wants_write ? + "writing" : + "reading"); + *done = FALSE; + return CURLE_OK; + } + /* socket is readable or writable */ + + if(wants_write) { + infof(data, "ClientSession wants us to write_tls.\n"); + cr_send(data, sockindex, NULL, 0, &tmperr); + if(tmperr == CURLE_AGAIN) { + infof(data, "writing would block\n"); + /* fall through */ + } + else if(tmperr != CURLE_OK) { + return tmperr; + } + } + + if(wants_read) { + infof(data, "ClientSession wants us to read_tls.\n"); + + cr_recv(data, sockindex, NULL, 0, &tmperr); + if(tmperr == CURLE_AGAIN) { + infof(data, "reading would block\n"); + /* fall through */ + } + else if(tmperr != CURLE_OK) { + if(tmperr == CURLE_READ_ERROR) { + return CURLE_SSL_CONNECT_ERROR; + } + else { + return tmperr; + } + } + } + } + + /* We should never fall through the loop. We should return either because + the handshake is done or because we can't read/write without blocking. */ + DEBUGASSERT(false); +} + +/* returns a bitmap of flags for this connection's first socket indicating + whether we want to read or write */ +static int +cr_getsock(struct connectdata *conn, curl_socket_t *socks) +{ + struct ssl_connect_data *const connssl = &conn->ssl[FIRSTSOCKET]; + curl_socket_t sockfd = conn->sock[FIRSTSOCKET]; + struct ssl_backend_data *const backend = connssl->backend; + struct rustls_client_session *session = backend->session; + + if(rustls_client_session_wants_write(session)) { + socks[0] = sockfd; + return GETSOCK_WRITESOCK(0); + } + if(rustls_client_session_wants_read(session)) { + socks[0] = sockfd; + return GETSOCK_READSOCK(0); + } + + return GETSOCK_BLANK; +} + +static void * +cr_get_internals(struct ssl_connect_data *connssl, + CURLINFO info UNUSED_PARAM) +{ + struct ssl_backend_data *backend = connssl->backend; + return &backend->session; +} + +static void +cr_close(struct Curl_easy *data, struct connectdata *conn, + int sockindex) +{ + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + struct ssl_backend_data *backend = connssl->backend; + CURLcode tmperr = CURLE_OK; + ssize_t n = 0; + + if(backend->session) { + rustls_client_session_send_close_notify(backend->session); + n = cr_send(data, sockindex, NULL, 0, &tmperr); + if(n < 0) { + failf(data, "error sending close notify: %d", tmperr); + } + + rustls_client_session_free(backend->session); + backend->session = NULL; + } + if(backend->config) { + rustls_client_config_free(backend->config); + backend->config = NULL; + } +} + +const struct Curl_ssl Curl_ssl_rustls = { + { CURLSSLBACKEND_RUSTLS, "rustls" }, + SSLSUPP_TLS13_CIPHERSUITES, /* supports */ + sizeof(struct ssl_backend_data), + + Curl_none_init, /* init */ + Curl_none_cleanup, /* cleanup */ + rustls_version, /* version */ + Curl_none_check_cxn, /* check_cxn */ + Curl_none_shutdown, /* shutdown */ + cr_data_pending, /* data_pending */ + Curl_none_random, /* random */ + Curl_none_cert_status_request, /* cert_status_request */ + cr_connect, /* connect */ + cr_connect_nonblocking, /* connect_nonblocking */ + cr_getsock, /* cr_getsock */ + cr_get_internals, /* get_internals */ + cr_close, /* close_one */ + Curl_none_close_all, /* close_all */ + Curl_none_session_free, /* session_free */ + Curl_none_set_engine, /* set_engine */ + Curl_none_set_engine_default, /* set_engine_default */ + Curl_none_engines_list, /* engines_list */ + Curl_none_false_start, /* false_start */ + NULL /* sha256sum */ +}; + +#endif /* USE_RUSTLS */ diff --git a/lib/vtls/rustls.h b/lib/vtls/rustls.h new file mode 100644 index 000000000..06effac4d --- /dev/null +++ b/lib/vtls/rustls.h @@ -0,0 +1,32 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2020, Jacob Hoffman-Andrews, <github@hoffman-andrews.com> + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ +#ifndef HEADER_CURL_RUSTLS_H +#define HEADER_CURL_RUSTLS_H + +#include "curl_setup.h" + +#ifdef USE_RUSTLS + +extern const struct Curl_ssl Curl_ssl_rustls; + +#endif /* USE_RUSTLS */ +#endif /* HEADER_CURL_RUSTLS_H */ diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c index 5a1240938..6a0069237 100644 --- a/lib/vtls/vtls.c +++ b/lib/vtls/vtls.c @@ -1222,6 +1222,8 @@ const struct Curl_ssl *Curl_ssl = &Curl_ssl_mbedtls; #elif defined(USE_NSS) &Curl_ssl_nss; +#elif defined(USE_RUSTLS) + &Curl_ssl_rustls; #elif defined(USE_OPENSSL) &Curl_ssl_openssl; #elif defined(USE_SCHANNEL) @@ -1265,6 +1267,9 @@ static const struct Curl_ssl *available_backends[] = { #if defined(USE_BEARSSL) &Curl_ssl_bearssl, #endif +#if defined(USE_RUSTLS) + &Curl_ssl_rustls, +#endif NULL }; diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h index 250a8b99f..9de8a80c1 100644 --- a/lib/vtls/vtls.h +++ b/lib/vtls/vtls.h @@ -116,6 +116,7 @@ bool Curl_ssl_tls13_ciphersuites(void); #include "mbedtls.h" /* mbedTLS versions */ #include "mesalink.h" /* MesaLink versions */ #include "bearssl.h" /* BearSSL versions */ +#include "rustls.h" /* rustls versions */ #ifndef MAX_PINNED_PUBKEY_SIZE #define MAX_PINNED_PUBKEY_SIZE 1048576 /* 1MB */ |