#ifdef USE_NGTCP2
#include <ngtcp2/ngtcp2.h>
#include <nghttp3/nghttp3.h>
+
#ifdef USE_OPENSSL
#include <openssl/err.h>
#ifdef OPENSSL_IS_BORINGSSL
#include <ngtcp2/ngtcp2_crypto_wolfssl.h>
#include "vtls/wolfssl.h"
#endif
+
#include "urldata.h"
#include "sendf.h"
#include "strdup.h"
/** SSL callbacks ***/
-static int quic_init_ssl(struct quicsocket *qs)
+static CURLcode quic_init_ssl(struct quicsocket *qs,
+ struct Curl_easy *data,
+ struct connectdata *conn)
{
const uint8_t *alpn = NULL;
size_t alpnlen = 0;
/* this will need some attention when HTTPS proxy over QUIC get fixed */
const char * const hostname = qs->conn->host.name;
+ (void)data;
+ (void)conn;
DEBUGASSERT(!qs->ssl);
qs->ssl = SSL_new(qs->sslctx);
/* set SNI */
SSL_set_tlsext_host_name(qs->ssl, hostname);
- return 0;
+ return CURLE_OK;
}
#elif defined(USE_GNUTLS)
-static int quic_init_ssl(struct quicsocket *qs)
+static CURLcode quic_init_ssl(struct quicsocket *qs,
+ struct Curl_easy *data,
+ struct connectdata *conn)
{
+ CURLcode result;
gnutls_datum_t alpn[2];
/* this will need some attention when HTTPS proxy over QUIC get fixed */
const char * const hostname = qs->conn->host.name;
+ long * const pverifyresult = &data->set.ssl.certverifyresult;
int rc;
- DEBUGASSERT(!qs->ssl);
+ DEBUGASSERT(qs->gtls == NULL);
+ qs->gtls = calloc(1, sizeof(*(qs->gtls)));
+ if(!qs->gtls)
+ return CURLE_OUT_OF_MEMORY;
- gnutls_init(&qs->ssl, GNUTLS_CLIENT);
- gnutls_session_set_ptr(qs->ssl, &qs->conn_ref);
+ result = gtls_client_init(data, &conn->ssl_config, &data->set.ssl,
+ hostname, qs->gtls, pverifyresult);
+ if(result)
+ return result;
+
+ gnutls_session_set_ptr(qs->gtls->session, &qs->conn_ref);
- if(ngtcp2_crypto_gnutls_configure_client_session(qs->ssl) != 0) {
+ if(ngtcp2_crypto_gnutls_configure_client_session(qs->gtls->session) != 0) {
H3BUGF(fprintf(stderr,
"ngtcp2_crypto_gnutls_configure_client_session failed\n"));
- return 1;
+ return CURLE_QUIC_CONNECT_ERROR;
}
- rc = gnutls_priority_set_direct(qs->ssl, QUIC_PRIORITY, NULL);
+ rc = gnutls_priority_set_direct(qs->gtls->session, QUIC_PRIORITY, NULL);
if(rc < 0) {
H3BUGF(fprintf(stderr, "gnutls_priority_set_direct failed: %s\n",
gnutls_strerror(rc)));
- return 1;
+ return CURLE_QUIC_CONNECT_ERROR;
}
/* Open the file if a TLS or QUIC backend has not done this before. */
Curl_tls_keylog_open();
if(Curl_tls_keylog_enabled()) {
- gnutls_session_set_keylog_function(qs->ssl, keylog_callback);
- }
-
- if(qs->cred)
- gnutls_certificate_free_credentials(qs->cred);
-
- rc = gnutls_certificate_allocate_credentials(&qs->cred);
- if(rc < 0) {
- H3BUGF(fprintf(stderr,
- "gnutls_certificate_allocate_credentials failed: %s\n",
- gnutls_strerror(rc)));
- return 1;
- }
-
- rc = gnutls_certificate_set_x509_system_trust(qs->cred);
- if(rc < 0) {
- H3BUGF(fprintf(stderr,
- "gnutls_certificate_set_x509_system_trust failed: %s\n",
- gnutls_strerror(rc)));
- return 1;
- }
-
- rc = gnutls_credentials_set(qs->ssl, GNUTLS_CRD_CERTIFICATE, qs->cred);
- if(rc < 0) {
- H3BUGF(fprintf(stderr, "gnutls_credentials_set failed: %s\n",
- gnutls_strerror(rc)));
- return 1;
+ gnutls_session_set_keylog_function(qs->gtls->session, keylog_callback);
}
/* strip the first byte (the length) from NGHTTP3_ALPN_H3 */
alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1;
alpn[1].size = sizeof(H3_ALPN_H3) - 2;
- gnutls_alpn_set_protocols(qs->ssl, alpn, 2, GNUTLS_ALPN_MANDATORY);
+ gnutls_alpn_set_protocols(qs->gtls->session, alpn, 2, GNUTLS_ALPN_MANDATORY);
- /* set SNI */
- gnutls_server_name_set(qs->ssl, GNUTLS_NAME_DNS, hostname, strlen(hostname));
- return 0;
+ return CURLE_OK;
}
#elif defined(USE_WOLFSSL)
/** SSL callbacks ***/
-static int quic_init_ssl(struct quicsocket *qs)
+static CURLcode quic_init_ssl(struct quicsocket *qs,
+ struct Curl_easy *data,
+ struct connectdata *conn)
{
const uint8_t *alpn = NULL;
size_t alpnlen = 0;
/* this will need some attention when HTTPS proxy over QUIC get fixed */
const char * const hostname = qs->conn->host.name;
+ (void)data;
+ (void)conn;
DEBUGASSERT(!qs->ssl);
qs->ssl = SSL_new(qs->sslctx);
wolfSSL_UseSNI(qs->ssl, WOLFSSL_SNI_HOST_NAME,
hostname, (unsigned short)strlen(hostname));
- return 0;
+ return CURLE_OK;
}
#endif /* defined(USE_WOLFSSL) */
return CURLE_QUIC_CONNECT_ERROR;
#endif
- if(quic_init_ssl(qs))
- return CURLE_QUIC_CONNECT_ERROR;
+ result = quic_init_ssl(qs, data, conn);
+ if(result)
+ return result;
qs->dcid.datalen = NGTCP2_MAX_CIDLEN;
result = Curl_rand(data, qs->dcid.data, NGTCP2_MAX_CIDLEN);
if(rc)
return CURLE_QUIC_CONNECT_ERROR;
+#ifdef USE_GNUTLS
+ ngtcp2_conn_set_tls_native_handle(qs->qconn, qs->gtls->session);
+#else
ngtcp2_conn_set_tls_native_handle(qs->qconn, qs->ssl);
+#endif
ngtcp2_connection_close_error_default(&qs->last_error);
close(qs->qlogfd);
qs->qlogfd = -1;
}
- if(qs->ssl)
#ifdef USE_OPENSSL
+ if(qs->ssl)
SSL_free(qs->ssl);
+ qs->ssl = NULL;
+ SSL_CTX_free(qs->sslctx);
#elif defined(USE_GNUTLS)
- gnutls_deinit(qs->ssl);
+ if(qs->gtls) {
+ if(qs->gtls->cred)
+ gnutls_certificate_free_credentials(qs->gtls->cred);
+ if(qs->gtls->session)
+ gnutls_deinit(qs->gtls->session);
+ free(qs->gtls);
+ qs->gtls = NULL;
+ }
#elif defined(USE_WOLFSSL)
+ if(qs->ssl)
wolfSSL_free(qs->ssl);
-#endif
qs->ssl = NULL;
-#ifdef USE_GNUTLS
- if(qs->cred) {
- gnutls_certificate_free_credentials(qs->cred);
- qs->cred = NULL;
- }
+ wolfSSL_CTX_free(qs->sslctx);
#endif
free(qs->pktbuf);
nghttp3_conn_del(qs->h3conn);
ngtcp2_conn_del(qs->qconn);
-#ifdef USE_OPENSSL
- SSL_CTX_free(qs->sslctx);
-#elif defined(USE_WOLFSSL)
- wolfSSL_CTX_free(qs->sslctx);
-#endif
}
void Curl_quic_disconnect(struct Curl_easy *data,
struct connectdata *conn, int tempindex)
{
CURLcode result = CURLE_OK;
+ const char *hostname, *disp_hostname;
+ int port;
+ char *snihost;
+
+ Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port);
+ snihost = Curl_ssl_snihost(data, hostname, NULL);
+ if(!snihost)
+ return CURLE_PEER_FAILED_VERIFICATION;
+
conn->recv[FIRSTSOCKET] = ngh3_stream_recv;
conn->send[FIRSTSOCKET] = ngh3_stream_send;
conn->handler = &Curl_handler_http3;
X509_free(server_cert);
if(result)
return result;
- infof(data, "Verified certificate just fine");
#elif defined(USE_GNUTLS)
- result = Curl_gtls_verifyserver(conn->cfilter[FIRSTSOCKET],
- data, conn->quic->ssl);
+ result = Curl_gtls_verifyserver(data, conn->quic->gtls->session,
+ &conn->ssl_config, &data->set.ssl,
+ hostname, disp_hostname,
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY]);
+ if(result)
+ return result;
#elif defined(USE_WOLFSSL)
- const char *hostname, *disp_hostname;
- int port;
- char *snihost;
-
- Curl_conn_get_host(data, FIRSTSOCKET, &hostname, &disp_hostname, &port);
- snihost = Curl_ssl_snihost(data, hostname, NULL);
- if(!snihost ||
- (wolfSSL_check_domain_name(conn->quic->ssl, snihost) == SSL_FAILURE))
+ if(wolfSSL_check_domain_name(conn->quic->ssl, snihost) == SSL_FAILURE)
return CURLE_PEER_FAILED_VERIFICATION;
- infof(data, "Verified certificate just fine");
#endif
+ infof(data, "Verified certificate just fine");
}
else
infof(data, "Skipped certificate verification");
/* The last #include file should be: */
#include "memdebug.h"
-#ifdef HAVE_GNUTLS_SRP
-/* the function exists */
-#ifdef USE_TLS_SRP
-/* the functionality is not disabled */
-#define USE_GNUTLS_SRP
-#endif
-#endif
-
/* Enable GnuTLS debugging by defining GTLSDEBUG */
/*#define GTLSDEBUG */
# include <gnutls/ocsp.h>
struct ssl_backend_data {
- gnutls_session_t session;
- gnutls_certificate_credentials_t cred;
-#ifdef USE_GNUTLS_SRP
- gnutls_srp_client_credentials_t srp_client_cred;
-#endif
+ struct gtls_instance gtls;
};
static ssize_t gtls_push(void *s, const void *buf, size_t blen)
DEBUGASSERT(data);
nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
if(nwritten < 0) {
- gnutls_transport_set_errno(connssl->backend->session,
+ gnutls_transport_set_errno(connssl->backend->gtls.session,
(CURLE_AGAIN == result)? EAGAIN : EINVAL);
nwritten = -1;
}
DEBUGASSERT(data);
nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
if(nread < 0) {
- gnutls_transport_set_errno(connssl->backend->session,
+ gnutls_transport_set_errno(connssl->backend->gtls.session,
(CURLE_AGAIN == result)? EAGAIN : EINVAL);
nread = -1;
}
curl_socket_t sockfd = cf->conn->sock[cf->sockindex];
DEBUGASSERT(backend);
- session = backend->session;
+ session = backend->gtls.session;
for(;;) {
timediff_t timeout_ms;
#define GNUTLS_SRP "+SRP"
static CURLcode
-set_ssl_version_min_max(struct Curl_cfilter *cf,
- struct Curl_easy *data,
+set_ssl_version_min_max(struct Curl_easy *data,
+ struct ssl_primary_config *conn_config,
const char **prioritylist,
const char *tls13support)
{
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
long ssl_version = conn_config->version;
long ssl_version_max = conn_config->version_max;
return CURLE_SSL_CONNECT_ERROR;
}
-static CURLcode
-gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
+CURLcode gtls_client_init(struct Curl_easy *data,
+ struct ssl_primary_config *config,
+ struct ssl_config_data *ssl_config,
+ const char *hostname,
+ struct gtls_instance *gtls,
+ long *pverifyresult)
{
- struct ssl_connect_data *connssl = cf->ctx;
- struct ssl_backend_data *backend = connssl->backend;
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
- struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
unsigned int init_flags;
- gnutls_session_t session;
int rc;
bool sni = TRUE; /* default is SNI enabled */
- void *transport_ptr = NULL;
- gnutls_push_func gnutls_transport_push = NULL;
- gnutls_pull_func gnutls_transport_pull = NULL;
#ifdef ENABLE_IPV6
struct in6_addr addr;
#else
#endif
const char *prioritylist;
const char *err = NULL;
- const char *hostname = connssl->hostname;
- long * const certverifyresult = &ssl_config->certverifyresult;
const char *tls13support;
CURLcode result;
- DEBUGASSERT(backend);
-
- if(connssl->state == ssl_connection_complete)
- /* to make us tolerant against being called more than once for the
- same connection */
- return CURLE_OK;
-
if(!gtls_inited)
gtls_init();
- /* Initialize certverifyresult to OK */
- *certverifyresult = 0;
+ *pverifyresult = 0;
- if(conn_config->version == CURL_SSLVERSION_SSLv2) {
+ if(config->version == CURL_SSLVERSION_SSLv2) {
failf(data, "GnuTLS does not support SSLv2");
return CURLE_SSL_CONNECT_ERROR;
}
- else if(conn_config->version == CURL_SSLVERSION_SSLv3)
+ else if(config->version == CURL_SSLVERSION_SSLv3)
sni = FALSE; /* SSLv3 has no SNI */
/* allocate a cred struct */
- rc = gnutls_certificate_allocate_credentials(&backend->cred);
+ rc = gnutls_certificate_allocate_credentials(>ls->cred);
if(rc != GNUTLS_E_SUCCESS) {
failf(data, "gnutls_cert_all_cred() failed: %s", gnutls_strerror(rc));
return CURLE_SSL_CONNECT_ERROR;
}
#ifdef USE_GNUTLS_SRP
- if((ssl_config->primary.authtype == CURL_TLSAUTH_SRP) &&
+ if((config->authtype == CURL_TLSAUTH_SRP) &&
Curl_auth_allowed_to_host(data)) {
- infof(data, "Using TLS-SRP username: %s",
- ssl_config->primary.username);
+ infof(data, "Using TLS-SRP username: %s", config->username);
- rc = gnutls_srp_allocate_client_credentials(&backend->srp_client_cred);
+ rc = gnutls_srp_allocate_client_credentials(
+ >ls->srp_client_cred);
if(rc != GNUTLS_E_SUCCESS) {
failf(data, "gnutls_srp_allocate_client_cred() failed: %s",
gnutls_strerror(rc));
return CURLE_OUT_OF_MEMORY;
}
- rc = gnutls_srp_set_client_credentials(backend->srp_client_cred,
- ssl_config->primary.username,
- ssl_config->primary.password);
+ rc = gnutls_srp_set_client_credentials(gtls->srp_client_cred,
+ config->username,
+ config->password);
if(rc != GNUTLS_E_SUCCESS) {
failf(data, "gnutls_srp_set_client_cred() failed: %s",
gnutls_strerror(rc));
}
#endif
- if(conn_config->CAfile) {
+ if(config->CAfile) {
/* set the trusted CA cert bundle file */
- gnutls_certificate_set_verify_flags(backend->cred,
+ gnutls_certificate_set_verify_flags(gtls->cred,
GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
- rc = gnutls_certificate_set_x509_trust_file(backend->cred,
- conn_config->CAfile,
+ rc = gnutls_certificate_set_x509_trust_file(gtls->cred,
+ config->CAfile,
GNUTLS_X509_FMT_PEM);
if(rc < 0) {
infof(data, "error reading ca cert file %s (%s)",
- conn_config->CAfile, gnutls_strerror(rc));
- if(conn_config->verifypeer) {
- *certverifyresult = rc;
+ config->CAfile, gnutls_strerror(rc));
+ if(config->verifypeer) {
+ *pverifyresult = rc;
return CURLE_SSL_CACERT_BADFILE;
}
}
else
- infof(data, "found %d certificates in %s", rc,
- conn_config->CAfile);
+ infof(data, "found %d certificates in %s", rc, config->CAfile);
}
- if(conn_config->CApath) {
+ if(config->CApath) {
/* set the trusted CA cert directory */
- rc = gnutls_certificate_set_x509_trust_dir(backend->cred,
- conn_config->CApath,
+ rc = gnutls_certificate_set_x509_trust_dir(gtls->cred,
+ config->CApath,
GNUTLS_X509_FMT_PEM);
if(rc < 0) {
infof(data, "error reading ca cert file %s (%s)",
- conn_config->CApath, gnutls_strerror(rc));
- if(conn_config->verifypeer) {
- *certverifyresult = rc;
+ config->CApath, gnutls_strerror(rc));
+ if(config->verifypeer) {
+ *pverifyresult = rc;
return CURLE_SSL_CACERT_BADFILE;
}
}
else
- infof(data, "found %d certificates in %s",
- rc, conn_config->CApath);
+ infof(data, "found %d certificates in %s", rc, config->CApath);
}
#ifdef CURL_CA_FALLBACK
/* use system ca certificate store as fallback */
- if(conn_config->verifypeer &&
- !(conn_config->CAfile || conn_config->CApath)) {
+ if(config->verifypeer && !(config->CAfile || config->CApath)) {
/* this ignores errors on purpose */
- gnutls_certificate_set_x509_system_trust(backend->cred);
+ gnutls_certificate_set_x509_system_trust(gtls->cred);
}
#endif
- if(ssl_config->primary.CRLfile) {
+ if(config->CRLfile) {
/* set the CRL list file */
- rc = gnutls_certificate_set_x509_crl_file(backend->cred,
- ssl_config->primary.CRLfile,
+ rc = gnutls_certificate_set_x509_crl_file(gtls->cred,
+ config->CRLfile,
GNUTLS_X509_FMT_PEM);
if(rc < 0) {
failf(data, "error reading crl file %s (%s)",
- ssl_config->primary.CRLfile, gnutls_strerror(rc));
+ config->CRLfile, gnutls_strerror(rc));
return CURLE_SSL_CRL_BADFILE;
}
else
- infof(data, "found %d CRL in %s",
- rc, ssl_config->primary.CRLfile);
+ infof(data, "found %d CRL in %s", rc, config->CRLfile);
}
/* Initialize TLS session as a client */
init_flags |= GNUTLS_NO_TICKETS;
#endif
- rc = gnutls_init(&backend->session, init_flags);
+ rc = gnutls_init(>ls->session, init_flags);
if(rc != GNUTLS_E_SUCCESS) {
failf(data, "gnutls_init() failed: %d", rc);
return CURLE_SSL_CONNECT_ERROR;
}
- /* convenient assign */
- session = backend->session;
-
if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) &&
#ifdef ENABLE_IPV6
(0 == Curl_inet_pton(AF_INET6, hostname, &addr)) &&
sni) {
size_t snilen;
char *snihost = Curl_ssl_snihost(data, hostname, &snilen);
- if(!snihost || gnutls_server_name_set(session, GNUTLS_NAME_DNS, snihost,
- snilen) < 0) {
+ if(!snihost || gnutls_server_name_set(gtls->session, GNUTLS_NAME_DNS,
+ snihost, snilen) < 0) {
failf(data, "Failed to set SNI");
return CURLE_SSL_CONNECT_ERROR;
}
}
/* Use default priorities */
- rc = gnutls_set_default_priority(session);
+ rc = gnutls_set_default_priority(gtls->session);
if(rc != GNUTLS_E_SUCCESS)
return CURLE_SSL_CONNECT_ERROR;
* removed if a run-time error indicates that SRP is not supported by this
* GnuTLS version */
- if(conn_config->version == CURL_SSLVERSION_SSLv2 ||
- conn_config->version == CURL_SSLVERSION_SSLv3) {
+ if(config->version == CURL_SSLVERSION_SSLv2 ||
+ config->version == CURL_SSLVERSION_SSLv3) {
failf(data, "GnuTLS does not support SSLv2 or SSLv3");
return CURLE_SSL_CONNECT_ERROR;
}
- if(conn_config->version == CURL_SSLVERSION_TLSv1_3) {
+ if(config->version == CURL_SSLVERSION_TLSv1_3) {
if(!tls13support) {
failf(data, "This GnuTLS installation does not support TLS 1.3");
return CURLE_SSL_CONNECT_ERROR;
}
/* At this point we know we have a supported TLS version, so set it */
- result = set_ssl_version_min_max(cf, data, &prioritylist, tls13support);
+ result = set_ssl_version_min_max(data, config, &prioritylist, tls13support);
if(result)
return result;
#ifdef USE_GNUTLS_SRP
/* Only add SRP to the cipher list if SRP is requested. Otherwise
* GnuTLS will disable TLS 1.3 support. */
- if(ssl_config->primary.authtype == CURL_TLSAUTH_SRP) {
+ if(config->authtype == CURL_TLSAUTH_SRP) {
size_t len = strlen(prioritylist);
char *prioritysrp = malloc(len + sizeof(GNUTLS_SRP) + 1);
return CURLE_OUT_OF_MEMORY;
strcpy(prioritysrp, prioritylist);
strcpy(prioritysrp + len, ":" GNUTLS_SRP);
- rc = gnutls_priority_set_direct(session, prioritysrp, &err);
+ rc = gnutls_priority_set_direct(gtls->session, prioritysrp, &err);
free(prioritysrp);
if((rc == GNUTLS_E_INVALID_REQUEST) && err) {
else {
#endif
infof(data, "GnuTLS ciphers: %s", prioritylist);
- rc = gnutls_priority_set_direct(session, prioritylist, &err);
+ rc = gnutls_priority_set_direct(gtls->session, prioritylist, &err);
#ifdef USE_GNUTLS_SRP
}
#endif
return CURLE_SSL_CONNECT_ERROR;
}
- if(cf->conn->bits.tls_enable_alpn) {
- int cur = 0;
- gnutls_datum_t protocols[2];
-
-#ifdef USE_HTTP2
- if(data->state.httpwant >= CURL_HTTP_VERSION_2
-#ifndef CURL_DISABLE_PROXY
- && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy)
-#endif
- ) {
- protocols[cur].data = (unsigned char *)ALPN_H2;
- protocols[cur].size = ALPN_H2_LENGTH;
- cur++;
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
- }
-#endif
-
- protocols[cur].data = (unsigned char *)ALPN_HTTP_1_1;
- protocols[cur].size = ALPN_HTTP_1_1_LENGTH;
- cur++;
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
-
- if(gnutls_alpn_set_protocols(session, protocols, cur, 0)) {
- failf(data, "failed setting ALPN");
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
-
- if(ssl_config->primary.clientcert) {
+ if(config->clientcert) {
if(ssl_config->key_passwd) {
const unsigned int supported_key_encryption_algorithms =
GNUTLS_PKCS_USE_PKCS12_3DES | GNUTLS_PKCS_USE_PKCS12_ARCFOUR |
GNUTLS_PKCS_USE_PBES2_AES_128 | GNUTLS_PKCS_USE_PBES2_AES_192 |
GNUTLS_PKCS_USE_PBES2_AES_256;
rc = gnutls_certificate_set_x509_key_file2(
- backend->cred,
- ssl_config->primary.clientcert,
- ssl_config->key ?
- ssl_config->key : ssl_config->primary.clientcert,
+ gtls->cred,
+ config->clientcert,
+ ssl_config->key ? ssl_config->key : config->clientcert,
do_file_type(ssl_config->cert_type),
ssl_config->key_passwd,
supported_key_encryption_algorithms);
}
else {
if(gnutls_certificate_set_x509_key_file(
- backend->cred,
- ssl_config->primary.clientcert,
- ssl_config->key ?
- ssl_config->key : ssl_config->primary.clientcert,
+ gtls->cred,
+ config->clientcert,
+ ssl_config->key ? ssl_config->key : config->clientcert,
do_file_type(ssl_config->cert_type) ) !=
GNUTLS_E_SUCCESS) {
failf(data, "error reading X.509 key or certificate file");
#ifdef USE_GNUTLS_SRP
/* put the credentials to the current session */
- if(ssl_config->primary.authtype == CURL_TLSAUTH_SRP) {
- rc = gnutls_credentials_set(session, GNUTLS_CRD_SRP,
- backend->srp_client_cred);
+ if(config->authtype == CURL_TLSAUTH_SRP) {
+ rc = gnutls_credentials_set(gtls->session, GNUTLS_CRD_SRP,
+ gtls->srp_client_cred);
if(rc != GNUTLS_E_SUCCESS) {
failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc));
return CURLE_SSL_CONNECT_ERROR;
else
#endif
{
- rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE,
- backend->cred);
+ rc = gnutls_credentials_set(gtls->session, GNUTLS_CRD_CERTIFICATE,
+ gtls->cred);
if(rc != GNUTLS_E_SUCCESS) {
failf(data, "gnutls_credentials_set() failed: %s", gnutls_strerror(rc));
return CURLE_SSL_CONNECT_ERROR;
}
}
- /* push/pull through filter chain */
- transport_ptr = cf;
- gnutls_transport_push = gtls_push;
- gnutls_transport_pull = gtls_pull;
+ if(config->verifystatus) {
+ rc = gnutls_ocsp_status_request_enable_client(gtls->session,
+ NULL, 0, NULL);
+ if(rc != GNUTLS_E_SUCCESS) {
+ failf(data, "gnutls_ocsp_status_request_enable_client() failed: %d", rc);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode
+gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_backend_data *backend = connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ long * const pverifyresult = &ssl_config->certverifyresult;
+ CURLcode result;
+
+ DEBUGASSERT(backend);
- /* set the connection handle */
- gnutls_transport_set_ptr(session, transport_ptr);
+ if(connssl->state == ssl_connection_complete)
+ /* to make us tolerant against being called more than once for the
+ same connection */
+ return CURLE_OK;
- /* register callback functions to send and receive data. */
- gnutls_transport_set_push_function(session, gnutls_transport_push);
- gnutls_transport_set_pull_function(session, gnutls_transport_pull);
+ result = gtls_client_init(data, conn_config, ssl_config,
+ connssl->hostname,
+ &backend->gtls, pverifyresult);
+ if(result)
+ return result;
- if(conn_config->verifystatus) {
- rc = gnutls_ocsp_status_request_enable_client(session, NULL, 0, NULL);
- if(rc != GNUTLS_E_SUCCESS) {
- failf(data, "gnutls_ocsp_status_request_enable_client() failed: %d", rc);
+ if(cf->conn->bits.tls_enable_alpn) {
+ int cur = 0;
+ gnutls_datum_t protocols[2];
+
+#ifdef USE_HTTP2
+ if(data->state.httpwant >= CURL_HTTP_VERSION_2
+#ifndef CURL_DISABLE_PROXY
+ && (!Curl_ssl_cf_is_proxy(cf) || !cf->conn->bits.tunnel_proxy)
+#endif
+ ) {
+ protocols[cur].data = (unsigned char *)ALPN_H2;
+ protocols[cur].size = ALPN_H2_LENGTH;
+ cur++;
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_H2);
+ }
+#endif
+
+ protocols[cur].data = (unsigned char *)ALPN_HTTP_1_1;
+ protocols[cur].size = ALPN_HTTP_1_1_LENGTH;
+ cur++;
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, ALPN_HTTP_1_1);
+
+ if(gnutls_alpn_set_protocols(backend->gtls.session, protocols, cur, 0)) {
+ failf(data, "failed setting ALPN");
return CURLE_SSL_CONNECT_ERROR;
}
}
/* This might be a reconnect, so we check for a session ID in the cache
to speed up things */
- if(ssl_config->primary.sessionid) {
+ if(conn_config->sessionid) {
void *ssl_sessionid;
size_t ssl_idsize;
Curl_ssl_sessionid_lock(data);
if(!Curl_ssl_getsessionid(cf, data, &ssl_sessionid, &ssl_idsize)) {
/* we got a session id, use it! */
- gnutls_session_set_data(session, ssl_sessionid, ssl_idsize);
+ gnutls_session_set_data(backend->gtls.session,
+ ssl_sessionid, ssl_idsize);
/* Informational message */
infof(data, "SSL re-using session ID");
Curl_ssl_sessionid_unlock(data);
}
+ /* register callback functions and handle to send and receive data. */
+ gnutls_transport_set_ptr(backend->gtls.session, cf);
+ gnutls_transport_set_push_function(backend->gtls.session, gtls_push);
+ gnutls_transport_set_pull_function(backend->gtls.session, gtls_pull);
+
return CURLE_OK;
}
}
CURLcode
-Curl_gtls_verifyserver(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- gnutls_session_t session)
+Curl_gtls_verifyserver(struct Curl_easy *data,
+ gnutls_session_t session,
+ struct ssl_primary_config *config,
+ struct ssl_config_data *ssl_config,
+ const char *hostname,
+ const char *dispname,
+ const char *pinned_key)
{
- struct ssl_connect_data *connssl = cf->ctx;
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
- struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
unsigned int cert_list_size;
const gnutls_datum_t *chainp;
unsigned int verify_status = 0;
time_t certclock;
const char *ptr;
int rc;
- gnutls_datum_t proto;
CURLcode result = CURLE_OK;
#ifndef CURL_DISABLE_VERBOSE_STRINGS
unsigned int algo;
unsigned int bits;
gnutls_protocol_t version = gnutls_protocol_get_version(session);
#endif
- const char *hostname = connssl->hostname;
long * const certverifyresult = &ssl_config->certverifyresult;
/* the name of the cipher suite used, e.g. ECDHE_RSA_AES_256_GCM_SHA384. */
chainp = gnutls_certificate_get_peers(session, &cert_list_size);
if(!chainp) {
- if(conn_config->verifypeer ||
- conn_config->verifyhost ||
- conn_config->issuercert) {
+ if(config->verifypeer ||
+ config->verifyhost ||
+ config->issuercert) {
#ifdef USE_GNUTLS_SRP
if(ssl_config->primary.authtype == CURL_TLSAUTH_SRP
&& ssl_config->primary.username
- && !conn_config->verifypeer
+ && !config->verifypeer
&& gnutls_cipher_get(session)) {
/* no peer cert, but auth is ok if we have SRP user and cipher and no
peer verify */
}
}
- if(conn_config->verifypeer) {
+ if(config->verifypeer) {
/* This function will try to verify the peer's certificate and return its
status (trusted, invalid etc.). The value of status should be one or
more of the gnutls_certificate_status_t enumerated elements bitwise
/* verify_status is a bitmask of gnutls_certificate_status bits */
if(verify_status & GNUTLS_CERT_INVALID) {
- if(conn_config->verifypeer) {
+ if(config->verifypeer) {
failf(data, "server certificate verification failed. CAfile: %s "
- "CRLfile: %s", conn_config->CAfile ? conn_config->CAfile:
+ "CRLfile: %s", config->CAfile ? config->CAfile:
"none",
ssl_config->primary.CRLfile ?
ssl_config->primary.CRLfile : "none");
else
infof(data, " server certificate verification SKIPPED");
- if(conn_config->verifystatus) {
+ if(config->verifystatus) {
if(gnutls_ocsp_status_request_is_checked(session, 0) == 0) {
gnutls_datum_t status_request;
gnutls_ocsp_resp_t ocsp_resp;
gnutls_x509_crt_t format */
gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER);
- if(conn_config->issuercert) {
+ if(config->issuercert) {
gnutls_x509_crt_init(&x509_issuer);
- issuerp = load_file(conn_config->issuercert);
+ issuerp = load_file(config->issuercert);
gnutls_x509_crt_import(x509_issuer, &issuerp, GNUTLS_X509_FMT_PEM);
rc = gnutls_x509_crt_check_issuer(x509_cert, x509_issuer);
gnutls_x509_crt_deinit(x509_issuer);
unload_file(issuerp);
if(rc <= 0) {
failf(data, "server certificate issuer check failed (IssuerCert: %s)",
- conn_config->issuercert?conn_config->issuercert:"none");
+ config->issuercert?config->issuercert:"none");
gnutls_x509_crt_deinit(x509_cert);
return CURLE_SSL_ISSUER_ERROR;
}
infof(data, " server certificate issuer check OK (Issuer Cert: %s)",
- conn_config->issuercert?conn_config->issuercert:"none");
+ config->issuercert?config->issuercert:"none");
}
size = sizeof(certname);
}
#endif
if(!rc) {
- if(conn_config->verifyhost) {
+ if(config->verifyhost) {
failf(data, "SSL: certificate subject name (%s) does not match "
- "target host name '%s'", certname, connssl->dispname);
+ "target host name '%s'", certname, dispname);
gnutls_x509_crt_deinit(x509_cert);
return CURLE_PEER_FAILED_VERIFICATION;
}
else
infof(data, " common name: %s (does not match '%s')",
- certname, connssl->dispname);
+ certname, dispname);
}
else
infof(data, " common name: %s (matched)", certname);
certclock = gnutls_x509_crt_get_expiration_time(x509_cert);
if(certclock == (time_t)-1) {
- if(conn_config->verifypeer) {
+ if(config->verifypeer) {
failf(data, "server cert expiration date verify failed");
*certverifyresult = GNUTLS_CERT_EXPIRED;
gnutls_x509_crt_deinit(x509_cert);
}
else {
if(certclock < time(NULL)) {
- if(conn_config->verifypeer) {
+ if(config->verifypeer) {
failf(data, "server certificate expiration date has passed.");
*certverifyresult = GNUTLS_CERT_EXPIRED;
gnutls_x509_crt_deinit(x509_cert);
certclock = gnutls_x509_crt_get_activation_time(x509_cert);
if(certclock == (time_t)-1) {
- if(conn_config->verifypeer) {
+ if(config->verifypeer) {
failf(data, "server cert activation date verify failed");
*certverifyresult = GNUTLS_CERT_NOT_ACTIVATED;
gnutls_x509_crt_deinit(x509_cert);
}
else {
if(certclock > time(NULL)) {
- if(conn_config->verifypeer) {
+ if(config->verifypeer) {
failf(data, "server certificate not activated yet.");
*certverifyresult = GNUTLS_CERT_NOT_ACTIVATED;
gnutls_x509_crt_deinit(x509_cert);
infof(data, " server certificate activation date OK");
}
- ptr = Curl_ssl_cf_is_proxy(cf)?
- data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
- data->set.str[STRING_SSL_PINNEDPUBLICKEY];
- if(ptr) {
- result = pkp_pin_peer_pubkey(data, x509_cert, ptr);
+ if(pinned_key) {
+ result = pkp_pin_peer_pubkey(data, x509_cert, pinned_key);
if(result != CURLE_OK) {
failf(data, "SSL: public key does not match pinned public key");
gnutls_x509_crt_deinit(x509_cert);
gnutls_x509_crt_deinit(x509_cert);
+ return result;
+}
+
+static CURLcode gtls_verifyserver(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ gnutls_session_t session)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ const char *pinned_key = Curl_ssl_cf_is_proxy(cf)?
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY]:
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY];
+ CURLcode result;
+
+ result = Curl_gtls_verifyserver(data, session, conn_config, ssl_config,
+ connssl->hostname, connssl->dispname,
+ pinned_key);
+ if(result)
+ goto out;
+
if(cf->conn->bits.tls_enable_alpn) {
+ gnutls_datum_t proto;
+ int rc;
+
rc = gnutls_alpn_get_selected_protocol(session, &proto);
if(rc == 0) {
infof(data, VTLS_INFOF_ALPN_ACCEPTED_LEN_1STR, proto.size,
BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE);
}
- connssl->state = ssl_connection_complete;
-
if(ssl_config->primary.sessionid) {
/* we always unconditionally get the session id here, as even if we
already got it from the cache and asked to use it in the connection, it
result = CURLE_OUT_OF_MEMORY;
}
+out:
return result;
}
-
/*
* This function is called after the TCP connect has completed. Setup the TLS
* layer and do all necessary magic.
struct ssl_backend_data *backend = connssl->backend;
gnutls_session_t session;
DEBUGASSERT(backend);
- session = backend->session;
- rc = Curl_gtls_verifyserver(cf, data, session);
+ session = backend->gtls.session;
+ rc = gtls_verifyserver(cf, data, session);
if(rc) {
result = rc;
goto out;
}
+ connssl->state = ssl_connection_complete;
}
out:
(void)data;
DEBUGASSERT(ctx && ctx->backend);
- if(ctx->backend->session &&
- 0 != gnutls_record_check_pending(ctx->backend->session))
+ if(ctx->backend->gtls.session &&
+ 0 != gnutls_record_check_pending(ctx->backend->gtls.session))
return TRUE;
return FALSE;
}
(void)data;
DEBUGASSERT(backend);
- rc = gnutls_record_send(backend->session, mem, len);
+ rc = gnutls_record_send(backend->gtls.session, mem, len);
if(rc < 0) {
*curlcode = (rc == GNUTLS_E_AGAIN)
(void) data;
DEBUGASSERT(backend);
- if(backend->session) {
+ if(backend->gtls.session) {
char buf[32];
/* Maybe the server has already sent a close notify alert.
Read it to avoid an RST on the TCP connection. */
- (void)gnutls_record_recv(backend->session, buf, sizeof(buf));
- gnutls_bye(backend->session, GNUTLS_SHUT_WR);
- gnutls_deinit(backend->session);
- backend->session = NULL;
+ (void)gnutls_record_recv(backend->gtls.session, buf, sizeof(buf));
+ gnutls_bye(backend->gtls.session, GNUTLS_SHUT_WR);
+ gnutls_deinit(backend->gtls.session);
+ backend->gtls.session = NULL;
}
- if(backend->cred) {
- gnutls_certificate_free_credentials(backend->cred);
- backend->cred = NULL;
+ if(backend->gtls.cred) {
+ gnutls_certificate_free_credentials(backend->gtls.cred);
+ backend->gtls.cred = NULL;
}
#ifdef USE_GNUTLS_SRP
- if(backend->srp_client_cred) {
- gnutls_srp_free_client_credentials(backend->srp_client_cred);
- backend->srp_client_cred = NULL;
+ if(backend->gtls.srp_client_cred) {
+ gnutls_srp_free_client_credentials(backend->gtls.srp_client_cred);
+ backend->gtls.srp_client_cred = NULL;
}
#endif
}
we do not send one. Let's hope other servers do the same... */
if(data->set.ftp_ccc == CURLFTPSSL_CCC_ACTIVE)
- gnutls_bye(backend->session, GNUTLS_SHUT_WR);
+ gnutls_bye(backend->gtls.session, GNUTLS_SHUT_WR);
#endif
- if(backend->session) {
+ if(backend->gtls.session) {
ssize_t result;
bool done = FALSE;
char buf[120];
if(what > 0) {
/* Something to read, let's do it and hope that it is the close
notify alert from the server */
- result = gnutls_record_recv(backend->session,
+ result = gnutls_record_recv(backend->gtls.session,
buf, sizeof(buf));
switch(result) {
case 0:
done = TRUE;
}
}
- gnutls_deinit(backend->session);
+ gnutls_deinit(backend->gtls.session);
}
- gnutls_certificate_free_credentials(backend->cred);
+ gnutls_certificate_free_credentials(backend->gtls.cred);
#ifdef USE_GNUTLS_SRP
if(ssl_config->primary.authtype == CURL_TLSAUTH_SRP
&& ssl_config->primary.username != NULL)
- gnutls_srp_free_client_credentials(backend->srp_client_cred);
+ gnutls_srp_free_client_credentials(backend->gtls.srp_client_cred);
#endif
- backend->cred = NULL;
- backend->session = NULL;
+ backend->gtls.cred = NULL;
+ backend->gtls.session = NULL;
return retval;
}
(void)data;
DEBUGASSERT(backend);
- ret = gnutls_record_recv(backend->session, buf, buffersize);
+ ret = gnutls_record_recv(backend->gtls.session, buf, buffersize);
if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
*curlcode = CURLE_AGAIN;
ret = -1;
struct ssl_backend_data *backend = connssl->backend;
(void)info;
DEBUGASSERT(backend);
- return backend->session;
+ return backend->gtls.session;
}
const struct Curl_ssl Curl_ssl_gnutls = {