proxy: Support HTTPS proxy and SOCKS+HTTP(s)

HTTPS proxies:

An HTTPS proxy receives all transactions over an SSL/TLS connection. Once a
secure connection with the proxy is established, the user agent uses the proxy
as usual, including sending CONNECT requests to instruct the proxy to establish
a [usually secure] TCP tunnel with an origin server. HTTPS proxies protect
nearly all aspects of user-proxy communications as opposed to HTTP proxies that
receive all requests (including CONNECT requests) in vulnerable clear text.

With HTTPS proxies, it is possible to have two concurrent _nested_ SSL/TLS
sessions: the "outer" one between the user agent and the proxy and the "inner"
one between the user agent and the origin server (through the proxy). This
change adds supports for such nested sessions as well.

The secure connection with the proxy requires its own set of the usual
SSL/TLS-related options (their descriptions need polishing):

  --proxy-cacert FILE        CA certificate to verify peer against
  --proxy-capath DIR         CA directory to verify peer against
  --proxy-cert CERT[:PASSWD] Client certificate file and password
  --proxy-cert-type TYPE     Certificate file type (DER/PEM/ENG)
  --proxy-ciphers LIST       SSL ciphers to use
  --proxy-crlfile FILE       Get a CRL list in PEM format from the given file
  --proxy-insecure           Allow connections to SSL sites without certs
  --proxy-key KEY            Private key file name
  --proxy-key-type TYPE      Private key file type (DER/PEM/ENG)
  --proxy-pass PASS          Pass phrase for the private key
  --proxy-ssl-allow-beast    Allow security flaw to improve interop
  --proxy-sslv2              Use SSLv2
  --proxy-sslv3              Use SSLv3
  --proxy-tlsv1              Use TLSv1
  --proxy-tlsuser USER       TLS username
  --proxy-tlspassword STRING TLS password
  --proxy-tlsauthtype STRING TLS authentication type (default SRP)

All --proxy-foo options are independent from their --foo counterparts, except
--proxy-crlfile defaults to --crlfile and --proxy-capath defaults to --capath.

Curl now also supports %{proxy_ssl_verify_result} --write-out variable,
similar to the existing %{ssl_verify_result} variable.

SOCKS proxy + HTTP/HTTPS proxy combination:

If both --socks* and --proxy options are given, Curl first connects to the
SOCKS proxy and then connects (through SOCKS) to the HTTP or HTTPS proxy.
This commit is contained in:
Alex Rousskov
2015-06-18 16:10:42 +02:00
committed by Daniel Stenberg
parent 69c77f69a5
commit 3d90ec5448
48 changed files with 1535 additions and 758 deletions

View File

