NSS: Full backend support for HTTPS proxies

Also adjusts SSL_IS_PROXY() to work correctly without requiring
sockindex.
This commit is contained in:
Alex Rousskov 2015-11-03 10:37:52 -07:00 committed by Daniel Stenberg
parent cd2644fe2c
commit 68a7d38aa0
3 changed files with 87 additions and 52 deletions

View File

@ -547,6 +547,7 @@ static CURLcode nss_load_key(struct connectdata *conn, int sockindex,
SECStatus status;
CURLcode result;
struct ssl_connect_data *ssl = conn->ssl;
struct SessionHandle *data = conn->data;
(void)sockindex; /* unused */
@ -564,8 +565,7 @@ static CURLcode nss_load_key(struct connectdata *conn, int sockindex,
SECMOD_WaitForAnyTokenEvent(mod, 0, 0);
PK11_IsPresent(slot);
status = PK11_Authenticate(slot, PR_TRUE,
conn->data->set.ssl.key_passwd);
status = PK11_Authenticate(slot, PR_TRUE, SSL_SET_OPTION(key_passwd));
PK11_FreeSlot(slot);
return (SECSuccess == status) ? CURLE_OK : CURLE_SSL_CERTPROBLEM;
@ -644,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->ssl_config.verifystatus) {
if(SSL_CONN_CONFIG(verifystatus)) {
SECStatus cacheResult;
const SECItemArray *csa = SSL_PeerStapledOCSPResponses(fd);
@ -670,7 +670,7 @@ static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig,
}
#endif
if(!conn->ssl_config.verifypeer) {
if(!SSL_CONN_CONFIG(verifypeer)) {
infof(conn->data, "skipping SSL peer certificate verification\n");
return SECSuccess;
}
@ -890,9 +890,12 @@ static SECStatus BadCertHandler(void *arg, PRFileDesc *sock)
CERTCertificate *cert;
/* remember the cert verification result */
data->set.ssl.certverifyresult = err;
if(SSL_IS_PROXY())
data->set.proxy_ssl.certverifyresult = err;
else
data->set.ssl.certverifyresult = err;
if(err == SSL_ERROR_BAD_CERT_DOMAIN && !conn->ssl_config.verifyhost)
if(err == SSL_ERROR_BAD_CERT_DOMAIN && !SSL_CONN_CONFIG(verifyhost))
/* we are asked not to verify the host name */
return SECSuccess;
@ -1328,19 +1331,9 @@ Curl_nss_check_cxn(struct connectdata *conn)
return -1; /* connection status unknown */
}
/*
* This function is called when an SSL connection is closed.
*/
void Curl_nss_close(struct connectdata *conn, int sockindex)
static void nss_close(struct ssl_connect_data *connssl)
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
if(connssl->handle) {
/* NSS closes the socket we previously handed to it, so we must mark it
as closed to avoid double close */
fake_sclose(conn->sock[sockindex]);
conn->sock[sockindex] = CURL_SOCKET_BAD;
if((connssl->client_nickname != NULL) || (connssl->obj_clicert != NULL))
/* A server might require different authentication based on the
* particular path being requested by the client. To support this
@ -1360,6 +1353,25 @@ void Curl_nss_close(struct connectdata *conn, int sockindex)
}
}
/*
* This function is called when an SSL connection is closed.
*/
void Curl_nss_close(struct connectdata *conn, int sockindex)
{
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
struct ssl_connect_data *connssl_proxy = &conn->proxy_ssl[sockindex];
if(connssl->handle || connssl_proxy->handle) {
/* NSS closes the socket we previously handed to it, so we must mark it
as closed to avoid double close */
fake_sclose(conn->sock[sockindex]);
conn->sock[sockindex] = CURL_SOCKET_BAD;
}
nss_close(connssl);
nss_close(connssl_proxy);
}
/* return true if NSS can provide error code (and possibly msg) for the
error */
static bool is_nss_error(CURLcode err)
@ -1398,8 +1410,8 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn,
int sockindex)
{
struct SessionHandle *data = conn->data;
const char *cafile = conn->ssl_config.CAfile;
const char *capath = conn->ssl_config.CApath;
const char *cafile = SSL_CONN_CONFIG(CAfile);
const char *capath = SSL_CONN_CONFIG(CApath);
if(cafile) {
CURLcode result = nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE);
@ -1447,9 +1459,10 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn,
}
static CURLcode nss_init_sslver(SSLVersionRange *sslver,
struct SessionHandle *data)
struct SessionHandle *data,
struct connectdata *conn)
{
switch (conn->ssl_config.version) {
switch (SSL_CONN_CONFIG(version)) {
default:
case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
@ -1550,6 +1563,7 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
curl_socket_t sockfd = conn->sock[sockindex];
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
CURLcode result;
bool second_layer = FALSE;
SSLVersionRange sslver = {
SSL_LIBRARY_VERSION_TLS_1_0, /* min */
@ -1608,18 +1622,18 @@ 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 && conn->ssl_config.verifypeer) ?
PR_FALSE : PR_TRUE;
ssl_no_cache = (data->set.general_ssl.sessionid
&& SSL_CONN_CONFIG(verifypeer)) ? PR_FALSE : PR_TRUE;
if(SSL_OptionSet(model, SSL_NO_CACHE, ssl_no_cache) != SECSuccess)
goto error;
/* enable/disable the requested SSL version(s) */
if(nss_init_sslver(&sslver, data) != CURLE_OK)
if(nss_init_sslver(&sslver, data, conn) != CURLE_OK)
goto error;
if(SSL_VersionRangeSet(model, &sslver) != SECSuccess)
goto error;
ssl_cbc_random_iv = !data->set.ssl.enable_beast;
ssl_cbc_random_iv = !SSL_SET_OPTION(enable_beast);
#ifdef SSL_CBC_RANDOM_IV
/* unless the user explicitly asks to allow the protocol vulnerability, we
use the work-around */
@ -1631,14 +1645,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(conn->ssl_config.cipher_list) {
if(set_ciphers(data, model, conn->ssl_config.cipher_list) != SECSuccess) {
if(SSL_CONN_CONFIG(cipher_list)) {
if(set_ciphers(data, model, SSL_CONN_CONFIG(cipher_list)) != SECSuccess) {
result = CURLE_SSL_CIPHER;
goto error;
}
}
if(!conn->ssl_config.verifypeer && conn->ssl_config.verifyhost)
if(!SSL_CONN_CONFIG(verifypeer) && SSL_CONN_CONFIG(verifyhost))
infof(data, "warning: ignoring value of ssl.verifyhost\n");
/* bypass the default SSL_AuthCertificate() hook in case we do not want to
@ -1646,14 +1660,19 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
if(SSL_AuthCertificateHook(model, nss_auth_cert_hook, conn) != SECSuccess)
goto error;
data->set.ssl.certverifyresult=0; /* not checked yet */
/* not checked yet */
if(SSL_IS_PROXY())
data->set.proxy_ssl.certverifyresult = 0;
else
data->set.ssl.certverifyresult = 0;
if(SSL_BadCertHook(model, BadCertHandler, conn) != SECSuccess)
goto error;
if(SSL_HandshakeCallback(model, HandshakeCallback, conn) != SECSuccess)
goto error;
if(conn->ssl_config.verifypeer) {
if(SSL_CONN_CONFIG(verifypeer)) {
const CURLcode rv = nss_load_ca_certificates(conn, sockindex);
if(rv) {
result = rv;
@ -1661,24 +1680,24 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
}
}
if(data->set.ssl.CRLfile) {
const CURLcode rv = nss_load_crl(data->set.ssl.CRLfile);
if(SSL_SET_OPTION(CRLfile)) {
const CURLcode rv = nss_load_crl(SSL_SET_OPTION(CRLfile));
if(rv) {
result = rv;
goto error;
}
infof(data, " CRLfile: %s\n", data->set.ssl.CRLfile);
infof(data, " CRLfile: %s\n", SSL_SET_OPTION(CRLfile));
}
if(data->set.ssl.cert) {
char *nickname = dup_nickname(data, data->set.ssl.cert);
if(SSL_SET_OPTION(cert)) {
char *nickname = dup_nickname(data, SSL_SET_OPTION(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.ssl.cert,
data->set.ssl.key);
CURLcode rv = cert_stuff(conn, sockindex, SSL_SET_OPTION(cert),
SSL_SET_OPTION(key));
if(rv) {
/* failf() is already done in cert_stuff() */
result = rv;
@ -1698,15 +1717,24 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
goto error;
}
/* wrap OS file descriptor by NSPR's file descriptor abstraction */
nspr_io = PR_ImportTCPSocket(sockfd);
if(!nspr_io)
goto error;
if(conn->proxy_ssl[sockindex].use) {
DEBUGASSERT(ssl_connection_complete == conn->proxy_ssl[sockindex].state);
DEBUGASSERT(conn->proxy_ssl[sockindex].handle != NULL);
nspr_io = conn->proxy_ssl[sockindex].handle;
second_layer = TRUE;
}
else {
/* wrap OS file descriptor by NSPR's file descriptor abstraction */
nspr_io = PR_ImportTCPSocket(sockfd);
if(!nspr_io)
goto error;
}
/* create our own NSPR I/O layer */
nspr_io_stub = PR_CreateIOLayerStub(nspr_io_identity, &nspr_io_methods);
if(!nspr_io_stub) {
PR_Close(nspr_io);
if(!second_layer)
PR_Close(nspr_io);
goto error;
}
@ -1715,7 +1743,8 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
/* push our new layer to the NSPR I/O stack */
if(PR_PushIOLayer(nspr_io, PR_TOP_IO_LAYER, nspr_io_stub) != PR_SUCCESS) {
PR_Close(nspr_io);
if(!second_layer)
PR_Close(nspr_io);
PR_Close(nspr_io_stub);
goto error;
}
@ -1723,7 +1752,8 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
/* import our model socket onto the current I/O stack */
connssl->handle = SSL_ImportFD(model, nspr_io);
if(!connssl->handle) {
PR_Close(nspr_io);
if(!second_layer)
PR_Close(nspr_io);
goto error;
}
@ -1731,12 +1761,12 @@ 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.ssl.key_passwd) {
SSL_SetPKCS11PinArg(connssl->handle, data->set.ssl.key_passwd);
if(SSL_SET_OPTION(key_passwd)) {
SSL_SetPKCS11PinArg(connssl->handle, SSL_SET_OPTION(key_passwd));
}
#ifdef SSL_ENABLE_OCSP_STAPLING
if(data->set.ssl.verifystatus) {
if(SSL_CONN_CONFIG(verifystatus)) {
if(SSL_OptionSet(connssl->handle, SSL_ENABLE_OCSP_STAPLING, PR_TRUE)
!= SECSuccess)
goto error;
@ -1793,7 +1823,8 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
/* Force handshake on next I/O */
SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE);
SSL_SetURL(connssl->handle, conn->host.name);
SSL_SetURL(connssl->handle, SSL_IS_PROXY() ? conn->http_proxy.host.name :
conn->host.name);
return CURLE_OK;
@ -1810,6 +1841,8 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex)
struct SessionHandle *data = conn->data;
CURLcode result = CURLE_SSL_CONNECT_ERROR;
PRUint32 timeout;
long * const certverifyresult = SSL_IS_PROXY() ?
&data->set.proxy_ssl.certverifyresult : &data->set.ssl.certverifyresult;
/* check timeout situation */
const long time_left = Curl_timeleft(data, NULL, TRUE);
@ -1825,9 +1858,9 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex)
if(PR_GetError() == PR_WOULD_BLOCK_ERROR)
/* blocking direction is updated by nss_update_connecting_state() */
return CURLE_AGAIN;
else if(conn->data->set.ssl.certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN)
else if(*certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN)
result = CURLE_PEER_FAILED_VERIFICATION;
else if(conn->data->set.ssl.certverifyresult!=0)
else if(*certverifyresult != 0)
result = CURLE_SSL_CACERT;
goto error;
}

View File

@ -308,7 +308,7 @@ 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
#if defined(USE_OPENSSL) || defined(USE_NSS)
conn->proxy_ssl[sockindex] = conn->ssl[sockindex];
memset(&conn->ssl[sockindex], 0, sizeof(conn->ssl[sockindex]));
#else

View File

@ -48,7 +48,9 @@
/* set of helper macros for the backends to access the correct fields. For the
proxy or for the remote host - to properly support HTTPS proxy */
#define SSL_IS_PROXY() (CURLPROXY_HTTPS == conn->http_proxy.proxytype)
#define SSL_IS_PROXY() (CURLPROXY_HTTPS == conn->http_proxy.proxytype && \
ssl_connection_complete != conn->proxy_ssl[conn->sock[SECONDARYSOCKET] == \
CURL_SOCKET_BAD ? FIRSTSOCKET : SECONDARYSOCKET].state)
#define SSL_SET_OPTION(var) (SSL_IS_PROXY() ? data->set.proxy_ssl.var : \
data->set.ssl.var)
#define SSL_CONN_CONFIG(var) (SSL_IS_PROXY() ? \