nss: do not ignore value of CURLOPT_SSL_VERIFYPEER

When NSS-powered libcurl connected to a SSL server with
CURLOPT_SSL_VERIFYPEER equal to zero, NSS remembered that the peer
certificate was accepted by libcurl and did not ask the second time when
connecting to the same server with CURLOPT_SSL_VERIFYPEER equal to one.

This patch turns off the SSL session cache for the particular SSL socket
if peer verification is disabled.  In order to avoid any performance
impact, the peer verification is completely skipped in that case, which
makes it even faster than before.

Bug: https://bugzilla.redhat.com/678580
This commit is contained in:
Kamil Dudka 2011-03-15 14:52:26 +01:00
parent 5a433a033f
commit 806dbb022b
3 changed files with 37 additions and 19 deletions

View File

@ -35,6 +35,7 @@ This release includes the following bugfixes:
o Fixed detection of recvfrom arguments on Android/bionic
o GSS: handle reuse fix
o transfer: avoid insane conversion of time_t
o nss: do not ignore value of CURLOPT_SSL_VERIFYPEER in certain cases
This release includes the following known bugs:

View File

@ -2007,7 +2007,10 @@ The default value for this option is 2.
This option controls checking the server's certificate's claimed identity.
The server could be lying. To control lying, see
\fICURLOPT_SSL_VERIFYPEER\fP.
\fICURLOPT_SSL_VERIFYPEER\fP. If libcurl is built against NSS and
\fICURLOPT_SSL_VERIFYPEER\fP is zero, \fICURLOPT_SSL_VERIFYHOST\fP
is ignored.
.IP CURLOPT_CERTINFO
Pass a long set to 1 to enable libcurl's certificate chain info gatherer. With
this enabled, libcurl (if built with OpenSSL) will extract lots of information

View File

@ -622,17 +622,28 @@ static char * nss_get_password(PK11SlotInfo * slot, PRBool retry, void *arg)
return (char *)PORT_Strdup((char *)arg);
}
/* bypass the default SSL_AuthCertificate() hook in case we do not want to
* verify peer */
static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig,
PRBool isServer)
{
struct connectdata *conn = (struct connectdata *)arg;
if(!conn->data->set.ssl.verifypeer) {
infof(conn->data, "skipping SSL peer certificate verification\n");
return SECSuccess;
}
return SSL_AuthCertificate(arg, fd, checksig, isServer);
}
static SECStatus BadCertHandler(void *arg, PRFileDesc *sock)
{
SECStatus success = SECSuccess;
SECStatus result = SECFailure;
struct connectdata *conn = (struct connectdata *)arg;
PRErrorCode err = PR_GetError();
CERTCertificate *cert = NULL;
char *subject, *subject_cn, *issuer;
if(conn->data->set.ssl.certverifyresult!=0)
return success;
conn->data->set.ssl.certverifyresult=err;
cert = SSL_PeerCertificate(sock);
subject = CERT_NameToAscii(&cert->subject);
@ -643,12 +654,8 @@ static SECStatus BadCertHandler(void *arg, PRFileDesc *sock)
switch(err) {
case SEC_ERROR_CA_CERT_INVALID:
infof(conn->data, "Issuer certificate is invalid: '%s'\n", issuer);
if(conn->data->set.ssl.verifypeer)
success = SECFailure;
break;
case SEC_ERROR_UNTRUSTED_ISSUER:
if(conn->data->set.ssl.verifypeer)
success = SECFailure;
infof(conn->data, "Certificate is signed by an untrusted issuer: '%s'\n",
issuer);
break;
@ -656,37 +663,31 @@ static SECStatus BadCertHandler(void *arg, PRFileDesc *sock)
if(conn->data->set.ssl.verifyhost) {
failf(conn->data, "SSL: certificate subject name '%s' does not match "
"target host name '%s'", subject_cn, conn->host.dispname);
success = SECFailure;
} else {
result = SECSuccess;
infof(conn->data, "warning: SSL: certificate subject name '%s' does not "
"match target host name '%s'\n", subject_cn, conn->host.dispname);
}
break;
case SEC_ERROR_EXPIRED_CERTIFICATE:
if(conn->data->set.ssl.verifypeer)
success = SECFailure;
infof(conn->data, "Remote Certificate has expired.\n");
break;
case SEC_ERROR_UNKNOWN_ISSUER:
if(conn->data->set.ssl.verifypeer)
success = SECFailure;
infof(conn->data, "Peer's certificate issuer is not recognized: '%s'\n",
issuer);
break;
default:
if(conn->data->set.ssl.verifypeer)
success = SECFailure;
infof(conn->data, "Bad certificate received. Subject = '%s', "
"Issuer = '%s'\n", subject, issuer);
break;
}
if(success == SECSuccess)
if(result == SECSuccess)
infof(conn->data, "SSL certificate verify ok.\n");
PR_Free(subject);
PR_Free(subject_cn);
PR_Free(issuer);
return success;
return result;
}
/**
@ -1154,6 +1155,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
PRBool ssl2 = PR_FALSE;
PRBool ssl3 = PR_FALSE;
PRBool tlsv1 = PR_FALSE;
PRBool ssl_no_cache;
struct SessionHandle *data = conn->data;
curl_socket_t sockfd = conn->sock[sockindex];
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
@ -1227,6 +1229,11 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess)
goto error;
/* do not use SSL cache if we are not going to verify peer */
ssl_no_cache = (data->set.ssl.verifypeer) ? PR_FALSE : PR_TRUE;
if(SSL_OptionSet(model, SSL_NO_CACHE, ssl_no_cache) != SECSuccess)
goto error;
switch (data->set.ssl.version) {
default:
case CURL_SSLVERSION_DEFAULT:
@ -1277,9 +1284,16 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
}
}
if(data->set.ssl.verifyhost == 1)
if(!data->set.ssl.verifypeer && data->set.ssl.verifyhost)
infof(data, "warning: ignoring value of ssl.verifyhost\n");
else if(data->set.ssl.verifyhost == 1)
infof(data, "warning: ignoring unsupported value (1) of ssl.verifyhost\n");
/* bypass the default SSL_AuthCertificate() hook in case we do not want to
* verify peer */
if(SSL_AuthCertificateHook(model, nss_auth_cert_hook, conn) != SECSuccess)
goto error;
data->set.ssl.certverifyresult=0; /* not checked yet */
if(SSL_BadCertHook(model, (SSLBadCertHandler) BadCertHandler, conn)
!= SECSuccess) {