summaryrefslogtreecommitdiff
path: root/lib/vtls/nss.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/vtls/nss.c')
-rw-r--r--lib/vtls/nss.c168
1 files changed, 106 insertions, 62 deletions
diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c
index 6e0dc5b29..2d8439934 100644
--- a/lib/vtls/nss.c
+++ b/lib/vtls/nss.c
@@ -337,9 +337,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 Curl_easy *data, enum dupstring cert_kind)
+static char* dup_nickname(struct Curl_easy *data, const char *str)
{
- const char *str = data->set.str[cert_kind];
const char *n;
if(!is_file(str))
@@ -585,6 +584,7 @@ static CURLcode nss_load_key(struct connectdata *conn, int sockindex,
SECStatus status;
CURLcode result;
struct ssl_connect_data *ssl = conn->ssl;
+ struct Curl_easy *data = conn->data;
(void)sockindex; /* unused */
@@ -602,8 +602,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.str[STRING_KEY_PASSWD]);
+ status = PK11_Authenticate(slot, PR_TRUE, SSL_SET_OPTION(key_passwd));
PK11_FreeSlot(slot);
return (SECSuccess == status) ? CURLE_OK : CURLE_SSL_CERTPROBLEM;
@@ -682,7 +681,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(SSL_CONN_CONFIG(verifystatus)) {
SECStatus cacheResult;
const SECItemArray *csa = SSL_PeerStapledOCSPResponses(fd);
@@ -708,7 +707,7 @@ static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig,
}
#endif
- if(!conn->data->set.ssl.verifypeer) {
+ if(!SSL_CONN_CONFIG(verifypeer)) {
infof(conn->data, "skipping SSL peer certificate verification\n");
return SECSuccess;
}
@@ -933,9 +932,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 && !data->set.ssl.verifyhost)
+ if(err == SSL_ERROR_BAD_CERT_DOMAIN && !SSL_CONN_CONFIG(verifyhost))
/* we are asked not to verify the host name */
return SECSuccess;
@@ -1372,36 +1374,55 @@ Curl_nss_check_cxn(struct connectdata *conn)
return -1; /* connection status unknown */
}
+static void nss_close(struct ssl_connect_data *connssl)
+{
+ /* before the cleanup, check whether we are using a client certificate */
+ const bool client_cert = (connssl->client_nickname != NULL)
+ || (connssl->obj_clicert != NULL);
+
+ free(connssl->client_nickname);
+ connssl->client_nickname = NULL;
+
+ /* destroy all NSS objects in order to avoid failure of NSS shutdown */
+ Curl_llist_destroy(connssl->obj_list, NULL);
+ connssl->obj_list = NULL;
+ connssl->obj_clicert = NULL;
+
+ if(connssl->handle) {
+ if(client_cert)
+ /* A server might require different authentication based on the
+ * particular path being requested by the client. To support this
+ * scenario, we must ensure that a connection will never reuse the
+ * authentication data from a previous connection. */
+ SSL_InvalidateSession(connssl->handle);
+
+ PR_Close(connssl->handle);
+ connssl->handle = NULL;
+ }
+}
+
/*
* 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) {
+ 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;
+ }
- 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
- * scenario, we must ensure that a connection will never reuse the
- * authentication data from a previous connection. */
- SSL_InvalidateSession(connssl->handle);
-
- free(connssl->client_nickname);
- connssl->client_nickname = NULL;
- /* destroy all NSS objects in order to avoid failure of NSS shutdown */
- Curl_llist_destroy(connssl->obj_list, NULL);
- connssl->obj_list = NULL;
- connssl->obj_clicert = NULL;
+ if(connssl->handle)
+ /* nss_close(connssl) will transitively close also connssl_proxy->handle
+ if both are used. Clear it to avoid a double close leading to crash. */
+ connssl_proxy->handle = NULL;
- PR_Close(connssl->handle);
- connssl->handle = NULL;
- }
+ nss_close(connssl);
+ nss_close(connssl_proxy);
}
/* return true if NSS can provide error code (and possibly msg) for the
@@ -1442,8 +1463,8 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn,
int sockindex)
{
struct Curl_easy *data = conn->data;
- const char *cafile = data->set.ssl.CAfile;
- const char *capath = data->set.ssl.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);
@@ -1491,9 +1512,10 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn,
}
static CURLcode nss_init_sslver(SSLVersionRange *sslver,
- struct Curl_easy *data)
+ struct Curl_easy *data,
+ struct connectdata *conn)
{
- switch(data->set.ssl.version) {
+ switch (SSL_CONN_CONFIG(version)) {
case CURL_SSLVERSION_DEFAULT:
/* map CURL_SSLVERSION_DEFAULT to NSS default */
if(SSL_VersionRangeGetDefault(ssl_variant_stream, sslver) != SECSuccess)
@@ -1614,6 +1636,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 */
@@ -1672,18 +1695,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 && data->set.ssl.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 */
@@ -1695,14 +1718,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(SSL_CONN_CONFIG(cipher_list)) {
+ if(set_ciphers(data, model, SSL_CONN_CONFIG(cipher_list)) != SECSuccess) {
result = CURLE_SSL_CIPHER;
goto error;
}
}
- if(!data->set.ssl.verifypeer && data->set.ssl.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
@@ -1710,14 +1733,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(data->set.ssl.verifypeer) {
+ if(SSL_CONN_CONFIG(verifypeer)) {
const CURLcode rv = nss_load_ca_certificates(conn, sockindex);
if(rv) {
result = rv;
@@ -1725,24 +1753,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.str[STRING_CERT]) {
- char *nickname = dup_nickname(data, STRING_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.str[STRING_CERT],
- data->set.str[STRING_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;
@@ -1762,15 +1790,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;
}
@@ -1779,7 +1816,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;
}
@@ -1787,7 +1825,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;
}
@@ -1795,12 +1834,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.str[STRING_KEY_PASSWD]) {
- SSL_SetPKCS11PinArg(connssl->handle, data->set.str[STRING_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;
@@ -1860,11 +1899,14 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
goto error;
/* propagate hostname to the TLS layer */
- if(SSL_SetURL(connssl->handle, conn->host.name) != SECSuccess)
+ if(SSL_SetURL(connssl->handle, SSL_IS_PROXY() ? conn->http_proxy.host.name :
+ conn->host.name) != SECSuccess)
goto error;
/* prevent NSS from re-using the session for a different hostname */
- if(SSL_SetSockPeerID(connssl->handle, conn->host.name) != SECSuccess)
+ if(SSL_SetSockPeerID(connssl->handle, SSL_IS_PROXY() ?
+ conn->http_proxy.host.name : conn->host.name)
+ != SECSuccess)
goto error;
return CURLE_OK;
@@ -1882,6 +1924,8 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex)
struct Curl_easy *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);
@@ -1897,9 +1941,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;
}
@@ -1908,11 +1952,11 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex)
if(result)
goto error;
- if(data->set.str[STRING_SSL_ISSUERCERT]) {
+ if(SSL_SET_OPTION(issuercert)) {
SECStatus ret = SECFailure;
- char *nickname = dup_nickname(data, STRING_SSL_ISSUERCERT);
+ char *nickname = dup_nickname(data, SSL_SET_OPTION(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);
}