rustls: support CURLOPT_SSL_VERIFYPEER

This requires the latest main branch of crustls, which provides
rustls_client_config_builder_dangerous_set_certificate_verifier and
rustls_client_config_builder_set_enable_sni.

This refactors the session setup into its own function, and adds a new
function cr_hostname_is_ip. Because crustls doesn't support verification
of IP addresses, special handling is needed: We disable SNI and set a
placeholder hostname (which never actually gets sent on the wire).

Closes #6719
This commit is contained in:
Jacob Hoffman-Andrews 2021-02-26 17:29:36 -08:00 committed by Daniel Stenberg
parent f7aeff58a3
commit 43a56e34e1
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
1 changed files with 97 additions and 42 deletions

View File

@ -29,6 +29,7 @@
#include <errno.h>
#include <crustls.h>
#include "inet_pton.h"
#include "urldata.h"
#include "sendf.h"
#include "vtls.h"
@ -276,29 +277,57 @@ cr_send(struct Curl_easy *data, int sockindex,
return plainwritten;
}
static CURLcode
cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn,
int sockindex, bool *done)
/* A server certificate verify callback for rustls that always returns
RUSTLS_RESULT_OK, or in other words disable certificate verification. */
static enum rustls_result
cr_verify_none(void *userdata UNUSED_PARAM,
const rustls_verify_server_cert_params *params UNUSED_PARAM)
{
return RUSTLS_RESULT_OK;
}
static bool
cr_hostname_is_ip(const char *hostname)
{
struct in_addr in;
#ifdef ENABLE_IPV6
struct in6_addr in6;
if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) {
return true;
}
#endif /* ENABLE_IPV6 */
if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
return true;
}
return false;
}
static CURLcode
cr_init_backend(struct Curl_easy *data, struct connectdata *conn,
struct ssl_backend_data *const backend)
{
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;
const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
const char *hostname = conn->host.name;
char errorbuf[256];
size_t errorlen;
int result;
if(ssl_connection_none == connssl->state) {
config_builder = rustls_client_config_builder_new();
if(ssl_cafile) {
if(!verifypeer) {
rustls_client_config_builder_dangerous_set_certificate_verifier(
config_builder, cr_verify_none, NULL);
/* rustls doesn't support IP addresses (as of 0.19.0), and will reject
* sessions created with an IP address, even when certificate verification
* is turned off. Set a placeholder hostname and disable SNI. */
if(cr_hostname_is_ip(hostname)) {
rustls_client_config_builder_set_enable_sni(config_builder, false);
hostname = "example.invalid";
}
}
else if(ssl_cafile) {
result = rustls_client_config_builder_load_roots_from_file(
config_builder, ssl_cafile);
if(result != RUSTLS_RESULT_OK) {
@ -321,16 +350,42 @@ cr_connect_nonblocking(struct Curl_easy *data, struct connectdata *conn,
backend->config = rustls_client_config_builder_build(config_builder);
DEBUGASSERT(session == NULL);
result = rustls_client_session_new(
backend->config, conn->host.name, &session);
backend->config, hostname, &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;
return CURLE_OK;
}
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 = NULL;
CURLcode tmperr = CURLE_OK;
int result;
int what;
bool wants_read;
bool wants_write;
curl_socket_t writefd;
curl_socket_t readfd;
if(ssl_connection_none == connssl->state) {
result = cr_init_backend(data, conn, connssl->backend);
if(result != CURLE_OK) {
return result;
}
connssl->state = ssl_connection_negotiating;
}
session = backend->session;
/* Read/write data until the handshake is done or the socket would block. */
for(;;) {
/*