gnutls: lazy init the trust settings
- delay loading of trust anchors and CRLs after the ClientHello has been sent off - add tracing to IO operations - on IO errors, return the CURLcode of the underlying filter Closes #13339
This commit is contained in:
Родитель
61e6db87ac
Коммит
8cee4c9238
|
@ -286,6 +286,12 @@ CURLcode Curl_vquic_tls_before_recv(struct curl_tls_ctx *ctx,
|
||||||
return result;
|
return result;
|
||||||
ctx->ossl.x509_store_setup = TRUE;
|
ctx->ossl.x509_store_setup = TRUE;
|
||||||
}
|
}
|
||||||
|
#elif defined(USE_GNUTLS)
|
||||||
|
if(!ctx->gtls.trust_setup) {
|
||||||
|
CURLcode result = Curl_gtls_client_trust_setup(cf, data, &ctx->gtls);
|
||||||
|
if(result)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
(void)ctx; (void)cf; (void)data;
|
(void)ctx; (void)cf; (void)data;
|
||||||
#endif
|
#endif
|
||||||
|
|
221
lib/vtls/gtls.c
221
lib/vtls/gtls.c
|
@ -95,15 +95,18 @@ static ssize_t gtls_push(void *s, const void *buf, size_t blen)
|
||||||
{
|
{
|
||||||
struct Curl_cfilter *cf = s;
|
struct Curl_cfilter *cf = s;
|
||||||
struct ssl_connect_data *connssl = cf->ctx;
|
struct ssl_connect_data *connssl = cf->ctx;
|
||||||
|
struct gtls_ssl_backend_data *backend =
|
||||||
|
(struct gtls_ssl_backend_data *)connssl->backend;
|
||||||
struct Curl_easy *data = CF_DATA_CURRENT(cf);
|
struct Curl_easy *data = CF_DATA_CURRENT(cf);
|
||||||
ssize_t nwritten;
|
ssize_t nwritten;
|
||||||
CURLcode result;
|
CURLcode result;
|
||||||
|
|
||||||
DEBUGASSERT(data);
|
DEBUGASSERT(data);
|
||||||
nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
|
nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, &result);
|
||||||
|
CURL_TRC_CF(data, cf, "gtls_push(len=%zu) -> %zd, err=%d",
|
||||||
|
blen, nwritten, result);
|
||||||
|
backend->gtls.io_result = result;
|
||||||
if(nwritten < 0) {
|
if(nwritten < 0) {
|
||||||
struct gtls_ssl_backend_data *backend =
|
|
||||||
(struct gtls_ssl_backend_data *)connssl->backend;
|
|
||||||
gnutls_transport_set_errno(backend->gtls.session,
|
gnutls_transport_set_errno(backend->gtls.session,
|
||||||
(CURLE_AGAIN == result)? EAGAIN : EINVAL);
|
(CURLE_AGAIN == result)? EAGAIN : EINVAL);
|
||||||
nwritten = -1;
|
nwritten = -1;
|
||||||
|
@ -115,15 +118,27 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen)
|
||||||
{
|
{
|
||||||
struct Curl_cfilter *cf = s;
|
struct Curl_cfilter *cf = s;
|
||||||
struct ssl_connect_data *connssl = cf->ctx;
|
struct ssl_connect_data *connssl = cf->ctx;
|
||||||
|
struct gtls_ssl_backend_data *backend =
|
||||||
|
(struct gtls_ssl_backend_data *)connssl->backend;
|
||||||
struct Curl_easy *data = CF_DATA_CURRENT(cf);
|
struct Curl_easy *data = CF_DATA_CURRENT(cf);
|
||||||
ssize_t nread;
|
ssize_t nread;
|
||||||
CURLcode result;
|
CURLcode result;
|
||||||
|
|
||||||
DEBUGASSERT(data);
|
DEBUGASSERT(data);
|
||||||
|
if(!backend->gtls.trust_setup) {
|
||||||
|
result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls);
|
||||||
|
if(result) {
|
||||||
|
gnutls_transport_set_errno(backend->gtls.session, EINVAL);
|
||||||
|
backend->gtls.io_result = result;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
|
nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
|
||||||
|
CURL_TRC_CF(data, cf, "glts_pull(len=%zu) -> %zd, err=%d",
|
||||||
|
blen, nread, result);
|
||||||
|
backend->gtls.io_result = result;
|
||||||
if(nread < 0) {
|
if(nread < 0) {
|
||||||
struct gtls_ssl_backend_data *backend =
|
|
||||||
(struct gtls_ssl_backend_data *)connssl->backend;
|
|
||||||
gnutls_transport_set_errno(backend->gtls.session,
|
gnutls_transport_set_errno(backend->gtls.session,
|
||||||
(CURLE_AGAIN == result)? EAGAIN : EINVAL);
|
(CURLE_AGAIN == result)? EAGAIN : EINVAL);
|
||||||
nread = -1;
|
nread = -1;
|
||||||
|
@ -279,8 +294,17 @@ static CURLcode handshake(struct Curl_cfilter *cf,
|
||||||
/* socket is readable or writable */
|
/* socket is readable or writable */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
backend->gtls.io_result = CURLE_OK;
|
||||||
rc = gnutls_handshake(session);
|
rc = gnutls_handshake(session);
|
||||||
|
|
||||||
|
if(!backend->gtls.trust_setup) {
|
||||||
|
/* After having send off the ClientHello, we prepare the trust
|
||||||
|
* store to verify the coming certificate from the server */
|
||||||
|
CURLcode result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls);
|
||||||
|
if(result)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
|
if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
|
||||||
connssl->connecting_state =
|
connssl->connecting_state =
|
||||||
gnutls_record_get_direction(session)?
|
gnutls_record_get_direction(session)?
|
||||||
|
@ -301,6 +325,9 @@ static CURLcode handshake(struct Curl_cfilter *cf,
|
||||||
infof(data, "gnutls_handshake() warning: %s", strerr);
|
infof(data, "gnutls_handshake() warning: %s", strerr);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
else if((rc < 0) && backend->gtls.io_result) {
|
||||||
|
return backend->gtls.io_result;
|
||||||
|
}
|
||||||
else if(rc < 0) {
|
else if(rc < 0) {
|
||||||
const char *strerr = NULL;
|
const char *strerr = NULL;
|
||||||
|
|
||||||
|
@ -423,13 +450,95 @@ set_ssl_version_min_max(struct Curl_easy *data,
|
||||||
return CURLE_SSL_CONNECT_ERROR;
|
return CURLE_SSL_CONNECT_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CURLcode gtls_client_init(struct Curl_easy *data,
|
CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf,
|
||||||
struct ssl_primary_config *config,
|
struct Curl_easy *data,
|
||||||
struct ssl_config_data *ssl_config,
|
struct gtls_ctx *gtls)
|
||||||
struct ssl_peer *peer,
|
|
||||||
struct gtls_ctx *gtls,
|
|
||||||
long *pverifyresult)
|
|
||||||
{
|
{
|
||||||
|
struct ssl_primary_config *config = Curl_ssl_cf_get_primary_config(cf);
|
||||||
|
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
CURL_TRC_CF(data, cf, "setup trust anchors and CRLs");
|
||||||
|
if(config->verifypeer) {
|
||||||
|
bool imported_native_ca = false;
|
||||||
|
|
||||||
|
if(ssl_config->native_ca_store) {
|
||||||
|
rc = gnutls_certificate_set_x509_system_trust(gtls->cred);
|
||||||
|
if(rc < 0)
|
||||||
|
infof(data, "error reading native ca store (%s), continuing anyway",
|
||||||
|
gnutls_strerror(rc));
|
||||||
|
else {
|
||||||
|
infof(data, "found %d certificates in native ca store", rc);
|
||||||
|
if(rc > 0)
|
||||||
|
imported_native_ca = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(config->CAfile) {
|
||||||
|
/* set the trusted CA cert bundle file */
|
||||||
|
gnutls_certificate_set_verify_flags(gtls->cred,
|
||||||
|
GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
|
||||||
|
|
||||||
|
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)%s",
|
||||||
|
config->CAfile, gnutls_strerror(rc),
|
||||||
|
(imported_native_ca ? ", continuing anyway" : ""));
|
||||||
|
if(!imported_native_ca) {
|
||||||
|
ssl_config->certverifyresult = rc;
|
||||||
|
return CURLE_SSL_CACERT_BADFILE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
infof(data, "found %d certificates in %s", rc, config->CAfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(config->CApath) {
|
||||||
|
/* set the trusted CA cert directory */
|
||||||
|
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)%s",
|
||||||
|
config->CApath, gnutls_strerror(rc),
|
||||||
|
(imported_native_ca ? ", continuing anyway" : ""));
|
||||||
|
if(!imported_native_ca) {
|
||||||
|
ssl_config->certverifyresult = rc;
|
||||||
|
return CURLE_SSL_CACERT_BADFILE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
infof(data, "found %d certificates in %s", rc, config->CApath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(config->CRLfile) {
|
||||||
|
/* set the CRL list file */
|
||||||
|
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)",
|
||||||
|
config->CRLfile, gnutls_strerror(rc));
|
||||||
|
return CURLE_SSL_CRL_BADFILE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
infof(data, "found %d CRL in %s", rc, config->CRLfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
gtls->trust_setup = TRUE;
|
||||||
|
return CURLE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static CURLcode gtls_client_init(struct Curl_cfilter *cf,
|
||||||
|
struct Curl_easy *data,
|
||||||
|
struct ssl_peer *peer,
|
||||||
|
struct gtls_ctx *gtls)
|
||||||
|
{
|
||||||
|
struct ssl_primary_config *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;
|
unsigned int init_flags;
|
||||||
int rc;
|
int rc;
|
||||||
bool sni = TRUE; /* default is SNI enabled */
|
bool sni = TRUE; /* default is SNI enabled */
|
||||||
|
@ -441,8 +550,6 @@ static CURLcode gtls_client_init(struct Curl_easy *data,
|
||||||
if(!gtls_inited)
|
if(!gtls_inited)
|
||||||
gtls_init();
|
gtls_init();
|
||||||
|
|
||||||
*pverifyresult = 0;
|
|
||||||
|
|
||||||
if(config->version == CURL_SSLVERSION_SSLv2) {
|
if(config->version == CURL_SSLVERSION_SSLv2) {
|
||||||
failf(data, "GnuTLS does not support SSLv2");
|
failf(data, "GnuTLS does not support SSLv2");
|
||||||
return CURLE_SSL_CONNECT_ERROR;
|
return CURLE_SSL_CONNECT_ERROR;
|
||||||
|
@ -479,74 +586,7 @@ static CURLcode gtls_client_init(struct Curl_easy *data,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if(config->verifypeer) {
|
ssl_config->certverifyresult = 0;
|
||||||
bool imported_native_ca = false;
|
|
||||||
|
|
||||||
if(ssl_config->native_ca_store) {
|
|
||||||
rc = gnutls_certificate_set_x509_system_trust(gtls->cred);
|
|
||||||
if(rc < 0)
|
|
||||||
infof(data, "error reading native ca store (%s), continuing anyway",
|
|
||||||
gnutls_strerror(rc));
|
|
||||||
else {
|
|
||||||
infof(data, "found %d certificates in native ca store", rc);
|
|
||||||
if(rc > 0)
|
|
||||||
imported_native_ca = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(config->CAfile) {
|
|
||||||
/* set the trusted CA cert bundle file */
|
|
||||||
gnutls_certificate_set_verify_flags(gtls->cred,
|
|
||||||
GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
|
|
||||||
|
|
||||||
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)%s",
|
|
||||||
config->CAfile, gnutls_strerror(rc),
|
|
||||||
(imported_native_ca ? ", continuing anyway" : ""));
|
|
||||||
if(!imported_native_ca) {
|
|
||||||
*pverifyresult = rc;
|
|
||||||
return CURLE_SSL_CACERT_BADFILE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
infof(data, "found %d certificates in %s", rc, config->CAfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(config->CApath) {
|
|
||||||
/* set the trusted CA cert directory */
|
|
||||||
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)%s",
|
|
||||||
config->CApath, gnutls_strerror(rc),
|
|
||||||
(imported_native_ca ? ", continuing anyway" : ""));
|
|
||||||
if(!imported_native_ca) {
|
|
||||||
*pverifyresult = rc;
|
|
||||||
return CURLE_SSL_CACERT_BADFILE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
infof(data, "found %d certificates in %s", rc, config->CApath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(config->CRLfile) {
|
|
||||||
/* set the CRL list file */
|
|
||||||
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)",
|
|
||||||
config->CRLfile, gnutls_strerror(rc));
|
|
||||||
return CURLE_SSL_CRL_BADFILE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
infof(data, "found %d CRL in %s", rc, config->CRLfile);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize TLS session as a client */
|
/* Initialize TLS session as a client */
|
||||||
init_flags = GNUTLS_CLIENT;
|
init_flags = GNUTLS_CLIENT;
|
||||||
|
@ -634,6 +674,11 @@ static CURLcode gtls_client_init(struct Curl_easy *data,
|
||||||
}
|
}
|
||||||
|
|
||||||
if(config->clientcert) {
|
if(config->clientcert) {
|
||||||
|
if(!gtls->trust_setup) {
|
||||||
|
result = Curl_gtls_client_trust_setup(cf, data, gtls);
|
||||||
|
if(result)
|
||||||
|
return result;
|
||||||
|
}
|
||||||
if(ssl_config->key_passwd) {
|
if(ssl_config->key_passwd) {
|
||||||
const unsigned int supported_key_encryption_algorithms =
|
const unsigned int supported_key_encryption_algorithms =
|
||||||
GNUTLS_PKCS_USE_PKCS12_3DES | GNUTLS_PKCS_USE_PKCS12_ARCFOUR |
|
GNUTLS_PKCS_USE_PKCS12_3DES | GNUTLS_PKCS_USE_PKCS12_ARCFOUR |
|
||||||
|
@ -725,14 +770,11 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
|
||||||
void *ssl_user_data)
|
void *ssl_user_data)
|
||||||
{
|
{
|
||||||
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
|
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;
|
CURLcode result;
|
||||||
|
|
||||||
DEBUGASSERT(gctx);
|
DEBUGASSERT(gctx);
|
||||||
|
|
||||||
result = gtls_client_init(data, conn_config, ssl_config, peer,
|
result = gtls_client_init(cf, data, peer, gctx);
|
||||||
gctx, pverifyresult);
|
|
||||||
if(result)
|
if(result)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
|
@ -1515,12 +1557,13 @@ static ssize_t gtls_send(struct Curl_cfilter *cf,
|
||||||
|
|
||||||
(void)data;
|
(void)data;
|
||||||
DEBUGASSERT(backend);
|
DEBUGASSERT(backend);
|
||||||
|
backend->gtls.io_result = CURLE_OK;
|
||||||
rc = gnutls_record_send(backend->gtls.session, mem, len);
|
rc = gnutls_record_send(backend->gtls.session, mem, len);
|
||||||
|
|
||||||
if(rc < 0) {
|
if(rc < 0) {
|
||||||
*curlcode = (rc == GNUTLS_E_AGAIN)
|
*curlcode = (rc == GNUTLS_E_AGAIN)?
|
||||||
? CURLE_AGAIN
|
CURLE_AGAIN :
|
||||||
: CURLE_SEND_ERROR;
|
(backend->gtls.io_result? backend->gtls.io_result : CURLE_SEND_ERROR);
|
||||||
|
|
||||||
rc = -1;
|
rc = -1;
|
||||||
}
|
}
|
||||||
|
@ -1656,6 +1699,7 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf,
|
||||||
(void)data;
|
(void)data;
|
||||||
DEBUGASSERT(backend);
|
DEBUGASSERT(backend);
|
||||||
|
|
||||||
|
backend->gtls.io_result = CURLE_OK;
|
||||||
ret = gnutls_record_recv(backend->gtls.session, buf, buffersize);
|
ret = gnutls_record_recv(backend->gtls.session, buf, buffersize);
|
||||||
if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
|
if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
|
||||||
*curlcode = CURLE_AGAIN;
|
*curlcode = CURLE_AGAIN;
|
||||||
|
@ -1680,7 +1724,8 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf,
|
||||||
failf(data, "GnuTLS recv error (%d): %s",
|
failf(data, "GnuTLS recv error (%d): %s",
|
||||||
|
|
||||||
(int)ret, gnutls_strerror((int)ret));
|
(int)ret, gnutls_strerror((int)ret));
|
||||||
*curlcode = CURLE_RECV_ERROR;
|
*curlcode = backend->gtls.io_result?
|
||||||
|
backend->gtls.io_result : CURLE_RECV_ERROR;
|
||||||
ret = -1;
|
ret = -1;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,8 @@ struct gtls_ctx {
|
||||||
#ifdef USE_GNUTLS_SRP
|
#ifdef USE_GNUTLS_SRP
|
||||||
gnutls_srp_client_credentials_t srp_client_cred;
|
gnutls_srp_client_credentials_t srp_client_cred;
|
||||||
#endif
|
#endif
|
||||||
|
CURLcode io_result; /* result of last IO cfilter operation */
|
||||||
|
BIT(trust_setup); /* x509 anchors + CRLs have been set up */
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef CURLcode Curl_gtls_ctx_setup_cb(struct Curl_cfilter *cf,
|
typedef CURLcode Curl_gtls_ctx_setup_cb(struct Curl_cfilter *cf,
|
||||||
|
@ -66,13 +68,16 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
|
||||||
void *cb_user_data,
|
void *cb_user_data,
|
||||||
void *ssl_user_data);
|
void *ssl_user_data);
|
||||||
|
|
||||||
CURLcode
|
CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf,
|
||||||
Curl_gtls_verifyserver(struct Curl_easy *data,
|
struct Curl_easy *data,
|
||||||
gnutls_session_t session,
|
struct gtls_ctx *gtls);
|
||||||
struct ssl_primary_config *config,
|
|
||||||
struct ssl_config_data *ssl_config,
|
CURLcode Curl_gtls_verifyserver(struct Curl_easy *data,
|
||||||
struct ssl_peer *peer,
|
gnutls_session_t session,
|
||||||
const char *pinned_key);
|
struct ssl_primary_config *config,
|
||||||
|
struct ssl_config_data *ssl_config,
|
||||||
|
struct ssl_peer *peer,
|
||||||
|
const char *pinned_key);
|
||||||
|
|
||||||
extern const struct Curl_ssl Curl_ssl_gnutls;
|
extern const struct Curl_ssl Curl_ssl_gnutls;
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче