diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 480603f0a..223f5ea11 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -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: diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index 56a54a9db..2dd536cf5 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -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 diff --git a/lib/nss.c b/lib/nss.c index be26253c4..a3bd77cfb 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -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) {