@@ -160,7 +160,7 @@ static CURLcode connect_prep(struct connectdata *conn, int sockindex)
/* axTLS only supports TLSv1 */
/* check to see if we've been told to use an explicit SSL/TLS version */
switch(data->set.ssl.version) {
switch(conn->ssl_config.version) {
case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
break;
@@ -185,17 +185,17 @@ static CURLcode connect_prep(struct connectdata *conn, int sockindex)
conn->ssl[sockindex].ssl = NULL;
/* Load the trusted CA cert bundle file */
if(data->set.ssl.CAfile) {
if(ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CACERT, data->set.ssl.CAfile, NULL)
!= SSL_OK) {
if(conn->ssl_config.CAfile) {
if(ssl_obj_load(ssl_ctx, SSL_OBJ_X509_CACERT,
conn->ssl_config.CAfile, NULL) != SSL_OK) {
infof(data, "error reading ca cert file %s \n",
data->set.ssl.CAfile);
if(data->set.ssl.verifypeer) {
conn->ssl_config.CAfile);
if(conn->ssl_config.verifypeer) {
return CURLE_SSL_CACERT_BADFILE;
}
}
else
infof(data, "found certificates in %s\n", data->set.ssl.CAfile);
infof(data, "found certificates in %s\n", conn->ssl_config.CAfile);
}
/* gtls.c tasks we're skipping for now:
@@ -207,15 +207,15 @@ static CURLcode connect_prep(struct connectdata *conn, int sockindex)
*/
/* Load client certificate */
if(data->set.str[STRING_CERT]) {
if(data->set.ssl.cert) {
i=0;
/* Instead of trying to analyze cert type here, let axTLS try them all. */
while(cert_types[i] != 0) {
ssl_fcn_return = ssl_obj_load(ssl_ctx, cert_types[i],
data->set.str[STRING_CERT], NULL);
data->set.ssl.cert, NULL);
if(ssl_fcn_return == SSL_OK) {
infof(data, "successfully read cert file %s \n",
data->set.str[STRING_CERT]);
data->set.ssl.cert);
break;
}
i++;
@@ -223,7 +223,7 @@ static CURLcode connect_prep(struct connectdata *conn, int sockindex)
/* Tried all cert types, none worked. */
if(cert_types[i] == 0) {
failf(data, "%s is not x509 or pkcs12 format",
data->set.str[STRING_CERT]);
data->set.ssl.cert);
return CURLE_SSL_CERTPROBLEM;
}
}
@@ -231,15 +231,15 @@ static CURLcode connect_prep(struct connectdata *conn, int sockindex)
/* Load client key.
If a pkcs12 file successfully loaded a cert, then there's nothing to do
because the key has already been loaded. */
if(data->set.str[STRING_KEY] && cert_types[i] != SSL_OBJ_PKCS12) {
if(data->set.ssl.key && cert_types[i] != SSL_OBJ_PKCS12) {
i=0;
/* Instead of trying to analyze key type here, let axTLS try them all. */
while(key_types[i] != 0) {
ssl_fcn_return = ssl_obj_load(ssl_ctx, key_types[i],
data->set.str[STRING_KEY], NULL);
data->set.ssl.key, NULL);
if(ssl_fcn_return == SSL_OK) {
infof(data, "successfully read key file %s \n",
data->set.str[STRING_KEY]);
data->set.ssl.key);
break;
}
i++;
@@ -247,7 +247,7 @@ static CURLcode connect_prep(struct connectdata *conn, int sockindex)
/* Tried all key types, none worked. */
if(key_types[i] == 0) {
failf(data, "Failure: %s is not a supported key file",
data->set.str[STRING_KEY]);
data->set.ssl.key);
return CURLE_SSL_CONNECT_ERROR;
}
}
@@ -259,7 +259,8 @@ static CURLcode connect_prep(struct connectdata *conn, int sockindex)
*/
/* In axTLS, handshaking happens inside ssl_client_new. */
if(!Curl_ssl_getsessionid(conn, (void **) &ssl_sessionid, &ssl_idsize)) {
if(!Curl_ssl_getsessionid(conn, (void **) &ssl_sessionid, &ssl_idsize,
sockindex)) {
/* we got a session id, use it! */
infof (data, "SSL re-using session ID\n");
ssl = ssl_client_new(ssl_ctx, conn->sock[sockindex],
@@ -293,7 +294,7 @@ static CURLcode connect_finish(struct connectdata *conn, int sockindex)
*/
/* Verify server's certificate */
if(data->set.ssl.verifypeer) {
if(conn->ssl_config.verifypeer) {
if(ssl_verify_cert(ssl) != SSL_OK) {
Curl_axtls_close(conn, sockindex);
failf(data, "server cert verify failed");
@@ -359,7 +360,7 @@ static CURLcode connect_finish(struct connectdata *conn, int sockindex)
}
else {
if(!Curl_cert_hostcheck((const char *)peer_CN, conn->host.name)) {
if(data->set.ssl.verifyhost) {
if(conn->ssl_config.verifyhost) {
/* Break connection ! */
Curl_axtls_close(conn, sockindex);
failf(data, "\tcommon name \"%s\" does not match \"%s\"\n",
@@ -381,7 +382,7 @@ static CURLcode connect_finish(struct connectdata *conn, int sockindex)
/* Put our freshly minted SSL session in cache */
ssl_idsize = ssl_get_session_id_size(ssl);
ssl_sessionid = ssl_get_session_id(ssl);
if(Curl_ssl_addsessionid(conn, (void *) ssl_sessionid, ssl_idsize)
if(Curl_ssl_addsessionid(conn, (void *) ssl_sessionid, ssl_idsize, sockindex)
!= CURLE_OK)
infof (data, "failed to add session to cache\n");

View File

@@ -116,7 +116,7 @@ cyassl_connect_step1(struct connectdata *conn,
return CURLE_OK;
/* check to see if we've been told to use an explicit SSL/TLS version */
switch(data->set.ssl.version) {
switch(conn->ssl_config.version) {
case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
#if LIBCYASSL_VERSION_HEX >= 0x03003000 /* >= 3.3.0 */
@@ -190,18 +190,18 @@ cyassl_connect_step1(struct connectdata *conn,
#ifndef NO_FILESYSTEM
/* load trusted cacert */
if(data->set.str[STRING_SSL_CAFILE]) {
if(conn->ssl_config.CAfile) {
if(1 != SSL_CTX_load_verify_locations(conssl->ctx,
data->set.str[STRING_SSL_CAFILE],
data->set.str[STRING_SSL_CAPATH])) {
if(data->set.ssl.verifypeer) {
conn->ssl_config.CAfile,
conn->ssl_config.CApath)) {
if(conn->ssl_config.verifypeer) {
/* Fail if we insist on successfully verifying the server. */
failf(data, "error setting certificate verify locations:\n"
" CAfile: %s\n CApath: %s",
data->set.str[STRING_SSL_CAFILE]?
data->set.str[STRING_SSL_CAFILE]: "none",
data->set.str[STRING_SSL_CAPATH]?
data->set.str[STRING_SSL_CAPATH] : "none");
conn->ssl_config.CAfile?
conn->ssl_config.CAfile: "none",
conn->ssl_config.CApath?
conn->ssl_config.CApath : "none");
return CURLE_SSL_CACERT_BADFILE;
}
else {
@@ -218,25 +218,25 @@ cyassl_connect_step1(struct connectdata *conn,
infof(data,
" CAfile: %s\n"
" CApath: %s\n",
data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]:
conn->ssl_config.CAfile ? conn->ssl_config.CAfile:
"none",
data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]:
conn->ssl_config.CApath ? conn->ssl_config.CApath:
"none");
}
/* Load the client certificate, and private key */
if(data->set.str[STRING_CERT] && data->set.str[STRING_KEY]) {
int file_type = do_file_type(data->set.str[STRING_CERT_TYPE]);
if(data->set.ssl.cert && data->set.ssl.key) {
int file_type = do_file_type(data->set.ssl.cert_type);
if(SSL_CTX_use_certificate_file(conssl->ctx, data->set.str[STRING_CERT],
if(SSL_CTX_use_certificate_file(conssl->ctx, data->set.ssl.cert,
file_type) != 1) {
failf(data, "unable to use client certificate (no key or wrong pass"
" phrase?)");
return CURLE_SSL_CONNECT_ERROR;
}
file_type = do_file_type(data->set.str[STRING_KEY_TYPE]);
if(SSL_CTX_use_PrivateKey_file(conssl->ctx, data->set.str[STRING_KEY],
file_type = do_file_type(data->set.ssl.key_type);
if(SSL_CTX_use_PrivateKey_file(conssl->ctx, data->set.ssl.key,
file_type) != 1) {
failf(data, "unable to set private key");
return CURLE_SSL_CONNECT_ERROR;
@@ -249,7 +249,8 @@ cyassl_connect_step1(struct connectdata *conn,
* anyway. In the latter case the result of the verification is checked with
* SSL_get_verify_result() below. */
SSL_CTX_set_verify(conssl->ctx,
data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
conn->ssl_config.verifypeer?SSL_VERIFY_PEER:
SSL_VERIFY_NONE,
NULL);
#ifdef HAVE_SNI
@@ -302,7 +303,7 @@ cyassl_connect_step1(struct connectdata *conn,
}
/* Check if there's a cached ID we can/should use here! */
if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL, sockindex)) {
/* we got a session id, use it! */
if(!SSL_set_session(conssl->handle, ssl_sessionid)) {
failf(data, "SSL: SSL_set_session failed: %s",
@@ -336,7 +337,7 @@ cyassl_connect_step2(struct connectdata *conn,
conn->send[sockindex] = cyassl_send;
/* Enable RFC2818 checks */
if(data->set.ssl.verifyhost) {
if(conn->ssl_config.verifyhost) {
ret = CyaSSL_check_domain_name(conssl->handle, conn->host.name);
if(ret == SSL_FAILURE)
return CURLE_OUT_OF_MEMORY;
@@ -369,7 +370,7 @@ cyassl_connect_step2(struct connectdata *conn,
* CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA error. The only
* way to do this is currently to switch the CyaSSL_check_domain_name()
* in and out based on the 'data->set.ssl.verifyhost' value. */
if(data->set.ssl.verifyhost) {
if(conn->ssl_config.verifyhost) {
failf(data,
"\tsubject alt name(s) or common name do not match \"%s\"\n",
conn->host.dispname);
@@ -464,7 +465,8 @@ cyassl_connect_step3(struct connectdata *conn,
our_ssl_sessionid = SSL_get_session(connssl->handle);
incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL,
sockindex));
if(incache) {
if(old_ssl_sessionid != our_ssl_sessionid) {
infof(data, "old SSL session ID is stale, removing\n");
@@ -475,7 +477,7 @@ cyassl_connect_step3(struct connectdata *conn,
if(!incache) {
result = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
0 /* unknown size */);
0 /* unknown size */, sockindex);
if(result) {
failf(data, "failed to store ssl session");
return result;

View File

@@ -1055,7 +1055,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
/* check to see if we've been told to use an explicit SSL/TLS version */
#if CURL_BUILD_MAC_10_8 || CURL_BUILD_IOS
if(SSLSetProtocolVersionMax != NULL) {
switch(data->set.ssl.version) {
switch(conn->ssl_config.version) {
default:
case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
@@ -1096,7 +1096,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
(void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx,
kSSLProtocolAll,
false);
switch (data->set.ssl.version) {
switch (conn->ssl_config.version) {
default:
case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
@@ -1148,7 +1148,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
}
#else
(void)SSLSetProtocolVersionEnabled(connssl->ssl_ctx, kSSLProtocolAll, false);
switch(data->set.ssl.version) {
switch(conn->ssl_config.version) {
default:
case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
@@ -1293,7 +1293,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
#else
if(SSLSetSessionOption != NULL) {
#endif /* CURL_BUILD_MAC */
bool break_on_auth = !data->set.ssl.verifypeer ||
bool break_on_auth = !conn->ssl_config.verifypeer ||
data->set.str[STRING_SSL_CAFILE];
err = SSLSetSessionOption(connssl->ssl_ctx,
kSSLSessionOptionBreakOnServerAuth,
@@ -1306,7 +1306,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
else {
#if CURL_SUPPORT_MAC_10_8
err = SSLSetEnableCertVerify(connssl->ssl_ctx,
data->set.ssl.verifypeer?true:false);
conn->ssl_config.verifypeer?true:false);
if(err != noErr) {
failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err);
return CURLE_SSL_CONNECT_ERROR;
@@ -1315,7 +1315,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
}
#else
err = SSLSetEnableCertVerify(connssl->ssl_ctx,
data->set.ssl.verifypeer?true:false);
conn->ssl_config.verifypeer?true:false);
if(err != noErr) {
failf(data, "SSL: SSLSetEnableCertVerify() failed: OSStatus %d", err);
return CURLE_SSL_CONNECT_ERROR;
@@ -1340,7 +1340,7 @@ static CURLcode darwinssl_connect_step1(struct connectdata *conn,
/* Configure hostname check. SNI is used if available.
* Both hostname check and SNI require SSLSetPeerDomainName().
* Also: the verifyhost setting influences SNI usage */
if(data->set.ssl.verifyhost) {
if(conn->ssl_config.verifyhost) {
err = SSLSetPeerDomainName(connssl->ssl_ctx, conn->host.name,
strlen(conn->host.name));

View File

@@ -411,11 +411,11 @@ gtls_connect_step1(struct connectdata *conn,
Curl_gtls_init();
/* GnuTLS only supports SSLv3 and TLSv1 */
if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) {
if(conn->ssl_config.version == CURL_SSLVERSION_SSLv2) {
failf(data, "GnuTLS does not support SSLv2");
return CURLE_SSL_CONNECT_ERROR;
}
else if(data->set.ssl.version == CURL_SSLVERSION_SSLv3)
else if(conn->ssl_config.version == CURL_SSLVERSION_SSLv3)
sni = FALSE; /* SSLv3 has no SNI */
/* allocate a cred struct */
@@ -449,23 +449,23 @@ gtls_connect_step1(struct connectdata *conn,
}
#endif
if(data->set.ssl.CAfile) {
if(conn->ssl_config.CAfile) {
/* set the trusted CA cert bundle file */
gnutls_certificate_set_verify_flags(conn->ssl[sockindex].cred,
GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT);
rc = gnutls_certificate_set_x509_trust_file(conn->ssl[sockindex].cred,
data->set.ssl.CAfile,
conn->ssl_config.CAfile,
GNUTLS_X509_FMT_PEM);
if(rc < 0) {
infof(data, "error reading ca cert file %s (%s)\n",
data->set.ssl.CAfile, gnutls_strerror(rc));
if(data->set.ssl.verifypeer)
conn->ssl_config.CAfile, gnutls_strerror(rc));
if(conn->ssl_config.verifypeer)
return CURLE_SSL_CACERT_BADFILE;
}
else
infof(data, "found %d certificates in %s\n",
rc, data->set.ssl.CAfile);
rc, conn->ssl_config.CAfile);
}
#ifdef HAS_CAPATH
@@ -544,7 +544,7 @@ gtls_connect_step1(struct connectdata *conn,
return CURLE_SSL_CONNECT_ERROR;
}
switch (data->set.ssl.version) {
switch (conn->ssl_config.version) {
case CURL_SSLVERSION_SSLv3:
protocol_priority[0] = GNUTLS_SSL3;
break;
@@ -579,7 +579,7 @@ gtls_connect_step1(struct connectdata *conn,
/* Ensure +SRP comes at the *end* of all relevant strings so that it can be
* removed if a run-time error indicates that SRP is not supported by this
* GnuTLS version */
switch (data->set.ssl.version) {
switch (conn->ssl_config.version) {
case CURL_SSLVERSION_SSLv3:
prioritylist = GNUTLS_CIPHERS ":-VERS-TLS-ALL:+VERS-SSL3.0";
sni = false;
@@ -654,13 +654,13 @@ gtls_connect_step1(struct connectdata *conn,
}
#endif
if(data->set.str[STRING_CERT]) {
if(data->set.ssl.cert) {
if(gnutls_certificate_set_x509_key_file(
conn->ssl[sockindex].cred,
data->set.str[STRING_CERT],
data->set.str[STRING_KEY] ?
data->set.str[STRING_KEY] : data->set.str[STRING_CERT],
do_file_type(data->set.str[STRING_CERT_TYPE]) ) !=
data->set.ssl.cert,
data->set.ssl.key ?
data->set.ssl.key : data->set.ssl.cert,
do_file_type(data->set.ssl.cert_type) ) !=
GNUTLS_E_SUCCESS) {
failf(data, "error reading X.509 key or certificate file");
return CURLE_SSL_CONNECT_ERROR;
@@ -712,7 +712,7 @@ gtls_connect_step1(struct connectdata *conn,
/* This might be a reconnect, so we check for a session ID in the cache
to speed up things */
if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, &ssl_idsize)) {
if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, &ssl_idsize, sockindex)) {
/* we got a session id, use it! */
gnutls_session_set_data(session, ssl_sessionid, ssl_idsize);
@@ -825,13 +825,13 @@ gtls_connect_step3(struct connectdata *conn,
chainp = gnutls_certificate_get_peers(session, &cert_list_size);
if(!chainp) {
if(data->set.ssl.verifypeer ||
data->set.ssl.verifyhost ||
if(conn->ssl_config.verifypeer ||
conn->ssl_config.verifyhost ||
data->set.ssl.issuercert) {
#ifdef USE_TLS_SRP
if(data->set.ssl.authtype == CURL_TLSAUTH_SRP
&& data->set.ssl.username != NULL
&& !data->set.ssl.verifypeer
&& !conn->ssl_config.verifypeer
&& gnutls_cipher_get(session)) {
/* no peer cert, but auth is ok if we have SRP user and cipher and no
peer verify */
@@ -864,7 +864,7 @@ gtls_connect_step3(struct connectdata *conn,
}
}
if(data->set.ssl.verifypeer) {
if(conn->ssl_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
@@ -880,9 +880,10 @@ gtls_connect_step3(struct connectdata *conn,
/* verify_status is a bitmask of gnutls_certificate_status bits */
if(verify_status & GNUTLS_CERT_INVALID) {
if(data->set.ssl.verifypeer) {
if(conn->ssl_config.verifypeer) {
failf(data, "server certificate verification failed. CAfile: %s "
"CRLfile: %s", data->set.ssl.CAfile?data->set.ssl.CAfile:"none",
"CRLfile: %s", conn->ssl_config.CAfile ? conn->ssl_config.CAfile:
"none",
data->set.ssl.CRLfile?data->set.ssl.CRLfile:"none");
return CURLE_SSL_CACERT;
}
@@ -1085,7 +1086,7 @@ gtls_connect_step3(struct connectdata *conn,
}
#endif
if(!rc) {
if(data->set.ssl.verifyhost) {
if(conn->ssl_config.verifyhost) {
failf(data, "SSL: certificate subject name (%s) does not match "
"target host name '%s'", certbuf, conn->host.dispname);
gnutls_x509_crt_deinit(x509_cert);
@@ -1102,7 +1103,7 @@ gtls_connect_step3(struct connectdata *conn,
certclock = gnutls_x509_crt_get_expiration_time(x509_cert);
if(certclock == (time_t)-1) {
if(data->set.ssl.verifypeer) {
if(conn->ssl_config.verifypeer) {
failf(data, "server cert expiration date verify failed");
gnutls_x509_crt_deinit(x509_cert);
return CURLE_SSL_CONNECT_ERROR;
@@ -1127,7 +1128,7 @@ gtls_connect_step3(struct connectdata *conn,
certclock = gnutls_x509_crt_get_activation_time(x509_cert);
if(certclock == (time_t)-1) {
if(data->set.ssl.verifypeer) {
if(conn->ssl_config.verifypeer) {
failf(data, "server cert activation date verify failed");
gnutls_x509_crt_deinit(x509_cert);
return CURLE_SSL_CONNECT_ERROR;
@@ -1245,7 +1246,8 @@ gtls_connect_step3(struct connectdata *conn,
/* extract session ID to the allocated buffer */
gnutls_session_get_data(session, connect_sessionid, &connect_idsize);
incache = !(Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL));
incache = !(Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL,
sockindex));
if(incache) {
/* there was one before in the cache, so instead of risking that the
previous one was rejected, we just kill that and store the new */
@@ -1253,7 +1255,8 @@ gtls_connect_step3(struct connectdata *conn,
}
/* store this session id */
result = Curl_ssl_addsessionid(conn, connect_sessionid, connect_idsize);
result = Curl_ssl_addsessionid(conn, connect_sessionid, connect_idsize,
sockindex);
if(result) {
free(connect_sessionid);
result = CURLE_OUT_OF_MEMORY;
@@ -1508,8 +1511,8 @@ static int Curl_gtls_seed(struct SessionHandle *data)
/* Quickly add a bit of entropy */
gcry_fast_random_poll();
if(!ssl_seeded || data->set.str[STRING_SSL_RANDOM_FILE] ||
data->set.str[STRING_SSL_EGDSOCKET]) {
if(!ssl_seeded || data->set.ssl.primary.random_file ||
data->set.ssl.primary.egdsocket) {
/* TODO: to a good job seeding the RNG
This may involve the gcry_control function and these options:

View File

@@ -313,9 +313,8 @@ static int is_file(const char *filename)
* should be later deallocated using free(). If the OOM failure occurs, we
* return NULL, too.
*/
static char* dup_nickname(struct SessionHandle *data, enum dupstring cert_kind)
static char* dup_nickname(struct SessionHandle *data, const char *str)
{
const char *str = data->set.str[cert_kind];
const char *n;
if(!is_file(str))
@@ -566,7 +565,7 @@ static CURLcode nss_load_key(struct connectdata *conn, int sockindex,
PK11_IsPresent(slot);
status = PK11_Authenticate(slot, PR_TRUE,
conn->data->set.str[STRING_KEY_PASSWD]);
conn->data->set.ssl.key_passwd);
PK11_FreeSlot(slot);
return (SECSuccess == status) ? CURLE_OK : CURLE_SSL_CERTPROBLEM;
@@ -645,7 +644,7 @@ static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig,
struct connectdata *conn = (struct connectdata *)arg;
#ifdef SSL_ENABLE_OCSP_STAPLING
if(conn->data->set.ssl.verifystatus) {
if(conn->ssl_config.verifystatus) {
SECStatus cacheResult;
const SECItemArray *csa = SSL_PeerStapledOCSPResponses(fd);
@@ -671,7 +670,7 @@ static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig,
}
#endif
if(!conn->data->set.ssl.verifypeer) {
if(!conn->ssl_config.verifypeer) {
infof(conn->data, "skipping SSL peer certificate verification\n");
return SECSuccess;
}
@@ -893,7 +892,7 @@ static SECStatus BadCertHandler(void *arg, PRFileDesc *sock)
/* remember the cert verification result */
data->set.ssl.certverifyresult = err;
if(err == SSL_ERROR_BAD_CERT_DOMAIN && !data->set.ssl.verifyhost)
if(err == SSL_ERROR_BAD_CERT_DOMAIN && !conn->ssl_config.verifyhost)
/* we are asked not to verify the host name */
return SECSuccess;
@@ -1399,8 +1398,8 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn,
int sockindex)
{
struct SessionHandle *data = conn->data;
const char *cafile = data->set.ssl.CAfile;
const char *capath = data->set.ssl.CApath;
const char *cafile = conn->ssl_config.CAfile;
const char *capath = conn->ssl_config.CApath;
if(cafile) {
CURLcode result = nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE);
@@ -1450,7 +1449,7 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn,
static CURLcode nss_init_sslver(SSLVersionRange *sslver,
struct SessionHandle *data)
{
switch(data->set.ssl.version) {
switch (conn->ssl_config.version) {
default:
case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
@@ -1609,7 +1608,7 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
goto error;
/* do not use SSL cache if disabled or we are not going to verify peer */
ssl_no_cache = (conn->ssl_config.sessionid && data->set.ssl.verifypeer) ?
ssl_no_cache = (conn->ssl_config.sessionid && conn->ssl_config.verifypeer) ?
PR_FALSE : PR_TRUE;
if(SSL_OptionSet(model, SSL_NO_CACHE, ssl_no_cache) != SECSuccess)
goto error;
@@ -1620,7 +1619,7 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
if(SSL_VersionRangeSet(model, &sslver) != SECSuccess)
goto error;
ssl_cbc_random_iv = !data->set.ssl_enable_beast;
ssl_cbc_random_iv = !data->set.ssl.enable_beast;
#ifdef SSL_CBC_RANDOM_IV
/* unless the user explicitly asks to allow the protocol vulnerability, we
use the work-around */
@@ -1632,14 +1631,14 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
infof(data, "warning: support for SSL_CBC_RANDOM_IV not compiled in\n");
#endif
if(data->set.ssl.cipher_list) {
if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) {
if(conn->ssl_config.cipher_list) {
if(set_ciphers(data, model, conn->ssl_config.cipher_list) != SECSuccess) {
result = CURLE_SSL_CIPHER;
goto error;
}
}
if(!data->set.ssl.verifypeer && data->set.ssl.verifyhost)
if(!conn->ssl_config.verifypeer && conn->ssl_config.verifyhost)
infof(data, "warning: ignoring value of ssl.verifyhost\n");
/* bypass the default SSL_AuthCertificate() hook in case we do not want to
@@ -1654,7 +1653,7 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
if(SSL_HandshakeCallback(model, HandshakeCallback, conn) != SECSuccess)
goto error;
if(data->set.ssl.verifypeer) {
if(conn->ssl_config.verifypeer) {
const CURLcode rv = nss_load_ca_certificates(conn, sockindex);
if(rv) {
result = rv;
@@ -1671,15 +1670,15 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
infof(data, " CRLfile: %s\n", data->set.ssl.CRLfile);
}
if(data->set.str[STRING_CERT]) {
char *nickname = dup_nickname(data, STRING_CERT);
if(data->set.ssl.cert) {
char *nickname = dup_nickname(data, data->set.ssl.cert);
if(nickname) {
/* we are not going to use libnsspem.so to read the client cert */
connssl->obj_clicert = NULL;
}
else {
CURLcode rv = cert_stuff(conn, sockindex, data->set.str[STRING_CERT],
data->set.str[STRING_KEY]);
CURLcode rv = cert_stuff(conn, sockindex, data->set.ssl.cert,
data->set.ssl.key);
if(rv) {
/* failf() is already done in cert_stuff() */
result = rv;
@@ -1732,8 +1731,8 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
model = NULL;
/* This is the password associated with the cert that we're using */
if(data->set.str[STRING_KEY_PASSWD]) {
SSL_SetPKCS11PinArg(connssl->handle, data->set.str[STRING_KEY_PASSWD]);
if(data->set.ssl.key_passwd) {
SSL_SetPKCS11PinArg(connssl->handle, data->set.ssl.key_passwd);
}
#ifdef SSL_ENABLE_OCSP_STAPLING
@@ -1837,11 +1836,11 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex)
if(result)
goto error;
if(data->set.str[STRING_SSL_ISSUERCERT]) {
if(data->set.ssl.issuercert) {
SECStatus ret = SECFailure;
char *nickname = dup_nickname(data, STRING_SSL_ISSUERCERT);
char *nickname = dup_nickname(data, data->set.ssl.issuercert);
if(nickname) {
/* we support only nicknames in case of STRING_SSL_ISSUERCERT for now */
/* we support only nicknames in case of issuercert for now */
ret = check_issuer_cert(connssl->handle, nickname);
free(nickname);
}

View File

@@ -149,6 +149,15 @@
*/
#define RAND_LOAD_LENGTH 1024
#define IS_PROXY_SSL() (CURLPROXY_HTTPS == conn->http_proxy.proxytype && \
ssl_connection_complete != conn->proxy_ssl[sockindex].state)
#define SSL_OPTION(var) (IS_PROXY_SSL() ? data->set.proxy_ssl.var : \
data->set.ssl.var)
#define SSL_OPTION_PRIM(var) (IS_PROXY_SSL() ? \
conn->proxy_ssl_config.var : conn->ssl_config.var)
static int passwd_callback(char *buf, int num, int encrypting,
void *global_passwd)
{
@@ -198,13 +207,13 @@ static int ossl_seed(struct SessionHandle *data)
#ifndef RANDOM_FILE
/* if RANDOM_FILE isn't defined, we only perform this if an option tells
us to! */
if(data->set.ssl.random_file)
if(data->set.ssl.primary.random_file)
#define RANDOM_FILE "" /* doesn't matter won't be used */
#endif
{
/* let the option override the define */
nread += RAND_load_file((data->set.str[STRING_SSL_RANDOM_FILE]?
data->set.str[STRING_SSL_RANDOM_FILE]:
nread += RAND_load_file((data->set.ssl.primary.random_file?
data->set.ssl.primary.random_file:
RANDOM_FILE),
RAND_LOAD_LENGTH);
if(seed_enough(nread))
@@ -217,14 +226,14 @@ static int ossl_seed(struct SessionHandle *data)
#ifndef EGD_SOCKET
/* If we don't have the define set, we only do this if the egd-option
is set */
if(data->set.str[STRING_SSL_EGDSOCKET])
if(data->set.ssl.primary.egdsocket)
#define EGD_SOCKET "" /* doesn't matter won't be used */
#endif
{
/* If there's an option and a define, the option overrides the
define */
int ret = RAND_egd(data->set.str[STRING_SSL_EGDSOCKET]?
data->set.str[STRING_SSL_EGDSOCKET]:EGD_SOCKET);
int ret = RAND_egd(data->set.ssl.primary.egdsocket?
data->set.ssl.primary.egdsocket:EGD_SOCKET);
if(-1 != ret) {
nread += ret;
if(seed_enough(nread))
@@ -262,8 +271,8 @@ static void Curl_ossl_seed(struct SessionHandle *data)
time-consuming seedings in vain */
static bool ssl_seeded = FALSE;
if(!ssl_seeded || data->set.str[STRING_SSL_RANDOM_FILE] ||
data->set.str[STRING_SSL_EGDSOCKET]) {
if(!ssl_seeded || data->set.ssl.primary.random_file ||
data->set.ssl.primary.egdsocket) {
ossl_seed(data);
ssl_seeded = TRUE;
}
@@ -343,7 +352,8 @@ int cert_stuff(struct connectdata *conn,
char *cert_file,
const char *cert_type,
char *key_file,
const char *key_type)
const char *key_type,
char *key_passwd)
{
struct SessionHandle *data = conn->data;
@@ -354,10 +364,9 @@ int cert_stuff(struct connectdata *conn,
X509 *x509;
int cert_done = 0;
if(data->set.str[STRING_KEY_PASSWD]) {
if(key_passwd) {
/* set the password in the callback userdata */
SSL_CTX_set_default_passwd_cb_userdata(ctx,
data->set.str[STRING_KEY_PASSWD]);
SSL_CTX_set_default_passwd_cb_userdata(ctx, key_passwd);
/* Set passwd callback: */
SSL_CTX_set_default_passwd_cb(ctx, passwd_callback);
}
@@ -467,7 +476,7 @@ int cert_stuff(struct connectdata *conn,
PKCS12_PBE_add();
if(!PKCS12_parse(p12, data->set.str[STRING_KEY_PASSWD], &pri, &x509,
if(!PKCS12_parse(p12, key_passwd, &pri, &x509,
&ca)) {
failf(data,
"could not parse PKCS12 file, check password, OpenSSL error %s",
@@ -579,7 +588,7 @@ int cert_stuff(struct connectdata *conn,
#ifdef HAVE_ENGINE_LOAD_FOUR_ARGS
ui_method,
#endif
data->set.str[STRING_KEY_PASSWD]);
key_passwd);
#ifdef HAVE_ENGINE_LOAD_FOUR_ARGS
UI_destroy_method(ui_method);
#endif
@@ -871,13 +880,8 @@ struct curl_slist *Curl_ossl_engines_list(struct SessionHandle *data)
}
/*
* This function is called when an SSL connection is closed.
*/
void Curl_ossl_close(struct connectdata *conn, int sockindex)
static void ossl_close(struct ssl_connect_data *connssl)
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
if(connssl->handle) {
(void)SSL_shutdown(connssl->handle);
SSL_set_connect_state(connssl->handle);
@@ -891,6 +895,15 @@ void Curl_ossl_close(struct connectdata *conn, int sockindex)
}
}
/*
* This function is called when an SSL connection is closed.
*/
void Curl_ossl_close(struct connectdata *conn, int sockindex)
{
ossl_close(&conn->ssl[sockindex]);
ossl_close(&conn->proxy_ssl[sockindex]);
}
/*
* This function is called to shut down the SSL layer but keep the
* socket open (CCC - Clear Command Channel)
@@ -1082,7 +1095,8 @@ static int asn1_output(const ASN1_UTCTIME *tm,
in the certificate and must exactly match the IP in the URI.
*/
static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert)
static CURLcode verifyhost(struct connectdata *conn,
X509 *server_cert, const int sockindex)
{
int matched = -1; /* -1 is no alternative match yet, 1 means match and 0
means mismatch */
@@ -1096,16 +1110,20 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert)
struct in_addr addr;
#endif
CURLcode result = CURLE_OK;
const char * const hostname = IS_PROXY_SSL() ? conn->http_proxy.host.name :
conn->host.name;
const char * const dispname = IS_PROXY_SSL() ?
conn->http_proxy.host.dispname : conn->host.dispname;
#ifdef ENABLE_IPV6
if(conn->bits.ipv6_ip &&
Curl_inet_pton(AF_INET6, conn->host.name, &addr)) {
Curl_inet_pton(AF_INET6, hostname, &addr)) {
target = GEN_IPADD;
addrlen = sizeof(struct in6_addr);
}
else
#endif
if(Curl_inet_pton(AF_INET, conn->host.name, &addr)) {
if(Curl_inet_pton(AF_INET, hostname, &addr)) {
target = GEN_IPADD;
addrlen = sizeof(struct in_addr);
}
@@ -1147,7 +1165,7 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert)
if((altlen == strlen(altptr)) &&
/* if this isn't true, there was an embedded zero in the name
string and we cannot match it. */
Curl_cert_hostcheck(altptr, conn->host.name))
Curl_cert_hostcheck(altptr, hostname))
matched = 1;
else
matched = 0;
@@ -1169,13 +1187,13 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert)
if(matched == 1)
/* an alternative name matched the server hostname */
infof(data, "\t subjectAltName: %s matched\n", conn->host.dispname);
infof(data, "\t subjectAltName: %s matched\n", dispname);
else if(matched == 0) {
/* an alternative name field existed, but didn't match and then
we MUST fail */
infof(data, "\t subjectAltName does not match %s\n", conn->host.dispname);
infof(data, "\t subjectAltName does not match %s\n", dispname);
failf(data, "SSL: no alternative certificate subject name matches "
"target host name '%s'", conn->host.dispname);
"target host name '%s'", dispname);
result = CURLE_PEER_FAILED_VERIFICATION;
}
else {
@@ -1249,9 +1267,9 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert)
"SSL: unable to obtain common name from peer certificate");
result = CURLE_PEER_FAILED_VERIFICATION;
}
else if(!Curl_cert_hostcheck((const char *)peer_CN, conn->host.name)) {
else if(!Curl_cert_hostcheck((const char *)peer_CN, hostname)) {
failf(data, "SSL: certificate subject name '%s' does not match "
"target host name '%s'", peer_CN, conn->host.dispname);
"target host name '%s'", peer_CN, dispname);
result = CURLE_PEER_FAILED_VERIFICATION;
}
else {
@@ -1688,17 +1706,30 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
struct in_addr addr;
#endif
#endif
long * const certverifyresult = IS_PROXY_SSL() ?
&data->set.proxy_ssl.certverifyresult : &data->set.ssl.certverifyresult;
const long int ssl_version = SSL_OPTION_PRIM(version);
#ifdef USE_TLS_SRP
const enum CURL_TLSAUTH ssl_authtype = SSL_OPTION(authtype);
#endif
char * const ssl_cert = SSL_OPTION(cert);
const char * const ssl_cert_type = SSL_OPTION(cert_type);
const char * const ssl_cafile = SSL_OPTION_PRIM(CAfile);
const char * const ssl_capath = SSL_OPTION_PRIM(CApath);
const bool verifypeer = SSL_OPTION_PRIM(verifypeer);
const char * const ssl_crlfile = SSL_OPTION(CRLfile);
const char * const hostname = IS_PROXY_SSL() ? conn->http_proxy.host.name :
conn->host.name;
DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
/* Make funny stuff to get random input */
Curl_ossl_seed(data);
data->set.ssl.certverifyresult = !X509_V_OK;
*certverifyresult = !X509_V_OK;
/* check to see if we've been told to use an explicit SSL/TLS version */
switch(data->set.ssl.version) {
switch(ssl_version) {
default:
case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
@@ -1720,7 +1751,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
return CURLE_NOT_BUILT_IN;
#else
#ifdef USE_TLS_SRP
if(data->set.ssl.authtype == CURL_TLSAUTH_SRP)
if(ssl_authtype == CURL_TLSAUTH_SRP)
return CURLE_SSL_CONNECT_ERROR;
#endif
req_method = SSLv2_client_method();
@@ -1733,7 +1764,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
return CURLE_NOT_BUILT_IN;
#else
#ifdef USE_TLS_SRP
if(data->set.ssl.authtype == CURL_TLSAUTH_SRP)
if(ssl_authtype == CURL_TLSAUTH_SRP)
return CURLE_SSL_CONNECT_ERROR;
#endif
req_method = SSLv3_client_method();
@@ -1818,14 +1849,14 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
/* unless the user explicitly ask to allow the protocol vulnerability we
use the work-around */
if(!conn->data->set.ssl_enable_beast)
if(!SSL_OPTION(enable_beast))
ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
#endif
switch(data->set.ssl.version) {
switch(ssl_version) {
case CURL_SSLVERSION_SSLv3:
#ifdef USE_TLS_SRP
if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) {
if(ssl_authtype == CURL_TLSAUTH_SRP) {
infof(data, "Set version TLSv1.x for SRP authorisation\n");
}
#endif
@@ -1919,19 +1950,16 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
}
#endif
if(data->set.str[STRING_CERT] || data->set.str[STRING_CERT_TYPE]) {
if(!cert_stuff(conn,
connssl->ctx,
data->set.str[STRING_CERT],
data->set.str[STRING_CERT_TYPE],
data->set.str[STRING_KEY],
data->set.str[STRING_KEY_TYPE])) {
if(ssl_cert || ssl_cert_type) {
if(!cert_stuff(conn, connssl->ctx, ssl_cert, ssl_cert_type,
SSL_OPTION(key), SSL_OPTION(key_type),
SSL_OPTION(key_passwd))) {
/* failf() is already done in cert_stuff() */
return CURLE_SSL_CERTPROBLEM;
}
}
ciphers = data->set.str[STRING_SSL_CIPHER_LIST];
ciphers = SSL_OPTION_PRIM(cipher_list);
if(!ciphers)
ciphers = (char *)DEFAULT_CIPHER_SELECTION;
if(!SSL_CTX_set_cipher_list(connssl->ctx, ciphers)) {
@@ -1941,18 +1969,20 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
infof(data, "Cipher selection: %s\n", ciphers);
#ifdef USE_TLS_SRP
if(data->set.ssl.authtype == CURL_TLSAUTH_SRP) {
infof(data, "Using TLS-SRP username: %s\n", data->set.ssl.username);
if(ssl_authtype == CURL_TLSAUTH_SRP) {
char * const ssl_username = SSL_OPTION(username);
if(!SSL_CTX_set_srp_username(connssl->ctx, data->set.ssl.username)) {
infof(data, "Using TLS-SRP username: %s\n", ssl_username);
if(!SSL_CTX_set_srp_username(connssl->ctx, ssl_username)) {
failf(data, "Unable to set SRP user name");
return CURLE_BAD_FUNCTION_ARGUMENT;
}
if(!SSL_CTX_set_srp_password(connssl->ctx, data->set.ssl.password)) {
if(!SSL_CTX_set_srp_password(connssl->ctx, SSL_OPTION(password))) {
failf(data, "failed setting SRP password");
return CURLE_BAD_FUNCTION_ARGUMENT;
}
if(!data->set.str[STRING_SSL_CIPHER_LIST]) {
if(!SSL_OPTION_PRIM(cipher_list)) {
infof(data, "Setting cipher list SRP\n");
if(!SSL_CTX_set_cipher_list(connssl->ctx, "SRP")) {
@@ -1962,20 +1992,17 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
}
}
#endif
if(data->set.str[STRING_SSL_CAFILE] || data->set.str[STRING_SSL_CAPATH]) {
if(ssl_cafile || ssl_capath) {
/* tell SSL where to find CA certificates that are used to verify
the servers certificate. */
if(!SSL_CTX_load_verify_locations(connssl->ctx,
data->set.str[STRING_SSL_CAFILE],
data->set.str[STRING_SSL_CAPATH])) {
if(data->set.ssl.verifypeer) {
if(!SSL_CTX_load_verify_locations(connssl->ctx, ssl_cafile, ssl_capath)) {
if(verifypeer) {
/* Fail if we insist on successfully verifying the server. */
failf(data, "error setting certificate verify locations:\n"
" CAfile: %s\n CApath: %s",
data->set.str[STRING_SSL_CAFILE]?
data->set.str[STRING_SSL_CAFILE]: "none",
data->set.str[STRING_SSL_CAPATH]?
data->set.str[STRING_SSL_CAPATH] : "none");
ssl_cafile ? ssl_cafile : "none",
ssl_capath ? ssl_capath : "none");
return CURLE_SSL_CACERT_BADFILE;
}
else {
@@ -1992,22 +2019,18 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
infof(data,
" CAfile: %s\n"
" CApath: %s\n",
data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]:
"none",
data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]:
"none");
ssl_cafile ? ssl_cafile : "none",
ssl_capath ? ssl_capath : "none");
}
if(data->set.str[STRING_SSL_CRLFILE]) {
if(ssl_crlfile) {
/* tell SSL where to find CRL file that is used to check certificate
* revocation */
lookup=X509_STORE_add_lookup(SSL_CTX_get_cert_store(connssl->ctx),
X509_LOOKUP_file());
if(!lookup ||
(!X509_load_crl_file(lookup, data->set.str[STRING_SSL_CRLFILE],
X509_FILETYPE_PEM)) ) {
failf(data, "error loading CRL file: %s",
data->set.str[STRING_SSL_CRLFILE]);
(!X509_load_crl_file(lookup, ssl_crlfile, X509_FILETYPE_PEM)) ) {
failf(data, "error loading CRL file: %s", ssl_crlfile);
return CURLE_SSL_CRL_BADFILE;
}
else {
@@ -2016,9 +2039,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
X509_STORE_set_flags(SSL_CTX_get_cert_store(connssl->ctx),
X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
}
infof(data,
" CRLfile: %s\n", data->set.str[STRING_SSL_CRLFILE] ?
data->set.str[STRING_SSL_CRLFILE]: "none");
infof(data, " CRLfile: %s\n", ssl_crlfile ? ssl_crlfile: "none");
}
/* Try building a chain using issuers in the trusted store first to avoid
@@ -2040,8 +2061,7 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
* anyway. In the latter case the result of the verification is checked with
* SSL_get_verify_result() below. */
SSL_CTX_set_verify(connssl->ctx,
data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
NULL);
verifypeer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
/* give application a chance to interfere with SSL set up. */
if(data->set.ssl.fsslctx) {
@@ -2064,27 +2084,26 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
!defined(OPENSSL_IS_BORINGSSL)
if(data->set.ssl.verifystatus)
if(SSL_OPTION_PRIM(verifystatus))
SSL_set_tlsext_status_type(connssl->handle, TLSEXT_STATUSTYPE_ocsp);
#endif
SSL_set_connect_state(connssl->handle);
connssl->server_cert = 0x0;
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
if((0 == Curl_inet_pton(AF_INET, conn->host.name, &addr)) &&
if((0 == Curl_inet_pton(AF_INET, hostname, &addr)) &&
#ifdef ENABLE_IPV6
(0 == Curl_inet_pton(AF_INET6, conn->host.name, &addr)) &&
(0 == Curl_inet_pton(AF_INET6, hostname, &addr)) &&
#endif
sni &&
!SSL_set_tlsext_host_name(connssl->handle, conn->host.name))
!SSL_set_tlsext_host_name(connssl->handle, hostname))
infof(data, "WARNING: failed to configure server name indication (SNI) "
"TLS extension\n");
#endif
/* Check if there's a cached ID we can/should use here! */
if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL, sockindex)) {
/* we got a session id, use it! */
if(!SSL_set_session(connssl->handle, ssl_sessionid)) {
failf(data, "SSL: SSL_set_session failed: %s",
@@ -2095,8 +2114,16 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
infof (data, "SSL re-using session ID\n");
}
/* pass the raw socket into the SSL layers */
if(!SSL_set_fd(connssl->handle, (int)sockfd)) {
if(conn->proxy_ssl[sockindex].use) {
BIO *const bio = BIO_new(BIO_f_ssl());
DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state);
DEBUGASSERT(conn->proxy_ssl[sockindex].handle != NULL);
DEBUGASSERT(bio != NULL);
BIO_set_ssl(bio, conn->proxy_ssl[sockindex].handle, FALSE);
SSL_set_bio(connssl->handle, bio, bio);
}
else if(!SSL_set_fd(connssl->handle, (int)sockfd)) {
/* pass the raw socket into the SSL layers */
failf(data, "SSL: SSL_set_fd failed: %s",
ERR_error_string(ERR_get_error(), NULL));
return CURLE_SSL_CONNECT_ERROR;
@@ -2189,8 +2216,11 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex)
* the SO_ERROR is also lost.
*/
if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) {
const char * const hostname = IS_PROXY_SSL() ?
conn->http_proxy.host.name : conn->host.name;
const long int port = IS_PROXY_SSL() ? conn->port : conn->remote_port;
failf(data, "Unknown SSL protocol error in connection to %s:%ld ",
conn->host.name, conn->remote_port);
hostname, port);
return result;
}
@@ -2626,7 +2656,8 @@ static CURLcode pkp_pin_peer_pubkey(X509* cert, const char *pinnedpubkey)
*/
static CURLcode servercert(struct connectdata *conn,
struct ssl_connect_data *connssl,
bool strict)
bool strict,
const int sockindex)
{
CURLcode result = CURLE_OK;
int rc;
@@ -2637,6 +2668,8 @@ static CURLcode servercert(struct connectdata *conn,
FILE *fp;
char *buffer = data->state.buffer;
const char *ptr;
long * const certverifyresult = IS_PROXY_SSL() ?
&data->set.proxy_ssl.certverifyresult : &data->set.ssl.certverifyresult;
if(data->set.ssl.certinfo)
/* we've been asked to gather certificate info! */
@@ -2649,7 +2682,7 @@ static CURLcode servercert(struct connectdata *conn,
return CURLE_PEER_FAILED_VERIFICATION;
}
infof(data, "Server certificate:\n");
infof(data, "%s certificate:\n", IS_PROXY_SSL() ? "Proxy" : "Server");
rc = x509_name_oneline(X509_get_subject_name(connssl->server_cert),
buffer, BUFSIZE);
@@ -2663,8 +2696,8 @@ static CURLcode servercert(struct connectdata *conn,
asn1_output(certdate, buffer, BUFSIZE);
infof(data, "\t expire date: %s\n", buffer);
if(data->set.ssl.verifyhost) {
result = verifyhost(conn, connssl->server_cert);
if(SSL_OPTION_PRIM(verifyhost)) {
result = verifyhost(conn, connssl->server_cert, sockindex);
if(result) {
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
@@ -2686,12 +2719,12 @@ static CURLcode servercert(struct connectdata *conn,
deallocating the certificate. */
/* e.g. match issuer name with provided issuer certificate */
if(data->set.str[STRING_SSL_ISSUERCERT]) {
fp = fopen(data->set.str[STRING_SSL_ISSUERCERT], FOPEN_READTEXT);
if(data->set.ssl.issuercert) {
fp = fopen(data->set.ssl.issuercert, FOPEN_READTEXT);
if(!fp) {
if(strict)
failf(data, "SSL: Unable to open issuer cert (%s)",
data->set.str[STRING_SSL_ISSUERCERT]);
data->set.ssl.issuercert);
X509_free(connssl->server_cert);
connssl->server_cert = NULL;
return CURLE_SSL_ISSUER_ERROR;
@@ -2701,7 +2734,7 @@ static CURLcode servercert(struct connectdata *conn,
if(!issuer) {
if(strict)
failf(data, "SSL: Unable to read issuer cert (%s)",
data->set.str[STRING_SSL_ISSUERCERT]);
data->set.ssl.issuercert);
X509_free(connssl->server_cert);
X509_free(issuer);
fclose(fp);
@@ -2713,7 +2746,7 @@ static CURLcode servercert(struct connectdata *conn,
if(X509_check_issued(issuer, connssl->server_cert) != X509_V_OK) {
if(strict)
failf(data, "SSL: Certificate issuer check failed (%s)",
data->set.str[STRING_SSL_ISSUERCERT]);
data->set.ssl.issuercert);
X509_free(connssl->server_cert);
X509_free(issuer);
connssl->server_cert = NULL;
@@ -2721,15 +2754,14 @@ static CURLcode servercert(struct connectdata *conn,
}
infof(data, "\t SSL certificate issuer check ok (%s)\n",
data->set.str[STRING_SSL_ISSUERCERT]);
data->set.ssl.issuercert);
X509_free(issuer);
}
lerr = data->set.ssl.certverifyresult =
SSL_get_verify_result(connssl->handle);
lerr = *certverifyresult = SSL_get_verify_result(connssl->handle);
if(data->set.ssl.certverifyresult != X509_V_OK) {
if(data->set.ssl.verifypeer) {
if(*certverifyresult != X509_V_OK) {
if(SSL_OPTION_PRIM(verifypeer)) {
/* We probably never reach this, because SSL_connect() will fail
and we return earlier if verifypeer is set? */
if(strict)
@@ -2748,7 +2780,7 @@ static CURLcode servercert(struct connectdata *conn,
#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
!defined(OPENSSL_IS_BORINGSSL)
if(data->set.ssl.verifystatus) {
if(SSL_OPTION_PRIM(verifystatus)) {
result = verifystatus(conn, connssl);
if(result) {
X509_free(connssl->server_cert);
@@ -2793,7 +2825,8 @@ static CURLcode ossl_connect_step3(struct connectdata *conn, int sockindex)
will stay in memory until explicitly freed with SSL_SESSION_free(3),
regardless of its state. */
incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL,
sockindex));
if(incache) {
if(old_ssl_sessionid != our_ssl_sessionid) {
infof(data, "old SSL session ID is stale, removing\n");
@@ -2804,7 +2837,7 @@ static CURLcode ossl_connect_step3(struct connectdata *conn, int sockindex)
if(!incache) {
result = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
0 /* unknown size */);
0 /* unknown size */, sockindex);
if(result) {
failf(data, "failed to store ssl session");
return result;
@@ -2825,8 +2858,8 @@ static CURLcode ossl_connect_step3(struct connectdata *conn, int sockindex)
* operations.
*/
result = servercert(conn, connssl,
(data->set.ssl.verifypeer || data->set.ssl.verifyhost));
result = servercert(conn, connssl, (SSL_OPTION_PRIM(verifypeer) ||
SSL_OPTION_PRIM(verifyhost)), sockindex);
if(!result)
connssl->connecting_state = ssl_connect_done;
@@ -2973,7 +3006,10 @@ bool Curl_ossl_data_pending(const struct connectdata *conn, int connindex)
{
if(conn->ssl[connindex].handle)
/* SSL is in use */
return (0 != SSL_pending(conn->ssl[connindex].handle)) ? TRUE : FALSE;
return (0 != SSL_pending(conn->ssl[connindex].handle) ||
(conn->proxy_ssl[connindex].handle &&
0 != SSL_pending(conn->proxy_ssl[connindex].handle))) ?
TRUE : FALSE;
else
return FALSE;
}
@@ -3018,8 +3054,18 @@ static ssize_t ossl_send(struct connectdata *conn,
/* A failure in the SSL library occurred, usually a protocol error.
The OpenSSL error queue contains more information on the error. */
sslerror = ERR_get_error();
failf(conn->data, "SSL_write() error: %s",
ERR_error_string(sslerror, error_buffer));
if(sslerror ==
ERR_PACK(ERR_LIB_SSL, SSL_F_SSL3_WRITE_PENDING, SSL_R_BIO_NOT_SET) &&
conn->ssl[sockindex].state == ssl_connection_complete &&
conn->proxy_ssl[sockindex].state == ssl_connection_complete) {
char ver[120];
Curl_ossl_version(ver, 120);
failf(conn->data, "Error: %s does not support double SSL tunneling.",
ver);
}
else
failf(conn->data, "SSL_write() error: %s",
ERR_error_string(sslerror, error_buffer));
*curlcode = CURLE_SEND_ERROR;
return -1;
}

View File

@@ -146,11 +146,11 @@ polarssl_connect_step1(struct connectdata *conn,
errorbuf[0]=0;
/* PolarSSL only supports SSLv3 and TLSv1 */
if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) {
if(conn->ssl_config.version == CURL_SSLVERSION_SSLv2) {
failf(data, "PolarSSL does not support SSLv2");
return CURLE_SSL_CONNECT_ERROR;
}
else if(data->set.ssl.version == CURL_SSLVERSION_SSLv3)
else if(conn->ssl_config.version == CURL_SSLVERSION_SSLv3)
sni = FALSE; /* SSLv3 has no SNI */
#ifdef THREADING_SUPPORT
@@ -180,18 +180,18 @@ polarssl_connect_step1(struct connectdata *conn,
/* Load the trusted CA */
memset(&connssl->cacert, 0, sizeof(x509_crt));
if(data->set.str[STRING_SSL_CAFILE]) {
if(conn->ssl_config.CAfile) {
ret = x509_crt_parse_file(&connssl->cacert,
data->set.str[STRING_SSL_CAFILE]);
conn->ssl_config.CAfile);
if(ret<0) {
#ifdef POLARSSL_ERROR_C
error_strerror(ret, errorbuf, sizeof(errorbuf));
#endif /* POLARSSL_ERROR_C */
failf(data, "Error reading ca cert file %s - PolarSSL: (-0x%04X) %s",
data->set.str[STRING_SSL_CAFILE], -ret, errorbuf);
conn->ssl_config.CAfile, -ret, errorbuf);
if(data->set.ssl.verifypeer)
if(conn->ssl_config.verifypeer)
return CURLE_SSL_CACERT_BADFILE;
}
}
@@ -215,27 +215,27 @@ polarssl_connect_step1(struct connectdata *conn,
/* Load the client certificate */
memset(&connssl->clicert, 0, sizeof(x509_crt));
if(data->set.str[STRING_CERT]) {
if(data->set.ssl.cert) {
ret = x509_crt_parse_file(&connssl->clicert,
data->set.str[STRING_CERT]);
data->set.ssl.cert);
if(ret) {
#ifdef POLARSSL_ERROR_C
error_strerror(ret, errorbuf, sizeof(errorbuf));
#endif /* POLARSSL_ERROR_C */
failf(data, "Error reading client cert file %s - PolarSSL: (-0x%04X) %s",
data->set.str[STRING_CERT], -ret, errorbuf);
data->set.ssl.cert, -ret, errorbuf);
return CURLE_SSL_CERTPROBLEM;
}
}
/* Load the client private key */
if(data->set.str[STRING_KEY]) {
if(data->set.ssl.key) {
pk_context pk;
pk_init(&pk);
ret = pk_parse_keyfile(&pk, data->set.str[STRING_KEY],
data->set.str[STRING_KEY_PASSWD]);
ret = pk_parse_keyfile(&pk, data->set.ssl.key,
data->set.ssl.key_passwd);
if(ret == 0 && !pk_can_do(&pk, POLARSSL_PK_RSA))
ret = POLARSSL_ERR_PK_TYPE_MISMATCH;
if(ret == 0)
@@ -249,7 +249,7 @@ polarssl_connect_step1(struct connectdata *conn,
error_strerror(ret, errorbuf, sizeof(errorbuf));
#endif /* POLARSSL_ERROR_C */
failf(data, "Error reading private key %s - PolarSSL: (-0x%04X) %s",
data->set.str[STRING_KEY], -ret, errorbuf);
data->set.ssl.key, -ret, errorbuf);
return CURLE_SSL_CERTPROBLEM;
}
@@ -258,16 +258,16 @@ polarssl_connect_step1(struct connectdata *conn,
/* Load the CRL */
memset(&connssl->crl, 0, sizeof(x509_crl));
if(data->set.str[STRING_SSL_CRLFILE]) {
if(data->set.ssl.CRLfile) {
ret = x509_crl_parse_file(&connssl->crl,
data->set.str[STRING_SSL_CRLFILE]);
data->set.ssl.CRLfile);
if(ret) {
#ifdef POLARSSL_ERROR_C
error_strerror(ret, errorbuf, sizeof(errorbuf));
#endif /* POLARSSL_ERROR_C */
failf(data, "Error reading CRL file %s - PolarSSL: (-0x%04X) %s",
data->set.str[STRING_SSL_CRLFILE], -ret, errorbuf);
data->set.ssl.CRLfile, -ret, errorbuf);
return CURLE_SSL_CRL_BADFILE;
}
@@ -328,7 +328,8 @@ polarssl_connect_step1(struct connectdata *conn,
net_send, &conn->sock[sockindex]);
ssl_set_ciphersuites(&connssl->ssl, ssl_list_ciphersuites());
if(!Curl_ssl_getsessionid(conn, &old_session, &old_session_size)) {
if(!Curl_ssl_getsessionid(conn, &old_session, &old_session_size,
sockindex)) {
memcpy(&connssl->ssn, old_session, old_session_size);
infof(data, "PolarSSL re-using session\n");
}
@@ -426,7 +427,7 @@ polarssl_connect_step2(struct connectdata *conn,
ret = ssl_get_verify_result(&conn->ssl[sockindex].ssl);
if(ret && data->set.ssl.verifypeer) {
if(ret && conn->ssl_config.verifypeer) {
if(ret & BADCERT_EXPIRED)
failf(data, "Cert verify failed: BADCERT_EXPIRED");
@@ -496,7 +497,8 @@ polarssl_connect_step3(struct connectdata *conn,
DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
/* Save the current session data for possible re-use */
incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL,
sockindex));
if(incache) {
if(old_ssl_sessionid != our_ssl_sessionid) {
infof(data, "old SSL session ID is stale, removing\n");
@@ -511,7 +513,8 @@ polarssl_connect_step3(struct connectdata *conn,
if(new_session) {
memcpy(new_session, our_ssl_sessionid, sizeof(ssl_session));
result = Curl_ssl_addsessionid(conn, new_session, sizeof(ssl_session));
result = Curl_ssl_addsessionid(conn, new_session,
sizeof(ssl_session), sockindex);
}
else
result = CURLE_OUT_OF_MEMORY;

View File

@@ -111,7 +111,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
conn->host.name, conn->remote_port);
/* check for an existing re-usable credential handle */
if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL)) {
if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL, sockindex)) {
connssl->cred = old_cred;
infof(data, "schannel: re-using existing credential handle\n");
}
@@ -120,7 +120,7 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
memset(&schannel_cred, 0, sizeof(schannel_cred));
schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
if(data->set.ssl.verifypeer) {
if(conn->ssl_config.verifypeer) {
#ifdef _WIN32_WCE
/* certificate validation on CE doesn't seem to work right; we'll
do it following a more manual process. */
@@ -140,14 +140,14 @@ schannel_connect_step1(struct connectdata *conn, int sockindex)
infof(data, "schannel: disable server certificate revocation checks\n");
}
if(!data->set.ssl.verifyhost) {
if(!conn->ssl_config.verifyhost) {
schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
infof(data, "schannel: verifyhost setting prevents Schannel from "
"comparing the supplied target name with the subject "
"names in server certificates. Also disables SNI.\n");
}
switch(data->set.ssl.version) {
switch(conn->ssl_config.version) {
default:
case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
@@ -512,7 +512,7 @@ schannel_connect_step2(struct connectdata *conn, int sockindex)
#ifdef _WIN32_WCE
/* Windows CE doesn't do any server certificate validation.
We have to do it manually. */
if(data->set.ssl.verifypeer)
if(conn->ssl_config.verifypeer)
return verify_certificate(conn, sockindex);
#endif
@@ -559,7 +559,8 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
}
/* save the current session data for possible re-use */
incache = !(Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL));
incache = !(Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL,
sockindex));
if(incache) {
if(old_cred != connssl->cred) {
infof(data, "schannel: old credential handle is stale, removing\n");
@@ -570,7 +571,8 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
if(!incache) {
result = Curl_ssl_addsessionid(conn, (void *)connssl->cred,
sizeof(struct curl_schannel_cred));
sizeof(struct curl_schannel_cred),
sockindex);
if(result) {
failf(data, "schannel: failed to store credential handle");
return result;
@@ -1416,7 +1418,7 @@ static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
}
if(result == CURLE_OK) {
if(data->set.ssl.verifyhost) {
if(conn->ssl_config.verifyhost) {
TCHAR cert_hostname_buff[128];
xcharp_u hostname;
xcharp_u cert_hostname;

View File

@@ -65,6 +65,7 @@
#include "url.h"
#include "progress.h"
#include "share.h"
#include "multiif.h"
#include "timeval.h"
#include "curl_md5.h"
#include "warnless.h"
@@ -80,86 +81,84 @@
(data->share->specifier & \
(1<<CURL_LOCK_DATA_SSL_SESSION)))
static bool safe_strequal(char* str1, char* str2)
{
if(str1 && str2)
/* both pointers point to something then compare them */
return (0 != Curl_raw_equal(str1, str2)) ? TRUE : FALSE;
else
/* if both pointers are NULL then treat them as equal */
return (!str1 && !str2) ? TRUE : FALSE;
}
#define CLONE_STRING(var) if(source->var) { \
dest->var = strdup(source->var); \
if(!dest->var) \
return FALSE; \
} \
else \
dest->var = NULL;
bool
Curl_ssl_config_matches(struct ssl_config_data* data,
struct ssl_config_data* needle)
Curl_ssl_config_matches(struct ssl_primary_config* data,
struct ssl_primary_config* needle)
{
if((data->version == needle->version) &&
(data->verifypeer == needle->verifypeer) &&
(data->verifyhost == needle->verifyhost) &&
safe_strequal(data->CApath, needle->CApath) &&
safe_strequal(data->CAfile, needle->CAfile) &&
safe_strequal(data->random_file, needle->random_file) &&
safe_strequal(data->egdsocket, needle->egdsocket) &&
safe_strequal(data->cipher_list, needle->cipher_list))
Curl_safe_strequal(data->CApath, needle->CApath) &&
Curl_safe_strequal(data->CAfile, needle->CAfile) &&
Curl_safe_strequal(data->random_file, needle->random_file) &&
Curl_safe_strequal(data->egdsocket, needle->egdsocket) &&
Curl_safe_strequal(data->cipher_list, needle->cipher_list))
return TRUE;
return FALSE;
}
void
Curl_clone_general_ssl_config(struct ssl_general_config *source,
struct ssl_general_config *dest)
{
dest->sessionid = source->sessionid;
dest->max_ssl_sessions = source->max_ssl_sessions;
}
bool
Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
struct ssl_primary_config *dest)
{
dest->verifyhost = source->verifyhost;
dest->verifypeer = source->verifypeer;
dest->version = source->version;
CLONE_STRING(CAfile);
CLONE_STRING(CApath);
CLONE_STRING(cipher_list);
CLONE_STRING(egdsocket);
CLONE_STRING(random_file);
return TRUE;
}
bool
Curl_clone_ssl_config(struct ssl_config_data *source,
struct ssl_config_data *dest)
{
dest->sessionid = source->sessionid;
dest->verifyhost = source->verifyhost;
dest->verifypeer = source->verifypeer;
dest->version = source->version;
Curl_clone_primary_ssl_config(&source->primary, &dest->primary);
dest->enable_beast = source->enable_beast;
dest->certverifyresult = source->certverifyresult;
dest->fsslctx = source->fsslctx;
dest->fsslctxp = source->fsslctxp;
dest->certinfo = source->certinfo;
if(source->CAfile) {
dest->CAfile = strdup(source->CAfile);
if(!dest->CAfile)
return FALSE;
}
else
dest->CAfile = NULL;
CLONE_STRING(CRLfile);
CLONE_STRING(issuercert);
CLONE_STRING(cert);
CLONE_STRING(cert_type);
CLONE_STRING(key);
CLONE_STRING(key_type);
CLONE_STRING(key_passwd);
if(source->CApath) {
dest->CApath = strdup(source->CApath);
if(!dest->CApath)
return FALSE;
}
else
dest->CApath = NULL;
if(source->cipher_list) {
dest->cipher_list = strdup(source->cipher_list);
if(!dest->cipher_list)
return FALSE;
}
else
dest->cipher_list = NULL;
if(source->egdsocket) {
dest->egdsocket = strdup(source->egdsocket);
if(!dest->egdsocket)
return FALSE;
}
else
dest->egdsocket = NULL;
if(source->random_file) {
dest->random_file = strdup(source->random_file);
if(!dest->random_file)
return FALSE;
}
else
dest->random_file = NULL;
#ifdef USE_TLS_SRP
CLONE_STRING(username);
CLONE_STRING(password);
dest->authtype = source->authtype;
#endif
return TRUE;
}
void Curl_free_ssl_config(struct ssl_config_data* sslc)
void Curl_free_primary_ssl_config(struct ssl_primary_config* sslc)
{
Curl_safefree(sslc->CAfile);
Curl_safefree(sslc->CApath);
@@ -244,6 +243,23 @@ int Curl_ssl_backend(void)
return (int)CURL_SSL_BACKEND;
}
void Curl_free_ssl_config(struct ssl_config_data* sslc)
{
Curl_free_primary_ssl_config(&sslc->primary);
Curl_safefree(sslc->CRLfile);
Curl_safefree(sslc->issuercert);
Curl_safefree(sslc->cert);
Curl_safefree(sslc->cert_type);
Curl_safefree(sslc->key);
Curl_safefree(sslc->key_type);
Curl_safefree(sslc->key_passwd);
#ifdef USE_TLS_SRP
Curl_safefree(sslc->username);
Curl_safefree(sslc->password);
#endif
}
#ifdef USE_SSL
/* "global" init done? */
@@ -279,19 +295,40 @@ void Curl_ssl_cleanup(void)
static bool ssl_prefs_check(struct SessionHandle *data)
{
/* check for CURLOPT_SSLVERSION invalid parameter value */
if((data->set.ssl.version < 0)
|| (data->set.ssl.version >= CURL_SSLVERSION_LAST)) {
if((data->set.ssl.primary.version < 0)
|| (data->set.ssl.primary.version >= CURL_SSLVERSION_LAST)) {
failf(data, "Unrecognized parameter value passed via CURLOPT_SSLVERSION");
return FALSE;
}
return TRUE;
}
static CURLcode
ssl_connect_init_proxy(struct connectdata *conn, int sockindex) {
DEBUGASSERT(conn->bits.proxy_ssl_connected[sockindex]);
if(ssl_connection_complete == conn->ssl[sockindex].state &&
!conn->proxy_ssl[sockindex].use) {
#ifdef USE_OPENSSL
conn->proxy_ssl[sockindex] = conn->ssl[sockindex];
memset(&conn->ssl[sockindex], 0, sizeof(conn->ssl[sockindex]));
#else
return CURLE_NOT_BUILT_IN;
#endif
}
return CURLE_OK;
}
CURLcode
Curl_ssl_connect(struct connectdata *conn, int sockindex)
{
CURLcode result;
if(conn->bits.proxy_ssl_connected[sockindex]) {
result = ssl_connect_init_proxy(conn, sockindex);
if(result)
return result;
}
if(!ssl_prefs_check(conn->data))
return CURLE_SSL_CONNECT_ERROR;
@@ -312,6 +349,11 @@ Curl_ssl_connect_nonblocking(struct connectdata *conn, int sockindex,
bool *done)
{
CURLcode result;
if(conn->bits.proxy_ssl_connected[sockindex]) {
result = ssl_connect_init_proxy(conn, sockindex);
if(result)
return result;
}
if(!ssl_prefs_check(conn->data))
return CURLE_SSL_CONNECT_ERROR;
@@ -335,7 +377,8 @@ Curl_ssl_connect_nonblocking(struct connectdata *conn, int sockindex,
*/
bool Curl_ssl_getsessionid(struct connectdata *conn,
void **ssl_sessionid,
size_t *idsize) /* set 0 if unknown */
size_t *idsize, /* set 0 if unknown */
int sockindex)
{
struct curl_ssl_session *check;
struct SessionHandle *data = conn->data;
@@ -343,9 +386,16 @@ bool Curl_ssl_getsessionid(struct connectdata *conn,
long *general_age;
bool no_match = TRUE;
const bool isProxy = CONNECT_PROXY_SSL();
struct ssl_primary_config * const ssl_config = isProxy ?
&conn->proxy_ssl_config :
&conn->ssl_config;
const char * const name = isProxy ? conn->http_proxy.host.name :
conn->host.name;
int port = isProxy ? (int)conn->port : conn->remote_port;
*ssl_sessionid = NULL;
if(!conn->ssl_config.sessionid)
if(!data->set.general_ssl.sessionid)
/* session ID re-use is disabled */
return TRUE;
@@ -357,14 +407,14 @@ bool Curl_ssl_getsessionid(struct connectdata *conn,
else
general_age = &data->state.sessionage;
for(i = 0; i < data->set.ssl.max_ssl_sessions; i++) {
for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) {
check = &data->state.session[i];
if(!check->sessionid)
/* not session ID means blank entry */
continue;
if(Curl_raw_equal(conn->host.name, check->name) &&
(conn->remote_port == check->remote_port) &&
Curl_ssl_config_matches(&conn->ssl_config, &check->ssl_config)) {
if(Curl_raw_equal(name, check->name) &&
(port == check->remote_port) &&
Curl_ssl_config_matches(ssl_config, &check->ssl_config)) {
/* yes, we have a session ID! */
(*general_age)++; /* increase general age */
check->age = *general_age; /* set this as used in this age */
@@ -397,7 +447,7 @@ void Curl_ssl_kill_session(struct curl_ssl_session *session)
session->sessionid = NULL;
session->age = 0; /* fresh */
Curl_free_ssl_config(&session->ssl_config);
Curl_free_primary_ssl_config(&session->ssl_config);
Curl_safefree(session->name);
}
@@ -414,7 +464,7 @@ void Curl_ssl_delsessionid(struct connectdata *conn, void *ssl_sessionid)
if(SSLSESSION_SHARED(data))
Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
for(i = 0; i < data->set.ssl.max_ssl_sessions; i++) {
for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) {
struct curl_ssl_session *check = &data->state.session[i];
if(check->sessionid == ssl_sessionid) {
@@ -435,7 +485,8 @@ void Curl_ssl_delsessionid(struct connectdata *conn, void *ssl_sessionid)
*/
CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
void *ssl_sessionid,
size_t idsize)
size_t idsize,
int sockindex)
{
size_t i;
struct SessionHandle *data=conn->data; /* the mother of all structs */
@@ -448,7 +499,12 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
IT. We still store it here in case the re-using is again enabled for an
upcoming transfer */
clone_host = strdup(conn->host.name);
const bool isProxy = CONNECT_PROXY_SSL();
struct ssl_primary_config * const ssl_config = isProxy ?
&conn->proxy_ssl_config :
&conn->ssl_config;
clone_host = strdup(isProxy ? conn->http_proxy.host.name : conn->host.name);
if(!clone_host)
return CURLE_OUT_OF_MEMORY; /* bail out */
@@ -465,14 +521,14 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
}
/* find an empty slot for us, or find the oldest */
for(i = 1; (i < data->set.ssl.max_ssl_sessions) &&
for(i = 1; (i < data->set.general_ssl.max_ssl_sessions) &&
data->state.session[i].sessionid; i++) {
if(data->state.session[i].age < oldest_age) {
oldest_age = data->state.session[i].age;
store = &data->state.session[i];
}
}
if(i == data->set.ssl.max_ssl_sessions)
if(i == data->set.general_ssl.max_ssl_sessions)
/* cache is full, we must "kill" the oldest entry! */
Curl_ssl_kill_session(store);
else
@@ -485,14 +541,15 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
/* free it if there's one already present */
free(store->name);
store->name = clone_host; /* clone host name */
store->remote_port = conn->remote_port; /* port number */
/* port number */
store->remote_port = isProxy ? (int)conn->port : conn->remote_port;
/* Unlock */
if(SSLSESSION_SHARED(data))
Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
if(!Curl_clone_ssl_config(&conn->ssl_config, &store->ssl_config)) {
if(!Curl_clone_primary_ssl_config(ssl_config, &store->ssl_config)) {
store->sessionid = NULL; /* let caller free sessionid */
free(clone_host);
return CURLE_OUT_OF_MEMORY;
@@ -507,7 +564,7 @@ void Curl_ssl_close_all(struct SessionHandle *data)
size_t i;
/* kill the session ID cache if not shared */
if(data->state.session && !SSLSESSION_SHARED(data)) {
for(i = 0; i < data->set.ssl.max_ssl_sessions; i++)
for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++)
/* the single-killer function handles empty table slots */
Curl_ssl_kill_session(&data->state.session[i]);
@@ -518,6 +575,43 @@ void Curl_ssl_close_all(struct SessionHandle *data)
curlssl_close_all(data);
}
#if defined(USE_SSLEAY) || defined(USE_GNUTLS) || defined(USE_SCHANNEL) || \
defined(USE_DARWINSSL) || defined(USE_NSS)
/* This function is for OpenSSL, GnuTLS, darwinssl, and schannel only. */
int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks,
int numsocks)
{
struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET];
if(!numsocks)
return GETSOCK_BLANK;
if(connssl->connecting_state == ssl_connect_2_writing) {
/* write mode */
socks[0] = conn->sock[FIRSTSOCKET];
return GETSOCK_WRITESOCK(0);
}
else if(connssl->connecting_state == ssl_connect_2_reading) {
/* read mode */
socks[0] = conn->sock[FIRSTSOCKET];
return GETSOCK_READSOCK(0);
}
return GETSOCK_BLANK;
}
#else
int Curl_ssl_getsock(struct connectdata *conn,
curl_socket_t *socks,
int numsocks)
{
(void)conn;
(void)socks;
(void)numsocks;
return GETSOCK_BLANK;
}
/* USE_SSLEAY || USE_GNUTLS || USE_SCHANNEL || USE_DARWINSSL || USE_NSS */
#endif
void Curl_ssl_close(struct connectdata *conn, int sockindex)
{
DEBUGASSERT((sockindex <= 1) && (sockindex >= -1));
@@ -575,7 +669,7 @@ CURLcode Curl_ssl_initsessions(struct SessionHandle *data, size_t amount)
return CURLE_OUT_OF_MEMORY;
/* store the info in the SSL section */
data->set.ssl.max_ssl_sessions = amount;
data->set.general_ssl.max_ssl_sessions = amount;
data->state.session = session;
data->state.sessionage = 1; /* this is brand new */
return CURLE_OK;

View File

@@ -45,11 +45,18 @@
#define ALPN_HTTP_1_1_LENGTH 8
#define ALPN_HTTP_1_1 "http/1.1"
bool Curl_ssl_config_matches(struct ssl_config_data* data,
struct ssl_config_data* needle);
bool Curl_ssl_config_matches(struct ssl_primary_config* data,
struct ssl_primary_config* needle);
bool Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
struct ssl_primary_config *dest);
bool Curl_clone_ssl_config(struct ssl_config_data* source,
struct ssl_config_data* dest);
void Curl_clone_general_ssl_config(struct ssl_general_config *source,
struct ssl_general_config *dest);
void Curl_free_ssl_config(struct ssl_config_data* sslc);
void Curl_free_primary_ssl_config(struct ssl_primary_config* sslc);
int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks,
int numsocks);
unsigned int Curl_rand(struct SessionHandle *);
@@ -94,11 +101,13 @@ CURLcode Curl_ssl_push_certinfo(struct SessionHandle * data, int certnum,
/* extract a session ID */
bool Curl_ssl_getsessionid(struct connectdata *conn,
void **ssl_sessionid,
size_t *idsize) /* set 0 if unknown */;
size_t *idsize, /* set 0 if unknown */
int sockindex);
/* add a new session ID */
CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
void *ssl_sessionid,
size_t idsize);
size_t idsize,
int sockindex);
/* Kill a single session ID entry in the cache */
void Curl_ssl_kill_session(struct curl_ssl_session *session);
/* delete a session from the cache */