From 7f70dbcad58eb7183d129860192d6968dd7063a1 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 12 Feb 2007 22:32:37 +0000 Subject: Rob Crittenden added support for NSS (Network Security Service) for the SSL/TLS layer. http://www.mozilla.org/projects/security/pki/nss/ --- lib/nss.c | 605 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 605 insertions(+) create mode 100644 lib/nss.c (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c new file mode 100644 index 000000000..82c218d0a --- /dev/null +++ b/lib/nss.c @@ -0,0 +1,605 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2007, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id$ + ***************************************************************************/ + +/* + * Source file for all NSS-specific code for the TLS/SSL layer. No code + * but sslgen.c should ever call or use these functions. + */ + +#include "setup.h" + +#include +#include +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#include "urldata.h" +#include "sendf.h" +#include "formdata.h" /* for the boundary function */ +#include "url.h" /* for the ssl config check function */ +#include "connect.h" /* Curl_sockerrno() proto */ +#include "strequal.h" +#include "select.h" +#include "sslgen.h" + +#define _MPRINTF_REPLACE /* use the internal *printf() functions */ +#include + +#ifdef USE_NSS + +#include "nssg.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "memory.h" +#include "easyif.h" /* for Curl_convert_from_utf8 prototype */ + +/* The last #include file should be: */ +#include "memdebug.h" + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +PRFileDesc *PR_ImportTCPSocket(PRInt32 osfd); + +static int initialized = 0; +static int noverify = 0; + +typedef struct { + PRInt32 retryCount; + struct SessionHandle *data; +} pphrase_arg_t; + +typedef struct { + const char *name; + int num; + PRInt32 version; /* protocol version valid for this cipher */ +} cipher_s; + +/* the table itself is defined in nss_engine_init.c */ +#ifdef NSS_ENABLE_ECC +#define ciphernum 48 +#else +#define ciphernum 23 +#endif + +enum sslversion { SSL2 = 1, SSL3 = 2, TLS = 4 }; + +cipher_s cipherlist[ciphernum] = { + /* SSL2 cipher suites */ + {"rc4", SSL_EN_RC4_128_WITH_MD5, SSL2}, + {"rc4export", SSL_EN_RC4_128_EXPORT40_WITH_MD5, SSL2}, + {"rc2", SSL_EN_RC2_128_CBC_WITH_MD5, SSL2}, + {"rc2export", SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, SSL2}, + {"des", SSL_EN_DES_64_CBC_WITH_MD5, SSL2}, + {"desede3", SSL_EN_DES_192_EDE3_CBC_WITH_MD5, SSL2}, + /* SSL3/TLS cipher suites */ + {"rsa_rc4_128_md5", SSL_RSA_WITH_RC4_128_MD5, SSL3 | TLS}, + {"rsa_rc4_128_sha", SSL_RSA_WITH_RC4_128_SHA, SSL3 | TLS}, + {"rsa_3des_sha", SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL3 | TLS}, + {"rsa_des_sha", SSL_RSA_WITH_DES_CBC_SHA, SSL3 | TLS}, + {"rsa_rc4_40_md5", SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL3 | TLS}, + {"rsa_rc2_40_md5", SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, SSL3 | TLS}, + {"rsa_null_md5", SSL_RSA_WITH_NULL_MD5, SSL3 | TLS}, + {"rsa_null_sha", SSL_RSA_WITH_NULL_SHA, SSL3 | TLS}, + {"fips_3des_sha", SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, SSL3 | TLS}, + {"fips_des_sha", SSL_RSA_FIPS_WITH_DES_CBC_SHA, SSL3 | TLS}, + {"fortezza", SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, SSL3 | TLS}, + {"fortezza_rc4_128_sha", SSL_FORTEZZA_DMS_WITH_RC4_128_SHA, SSL3 | TLS}, + {"fortezza_null", SSL_FORTEZZA_DMS_WITH_NULL_SHA, SSL3 | TLS}, + /* TLS 1.0: Exportable 56-bit Cipher Suites. */ + {"rsa_des_56_sha", TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, SSL3 | TLS}, + {"rsa_rc4_56_sha", TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, SSL3 | TLS}, + /* AES ciphers. */ + {"rsa_aes_128_sha", TLS_RSA_WITH_AES_128_CBC_SHA, SSL3 | TLS}, + {"rsa_aes_256_sha", TLS_RSA_WITH_AES_256_CBC_SHA, SSL3 | TLS}, +#ifdef NSS_ENABLE_ECC + /* ECC ciphers. */ + {"ecdh_ecdsa_null_sha", TLS_ECDH_ECDSA_WITH_NULL_SHA, TLS}, + {"ecdh_ecdsa_rc4_128_sha", TLS_ECDH_ECDSA_WITH_RC4_128_SHA, TLS}, + {"ecdh_ecdsa_3des_sha", TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS}, + {"ecdh_ecdsa_aes_128_sha", TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS}, + {"ecdh_ecdsa_aes_256_sha", TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, TLS}, + {"ecdhe_ecdsa_null_sha", TLS_ECDHE_ECDSA_WITH_NULL_SHA, TLS}, + {"ecdhe_ecdsa_rc4_128_sha", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS}, + {"ecdhe_ecdsa_3des_sha", TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS}, + {"ecdhe_ecdsa_aes_128_sha", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS}, + {"ecdhe_ecdsa_aes_256_sha", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS}, + {"ecdh_rsa_null_sha", TLS_ECDH_RSA_WITH_NULL_SHA, TLS}, + {"ecdh_rsa_128_sha", TLS_ECDH_RSA_WITH_RC4_128_SHA, TLS}, + {"ecdh_rsa_3des_sha", TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, TLS}, + {"ecdh_rsa_aes_128_sha", TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS}, + {"ecdh_rsa_aes_256_sha", TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, TLS}, + {"echde_rsa_null", TLS_ECDHE_RSA_WITH_NULL_SHA, TLS}, + {"ecdhe_rsa_rc4_128_sha", TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLS}, + {"ecdhe_rsa_3des_sha", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS}, + {"ecdhe_rsa_aes_128_sha", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS}, + {"ecdhe_rsa_aes_256_sha", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS}, + {"ecdh_anon_null_sha", TLS_ECDH_anon_WITH_NULL_SHA, TLS}, + {"ecdh_anon_rc4_128sha", TLS_ECDH_anon_WITH_RC4_128_SHA, TLS}, + {"ecdh_anon_3des_sha", TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, TLS}, + {"ecdh_anon_aes_128_sha", TLS_ECDH_anon_WITH_AES_128_CBC_SHA, TLS}, + {"ecdh_anon_aes_256_sha", TLS_ECDH_anon_WITH_AES_256_CBC_SHA, TLS}, +#endif +}; + +static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model, + char *cipher_list) +{ + int i; + PRBool cipher_state[ciphernum]; + PRBool found; + char *cipher; + SECStatus rv; + + /* First disable all ciphers. This uses a different max value in case + * NSS adds more ciphers later we don't want them available by + * accident + */ + for(i=0; idata->set.key_passwd) + return (char *)PORT_Strdup((char *)parg->data->set.key_passwd); + else + return NULL; +} + +static SECStatus nss_Init_Tokens(struct connectdata * conn) +{ + PK11SlotList *slotList; + PK11SlotListElement *listEntry; + SECStatus ret, status = SECSuccess; + pphrase_arg_t *parg; + + parg = (pphrase_arg_t *) malloc(sizeof(*parg)); + parg->retryCount = 0; + parg->data = conn->data; + + PK11_SetPasswordFunc(nss_get_password); + + slotList = + PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_TRUE, NULL); + + for(listEntry = PK11_GetFirstSafe(slotList); + listEntry; listEntry = listEntry->next) { + PK11SlotInfo *slot = listEntry->slot; + + if(PK11_NeedLogin(slot) && PK11_NeedUserInit(slot)) { + if(slot == PK11_GetInternalKeySlot()) { + failf(conn->data, "The NSS database has not been initialized.\n"); + } + else { + failf(conn->data, "The token %s has not been initialized.", + PK11_GetTokenName(slot)); + } + PK11_FreeSlot(slot); + continue; + } + + ret = PK11_Authenticate(slot, PR_TRUE, parg); + if(SECSuccess != ret) { + status = SECFailure; + break; + } + parg->retryCount = 0; /* reset counter to 0 for the next token */ + PK11_FreeSlot(slot); + } + + free(parg); + return status; +} + +static SECStatus BadCertHandler(void *arg, PRFileDesc * socket) +{ + SECStatus success = SECSuccess; + (void)arg; + (void)socket; + + return success; +} + +/** + * Inform the application that the handshake is complete. + */ +static SECStatus HandshakeCallback(PRFileDesc * socket, void *arg) +{ + (void)socket; + (void)arg; + return SECSuccess; +} + +/** + * + * Callback to pick the SSL client certificate. + */ +static SECStatus SelectClientCert(void *arg, PRFileDesc * socket, + struct CERTDistNamesStr * caNames, + struct CERTCertificateStr ** pRetCert, + struct SECKEYPrivateKeyStr ** pRetKey) +{ + CERTCertificate *cert; + SECKEYPrivateKey *privKey; + char *nickname = (char *)arg; + void *proto_win = NULL; + SECStatus secStatus = SECFailure; + (void)caNames; + + proto_win = SSL_RevealPinArg(socket); + + cert = PK11_FindCertFromNickname(nickname, proto_win); + if(cert) { + privKey = PK11_FindKeyByAnyCert(cert, proto_win); + if(privKey) { + secStatus = SECSuccess; + } + else { + CERT_DestroyCertificate(cert); + } + } + + if(secStatus == SECSuccess) { + *pRetCert = cert; + *pRetKey = privKey; + } + + return secStatus; +} + +/** + * Global SSL init + * + * @retval 0 error initializing SSL + * @retval 1 SSL initialized successfully + */ +int Curl_nss_init(void) +{ + if(!initialized) + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 256); + + /* We will actually initialize NSS later */ + + return 1; +} + +/* Global cleanup */ +void Curl_nss_cleanup(void) +{ + NSS_Shutdown(); + initialized = 0; +} + +/* + * This function uses SSL_peek to determine connection status. + * + * Return codes: + * 1 means the connection is still in place + * 0 means the connection has been closed + * -1 means the connection status is unknown + */ +int +Curl_nss_check_cxn(struct connectdata *conn) +{ + int rc; + char buf; + + rc = + PR_Recv(conn->ssl[FIRSTSOCKET].handle, (void *)&buf, 1, PR_MSG_PEEK, + PR_SecondsToInterval(1)); + if(rc > 0) + return 1; /* connection still in place */ + + if(rc == 0) + return 0; /* connection has been closed */ + + return -1; /* connection status unknown */ +} + +/* + * This function is called when an SSL connection is closed. + */ +void Curl_nss_close(struct connectdata *conn) +{ + int i; + + for(i=0; i<2; i++) { + struct ssl_connect_data *connssl = &conn->ssl[i]; + + if(connssl->handle) { + PR_Close(connssl->handle); + connssl->handle = NULL; + } + connssl->use = FALSE; /* get back to ordinary socket usage */ + } +} + +/* + * This function is called when the 'data' struct is going away. Close + * down everything and free all resources! + */ +int Curl_nss_close_all(struct SessionHandle *data) +{ + (void)data; + return 0; +} + +CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) +{ + PRInt32 err; + PRFileDesc *model = NULL; + PRBool ssl2, ssl3, tlsv1; + struct SessionHandle *data = conn->data; + curl_socket_t sockfd = conn->sock[sockindex]; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; + SECStatus rv; + int curlerr = CURLE_SSL_CONNECT_ERROR; + + /* FIXME. NSS doesn't support multiple databases open at the same time. */ + if(!initialized) { + if(!data->set.ssl.CAfile) { + if(data->set.ssl.verifypeer) { + failf(data, "No NSS cacert database specified."); + return CURLE_SSL_CACERT_BADFILE; + } + else { + rv = NSS_NoDB_Init(NULL); + noverify = 1; + } + } + else { + rv = NSS_Initialize(data->set.ssl.CAfile, NULL, NULL, "secmod.db", + NSS_INIT_READONLY); + } + if(rv != SECSuccess) { + curlerr = CURLE_SSL_CACERT_BADFILE; + goto error; + } + } + + NSS_SetDomesticPolicy(); + + model = PR_NewTCPSocket(); + if(!model) + goto error; + model = SSL_ImportFD(NULL, model); + + if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess) + goto error; + if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess) + goto error; + if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess) + goto error; + + ssl2 = ssl3 = tlsv1 = PR_FALSE; + + switch (data->set.ssl.version) { + default: + case CURL_SSLVERSION_DEFAULT: + ssl2 = ssl3 = tlsv1 = PR_TRUE; + break; + case CURL_SSLVERSION_TLSv1: + tlsv1 = PR_TRUE; + break; + case CURL_SSLVERSION_SSLv2: + ssl2 = PR_TRUE; + break; + case CURL_SSLVERSION_SSLv3: + ssl3 = PR_TRUE; + break; + } + + if(SSL_OptionSet(model, SSL_ENABLE_SSL2, ssl2) != SECSuccess) + goto error; + if(SSL_OptionSet(model, SSL_ENABLE_SSL3, ssl3) != SECSuccess) + goto error; + if(SSL_OptionSet(model, SSL_ENABLE_TLS, tlsv1) != SECSuccess) + goto error; + + if(data->set.ssl.cipher_list) { + if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) + goto error; + } + + if(SSL_BadCertHook(model, (SSLBadCertHandler) BadCertHandler, NULL) + != SECSuccess) + goto error; + if(SSL_HandshakeCallback(model, (SSLHandshakeCallback) HandshakeCallback, + NULL) != SECSuccess) + goto error; + + if(data->set.cert) { + if(SSL_GetClientAuthDataHook(model, + (SSLGetClientAuthData) SelectClientCert, + (void *)data->set.cert) != SECSuccess) { + curlerr = CURLE_SSL_CERTPROBLEM; + goto error; + } + if(nss_Init_Tokens(conn) != SECSuccess) + goto error; + } + + /* Import our model socket onto the existing file descriptor */ + connssl->handle = PR_ImportTCPSocket(sockfd); + connssl->handle = SSL_ImportFD(model, connssl->handle); + if(!connssl->handle) + goto error; + + /* Force handshake on next I/O */ + SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE); + + SSL_SetURL(connssl->handle, conn->host.name); + + return CURLE_OK; + +error: + err = PR_GetError(); + failf(data, "NSS error %d", err); + if(model) + PR_Close(model); + return curlerr; +} + +/* return number of sent (non-SSL) bytes */ +int Curl_nss_send(struct connectdata *conn, /* connection data */ + int sockindex, /* socketindex */ + void *mem, /* send this data */ + size_t len) /* amount to write */ +{ + PRInt32 err; + struct SessionHandle *data = conn->data; + PRInt32 timeout; + int rc; + + if(data->set.timeout) + timeout = PR_MillisecondsToInterval(data->set.timeout); + else + timeout = PR_MillisecondsToInterval(DEFAULT_CONNECT_TIMEOUT); + + rc = PR_Send(conn->ssl[sockindex].handle, mem, (int)len, 0, timeout); + + if(rc < 0) { + err = PR_GetError(); + + if(err == PR_IO_TIMEOUT_ERROR) { + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEOUTED; + } + + failf(conn->data, "SSL write: error %d\n", err); + return -1; + } + return rc; /* number of bytes */ +} + +/* + * If the read would block we return -1 and set 'wouldblock' to TRUE. + * Otherwise we return the amount of data read. Other errors should return -1 + * and set 'wouldblock' to FALSE. + */ +ssize_t Curl_nss_recv(struct connectdata * conn, /* connection data */ + int num, /* socketindex */ + char *buf, /* store read data here */ + size_t buffersize, /* max amount to read */ + bool * wouldblock) +{ + ssize_t nread; + struct SessionHandle *data = conn->data; + PRInt32 timeout; + + if(data->set.timeout) + timeout = PR_SecondsToInterval(data->set.timeout); + else + timeout = PR_MillisecondsToInterval(DEFAULT_CONNECT_TIMEOUT); + + nread = PR_Recv(conn->ssl[num].handle, buf, (int)buffersize, 0, timeout); + *wouldblock = FALSE; + if(nread < 0) { + /* failed SSL read */ + PRInt32 err = PR_GetError(); + + if(err == PR_WOULD_BLOCK_ERROR) { + *wouldblock = TRUE; + return -1; /* basically EWOULDBLOCK */ + } + if(err == PR_IO_TIMEOUT_ERROR) { + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEOUTED; + } + failf(conn->data, "SSL read: errno %d", err); + return -1; + } + return nread; +} + +size_t Curl_nss_version(char *buffer, size_t size) +{ + return snprintf(buffer, size, " NSS/%s", NSS_VERSION); +} +#endif /* USE_NSS */ -- cgit v1.2.1 From 569c169559823383042202f46b5ec554428102ad Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Tue, 13 Feb 2007 17:28:40 +0000 Subject: use our own ISSPACE macro --- lib/nss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 82c218d0a..1019b0c43 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -180,7 +180,7 @@ static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model, cipher = cipher_list; while(cipher_list && (cipher_list[0])) { - while((*cipher) && (isspace(*cipher))) + while((*cipher) && (ISSPACE(*cipher))) ++cipher; if((cipher_list = strchr(cipher, ','))) { -- cgit v1.2.1 From a1d598399146984c99baa46db148e87c75261033 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Fri, 16 Feb 2007 18:19:35 +0000 Subject: use macros ERRNO, SET_ERRNO(), SOCKERRNO and SET_SOCKERRNO() for errno handling --- lib/nss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 1019b0c43..afeb2ffb9 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -42,7 +42,7 @@ #include "sendf.h" #include "formdata.h" /* for the boundary function */ #include "url.h" /* for the ssl config check function */ -#include "connect.h" /* Curl_sockerrno() proto */ +#include "connect.h" #include "strequal.h" #include "select.h" #include "sslgen.h" -- cgit v1.2.1 From c514a2a89aa1c1e06b70405eedb4e1f70b27fd10 Mon Sep 17 00:00:00 2001 From: Gisle Vanem Date: Mon, 26 Feb 2007 04:24:26 +0000 Subject: Removed inclusion of and in .c-files since they're already included through "setup.h". --- lib/nss.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index afeb2ffb9..652d9f1f7 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -31,9 +31,6 @@ #include #include #include -#ifdef HAVE_SYS_TYPES_H -#include -#endif #ifdef HAVE_SYS_SOCKET_H #include #endif -- cgit v1.2.1 From fe1fe64fd44bf46ea0f7c9b4bb2b1cb5637174cb Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 25 May 2007 21:56:27 +0000 Subject: Rob Crittenden fixed bug #1705802 (http://curl.haxx.se/bug/view.cgi?id=1705802), which was filed by Daniel Black identifying several FTP-SSL test cases fail when we build libcurl with NSS for TLS/SSL. Listed as #42 in KNOWN_BUGS. --- lib/nss.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 652d9f1f7..ec7815400 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -74,6 +74,8 @@ PRFileDesc *PR_ImportTCPSocket(PRInt32 osfd); static int initialized = 0; static int noverify = 0; +#define HANDSHAKE_TIMEOUT 30 + typedef struct { PRInt32 retryCount; struct SessionHandle *data; @@ -513,6 +515,12 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) SSL_SetURL(connssl->handle, conn->host.name); + /* Force the handshake now */ + if (SSL_ForceHandshakeWithTimeout(connssl->handle, + PR_SecondsToInterval(HANDSHAKE_TIMEOUT)) + != SECSuccess) + goto error; + return CURLE_OK; error: -- cgit v1.2.1 From 4a2f0fb2beb95b2edccead115a92971643bd5297 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Fri, 20 Jul 2007 00:41:12 +0000 Subject: Made some const arrays static to avoid unnecessary stack usage. --- lib/nss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index ec7815400..189a19a0c 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -96,7 +96,7 @@ typedef struct { enum sslversion { SSL2 = 1, SSL3 = 2, TLS = 4 }; -cipher_s cipherlist[ciphernum] = { +static const cipher_s cipherlist[ciphernum] = { /* SSL2 cipher suites */ {"rc4", SSL_EN_RC4_128_WITH_MD5, SSL2}, {"rc4export", SSL_EN_RC4_128_EXPORT40_WITH_MD5, SSL2}, -- cgit v1.2.1 From f1fa7b8ba469d9b8681e30f107b44004695b32e9 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 29 Jul 2007 12:54:05 +0000 Subject: Bug report #1759542 (http://curl.haxx.se/bug/view.cgi?id=1759542). A bad use of a socket after it has been closed, when the FTP-SSL data connection is taken down. --- lib/nss.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 189a19a0c..c99258969 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -384,18 +384,13 @@ Curl_nss_check_cxn(struct connectdata *conn) /* * This function is called when an SSL connection is closed. */ -void Curl_nss_close(struct connectdata *conn) +void Curl_nss_close(struct connectdata *conn, int sockindex) { - int i; - - for(i=0; i<2; i++) { - struct ssl_connect_data *connssl = &conn->ssl[i]; + struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - if(connssl->handle) { - PR_Close(connssl->handle); - connssl->handle = NULL; - } - connssl->use = FALSE; /* get back to ordinary socket usage */ + if(connssl->handle) { + PR_Close(connssl->handle); + connssl->handle = NULL; } } -- cgit v1.2.1 From 50c10aa5bf545eedfdbe561116656b6ec12654cd Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 1 Aug 2007 21:20:01 +0000 Subject: Patrick Monnerat and I modified libcurl so that now it *copies* all strings passed to it with curl_easy_setopt()! Previously it has always just refered to the data, forcing the user to keep the data around until libcurl is done with it. That is now history and libcurl will instead clone the given strings and keep private copies. --- lib/nss.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index c99258969..e90156e15 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -225,8 +225,8 @@ static char * nss_get_password(PK11SlotInfo * slot, PRBool retry, void *arg) pphrase_arg_t *parg = (pphrase_arg_t *) arg; (void)slot; /* unused */ (void)retry; /* unused */ - if(parg->data->set.key_passwd) - return (char *)PORT_Strdup((char *)parg->data->set.key_passwd); + if(parg->data->set.str[STRING_KEY_PASSWD]) + return (char *)PORT_Strdup((char *)parg->data->set.str[STRING_KEY_PASSWD]); else return NULL; } @@ -488,10 +488,11 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) NULL) != SECSuccess) goto error; - if(data->set.cert) { + if(data->set.str[STRING_CERT]) { if(SSL_GetClientAuthDataHook(model, (SSLGetClientAuthData) SelectClientCert, - (void *)data->set.cert) != SECSuccess) { + (void *)data->set.str[STRING_CERT]) != + SECSuccess) { curlerr = CURLE_SSL_CERTPROBLEM; goto error; } -- cgit v1.2.1 From d994fcf2b1b22cf08073cec98b8deb7031758197 Mon Sep 17 00:00:00 2001 From: Patrick Monnerat Date: Fri, 24 Aug 2007 09:06:17 +0000 Subject: Remove leading space in curl_version_info ss_version field. --- lib/nss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index e90156e15..b61308309 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -601,6 +601,6 @@ ssize_t Curl_nss_recv(struct connectdata * conn, /* connection data */ size_t Curl_nss_version(char *buffer, size_t size) { - return snprintf(buffer, size, " NSS/%s", NSS_VERSION); + return snprintf(buffer, size, "NSS/%s", NSS_VERSION); } #endif /* USE_NSS */ -- cgit v1.2.1 From 9f44a95522162c0f4a61093efe1bf1f58b087358 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Thu, 30 Aug 2007 20:34:57 +0000 Subject: Renamed several libcurl error codes and options to make them more general and allow reuse by multiple protocols. Several unused error codes were removed. In all cases, macros were added to preserve source (and binary) compatibility with the old names. These macros are subject to removal at a future date, but probably not before 2009. An application can be tested to see if it is using any obsolete code by compiling it with the CURL_NO_OLDIES macro defined. Documented some newer error codes in libcurl-error(3) --- lib/nss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index b61308309..36facb1b3 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -550,7 +550,7 @@ int Curl_nss_send(struct connectdata *conn, /* connection data */ if(err == PR_IO_TIMEOUT_ERROR) { failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEOUTED; + return CURLE_OPERATION_TIMEDOUT; } failf(conn->data, "SSL write: error %d\n", err); @@ -591,7 +591,7 @@ ssize_t Curl_nss_recv(struct connectdata * conn, /* connection data */ } if(err == PR_IO_TIMEOUT_ERROR) { failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEOUTED; + return CURLE_OPERATION_TIMEDOUT; } failf(conn->data, "SSL read: errno %d", err); return -1; -- cgit v1.2.1 From 8c3f40ee320c419800b97f7ed385c43948970f61 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 18 Sep 2007 22:21:54 +0000 Subject: Rob Crittenden provided an NSS update with the following highlights: o It looks for the NSS database first in the environment variable SSL_DIR, then in /etc/pki/nssdb, then it initializes with no database if neither of those exist. o If the NSS PKCS#11 libnspsem.so driver is available then PEM files may be loaded, including the ca-bundle. If it is not available then only certificates already in the NSS database are used. o Tries to detect whether a file or nickname is being passed in so the right thing is done o Added a bit of code to make the output more like the OpenSSL module, including displaying the certificate information when connecting in verbose mode o Improved handling of certificate errors (expired, untrusted, etc) The libnsspem.so PKCS#11 module is currently only available in Fedora 8/rawhide. Work will be done soon to upstream it. The NSS module will work with or without it, all that changes is the source of the certificates and keys. --- lib/nss.c | 572 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 535 insertions(+), 37 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 36facb1b3..663234902 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -69,10 +70,14 @@ #define min(a, b) ((a) < (b) ? (a) : (b)) #endif +#define SSL_DIR "/etc/pki/nssdb" + +/* enough to fit the string "PEM Token #[0|1]" */ +#define SLOTSIZE 13 + PRFileDesc *PR_ImportTCPSocket(PRInt32 osfd); -static int initialized = 0; -static int noverify = 0; +int initialized = 0; #define HANDSHAKE_TIMEOUT 30 @@ -87,13 +92,17 @@ typedef struct { PRInt32 version; /* protocol version valid for this cipher */ } cipher_s; -/* the table itself is defined in nss_engine_init.c */ #ifdef NSS_ENABLE_ECC #define ciphernum 48 #else #define ciphernum 23 #endif +#define PK11_SETATTRS(x,id,v,l) (x)->type = (id); \ + (x)->pValue=(v); (x)->ulValueLen = (l) + +#define CERT_NewTempCertificate __CERT_NewTempCertificate + enum sslversion { SSL2 = 1, SSL3 = 2, TLS = 4 }; static const cipher_s cipherlist[ciphernum] = { @@ -154,6 +163,11 @@ static const cipher_s cipherlist[ciphernum] = { #endif }; +#ifdef HAVE_PK11_CREATEGENERICOBJECT +static const char* pem_library = "libnsspem.so"; +#endif +SECMODModule* mod = NULL; + static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model, char *cipher_list) { @@ -197,9 +211,7 @@ static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model, } if(found == PR_FALSE) { - char buf[1024]; - snprintf(buf, 1024, "Unknown cipher in list: %s", cipher); - failf(data, buf); + failf(data, "Unknown cipher in list: %s", cipher); return SECFailure; } @@ -220,23 +232,279 @@ static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model, return SECSuccess; } +/* + * Determine whether the nickname passed in is a filename that needs to + * be loaded as a PEM or a regular NSS nickname. + * + * returns 1 for a file + * returns 0 for not a file (NSS nickname) + */ +static int is_file(const char *filename) +{ + struct stat st; + + if(filename == NULL) + return 0; + + if(stat(filename, &st) == 0) + if(S_ISREG(st.st_mode)) + return 1; + + return 0; +} + +static int +nss_load_cert(const char *filename, PRBool cacert) +{ +#ifdef HAVE_PK11_CREATEGENERICOBJECT + CK_SLOT_ID slotID; + PK11SlotInfo * slot = NULL; + PK11GenericObject *rv; + CK_ATTRIBUTE *attrs; + CK_ATTRIBUTE theTemplate[20]; + CK_BBOOL cktrue = CK_TRUE; + CK_BBOOL ckfalse = CK_FALSE; + CK_OBJECT_CLASS objClass = CKO_CERTIFICATE; + char *slotname = NULL; +#endif + CERTCertificate *cert; + char *nickname = NULL; + char *n = NULL; + + /* If there is no slash in the filename it is assumed to be a regular + * NSS nickname. + */ + if(is_file(filename)) { + n = strrchr(filename, '/'); + if(n) + n++; + if(!mod) + return 1; + } + else { + /* A nickname from the NSS internal database */ + if (cacert) + return 0; /* You can't specify an NSS CA nickname this way */ + nickname = strdup(filename); + goto done; + } + +#ifdef HAVE_PK11_CREATEGENERICOBJECT + attrs = theTemplate; + + /* All CA and trust objects go into slot 0. Other slots are used + * for storing certificates. With each new user certificate we increment + * the slot count. We only support 1 user certificate right now. + */ + if (cacert) + slotID = 0; + else + slotID = 1; + + slotname = (char *)malloc(SLOTSIZE); + nickname = (char *)malloc(PATH_MAX); + snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID); + snprintf(nickname, PATH_MAX, "PEM Token #%ld:%s", slotID, n); + + slot = PK11_FindSlotByName(slotname); + + if (!slot) { + free(slotname); + free(nickname); + return 0; + } + + PK11_SETATTRS(attrs, CKA_CLASS, &objClass, sizeof(objClass) ); attrs++; + PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_LABEL, (unsigned char *)filename, + strlen(filename)+1); attrs++; + if (cacert) { + PK11_SETATTRS(attrs, CKA_TRUST, &cktrue, sizeof(CK_BBOOL) ); attrs++; + } + else { + PK11_SETATTRS(attrs, CKA_TRUST, &ckfalse, sizeof(CK_BBOOL) ); attrs++; + } + + /* This load the certificate in our PEM module into the appropriate + * slot. + */ + rv = PK11_CreateGenericObject(slot, theTemplate, 4, PR_FALSE /* isPerm */); + + PK11_FreeSlot(slot); + + free(slotname); + if(rv == NULL) { + free(nickname); + return 0; + } +#else + /* We don't have PK11_CreateGenericObject but a file-based cert was passed + * in. We need to fail. + */ + return 0; +#endif + +done: + /* Double-check that the certificate or nickname requested exists in + * either the token or the NSS certificate database. + */ + if (!cacert) { + cert = PK11_FindCertFromNickname((char *)nickname, NULL); + + /* An invalid nickname was passed in */ + if (cert == NULL) { + free(nickname); + PR_SetError(SEC_ERROR_UNKNOWN_CERT, 0); + return 0; + } + + CERT_DestroyCertificate(cert); + } + + free(nickname); + + return 1; +} + +static int nss_load_key(struct connectdata *conn, char *key_file) +{ +#ifdef HAVE_PK11_CREATEGENERICOBJECT + PK11SlotInfo * slot = NULL; + PK11GenericObject *rv; + CK_ATTRIBUTE *attrs; + CK_ATTRIBUTE theTemplate[20]; + CK_BBOOL cktrue = CK_TRUE; + CK_OBJECT_CLASS objClass = CKO_PRIVATE_KEY; + CK_SLOT_ID slotID; + char *slotname = NULL; + pphrase_arg_t *parg = NULL; + + attrs = theTemplate; + + /* FIXME: grok the various file types */ + + slotID = 1; /* hardcoded for now */ + + slotname = (char *)malloc(SLOTSIZE); + snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID); + + slot = PK11_FindSlotByName(slotname); + free(slotname); + + if(!slot) + return 0; + + PK11_SETATTRS(attrs, CKA_CLASS, &objClass, sizeof(objClass) ); attrs++; + PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_LABEL, (unsigned char *)key_file, + strlen(key_file)+1); attrs++; + + /* When adding an encrypted key the PKCS#11 will be set as removed */ + rv = PK11_CreateGenericObject(slot, theTemplate, 3, PR_FALSE /* isPerm */); + if(rv == NULL) { + PR_SetError(SEC_ERROR_BAD_KEY, 0); + return 0; + } + + /* This will force the token to be seen as re-inserted */ + SECMOD_WaitForAnyTokenEvent(mod, 0, 0); + PK11_IsPresent(slot); + + parg = (pphrase_arg_t *) malloc(sizeof(*parg)); + parg->retryCount = 0; + parg->data = conn->data; + /* parg is initialized in nss_Init_Tokens() */ + if(PK11_Authenticate(slot, PR_TRUE, parg) != SECSuccess) { + free(parg); + return 0; + } + free(parg); + + return 1; +#else + /* If we don't have PK11_CreateGenericObject then we can't load a file-based + * key. + */ + (void)conn; /* unused */ + (void)key_file; /* unused */ + return 0; +#endif +} + +static int display_error(struct connectdata *conn, PRInt32 err, + const char *filename) +{ + switch(err) { + case SEC_ERROR_BAD_PASSWORD: + failf(conn->data, "Unable to load client key: Incorrect password\n"); + return 1; + case SEC_ERROR_UNKNOWN_CERT: + failf(conn->data, "Unable to load certificate %s\n", filename); + return 1; + default: + break; + } + return 0; /* The caller will print a generic error */ +} + +static int cert_stuff(struct connectdata *conn, char *cert_file, char *key_file) +{ + struct SessionHandle *data = conn->data; + int rv = 0; + + if(cert_file) { + rv = nss_load_cert(cert_file, PR_FALSE); + if(!rv) { + if(!display_error(conn, PR_GetError(), cert_file)) + failf(data, "Unable to load client cert %d.", PR_GetError()); + return 0; + } + } + if(key_file || (is_file(cert_file))) { + if(key_file) + rv = nss_load_key(conn, key_file); + else + /* In case the cert file also has the key */ + rv = nss_load_key(conn, cert_file); + if(!rv) { + if(!display_error(conn, PR_GetError(), key_file)) + failf(data, "Unable to load client key %d.", PR_GetError()); + + return 0; + } + } + return 1; +} + static char * nss_get_password(PK11SlotInfo * slot, PRBool retry, void *arg) { - pphrase_arg_t *parg = (pphrase_arg_t *) arg; + pphrase_arg_t *parg; + parg = (pphrase_arg_t *) arg; + (void)slot; /* unused */ - (void)retry; /* unused */ + if(retry > 2) + return NULL; if(parg->data->set.str[STRING_KEY_PASSWD]) return (char *)PORT_Strdup((char *)parg->data->set.str[STRING_KEY_PASSWD]); else return NULL; } +/* No longer ask for the password, parg has been freed */ +static char * nss_no_password(PK11SlotInfo *slot, PRBool retry, void *arg) +{ + (void)slot; /* unused */ + (void)retry; /* unused */ + (void)arg; /* unused */ + return NULL; +} + static SECStatus nss_Init_Tokens(struct connectdata * conn) { PK11SlotList *slotList; PK11SlotListElement *listEntry; SECStatus ret, status = SECSuccess; - pphrase_arg_t *parg; + pphrase_arg_t *parg = NULL; parg = (pphrase_arg_t *) malloc(sizeof(*parg)); parg->retryCount = 0; @@ -265,6 +533,9 @@ static SECStatus nss_Init_Tokens(struct connectdata * conn) ret = PK11_Authenticate(slot, PR_TRUE, parg); if(SECSuccess != ret) { + if (PR_GetError() == SEC_ERROR_BAD_PASSWORD) + infof(conn->data, "The password for token '%s' is incorrect\n", + PK11_GetTokenName(slot)); status = SECFailure; break; } @@ -273,14 +544,61 @@ static SECStatus nss_Init_Tokens(struct connectdata * conn) } free(parg); + return status; } static SECStatus BadCertHandler(void *arg, PRFileDesc * socket) { SECStatus success = SECSuccess; - (void)arg; - (void)socket; + struct connectdata *conn = (struct connectdata *)arg; + PRErrorCode err = PR_GetError(); + CERTCertificate *cert = NULL; + char *subject, *issuer; + + if (conn->data->set.ssl.certverifyresult!=0) + return success; + + conn->data->set.ssl.certverifyresult=err; + cert = SSL_PeerCertificate(socket); + subject = CERT_NameToAscii(&cert->subject); + issuer = CERT_NameToAscii(&cert->issuer); + CERT_DestroyCertificate(cert); + + 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; + case SSL_ERROR_BAD_CERT_DOMAIN: + if (conn->data->set.ssl.verifypeer) + success = SECFailure; + infof(conn->data, "common name: %s (does not match '%s')\n", + subject, 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; + 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) + infof(conn->data, "SSL certificate verify ok.\n"); + PR_Free(subject); + PR_Free(issuer); return success; } @@ -295,6 +613,52 @@ static SECStatus HandshakeCallback(PRFileDesc * socket, void *arg) return SECSuccess; } +static void display_conn_info(struct connectdata *conn, PRFileDesc * socket) +{ + SSLChannelInfo channel; + SSLCipherSuiteInfo suite; + CERTCertificate *cert; + char *subject, *issuer, *common_name; + PRExplodedTime printableTime; + char timeString[256]; + PRTime notBefore, notAfter; + + if (SSL_GetChannelInfo(socket, &channel, sizeof channel) == + SECSuccess && channel.length == sizeof channel && + channel.cipherSuite) { + if (SSL_GetCipherSuiteInfo(channel.cipherSuite, + &suite, sizeof suite) == SECSuccess) { + infof(conn->data, "SSL connection using %s\n", suite.cipherSuiteName); + } + } + + infof(conn->data, "Server certificate:\n"); + + cert = SSL_PeerCertificate(socket); + subject = CERT_NameToAscii(&cert->subject); + issuer = CERT_NameToAscii(&cert->issuer); + common_name = CERT_GetCommonName(&cert->subject); + infof(conn->data, "\tsubject: %s\n", subject); + + CERT_GetCertTimes(cert, ¬Before, ¬After); + PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime); + PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); + infof(conn->data, "\tstart date: %s\n", timeString); + PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime); + PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); + infof(conn->data, "\texpire date: %s\n", timeString); + infof(conn->data, "\tcommon name: %s\n", common_name); + infof(conn->data, "\tissuer: %s\n", issuer); + + PR_Free(subject); + PR_Free(issuer); + PR_Free(common_name); + + CERT_DestroyCertificate(cert); + + return; +} + /** * * Callback to pick the SSL client certificate. @@ -309,18 +673,33 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc * socket, char *nickname = (char *)arg; void *proto_win = NULL; SECStatus secStatus = SECFailure; + PK11SlotInfo *slot; (void)caNames; proto_win = SSL_RevealPinArg(socket); + if (!nickname) + return secStatus; + cert = PK11_FindCertFromNickname(nickname, proto_win); if(cert) { - privKey = PK11_FindKeyByAnyCert(cert, proto_win); - if(privKey) { - secStatus = SECSuccess; + + if(!strncmp(nickname, "PEM Token", 9)) { + CK_SLOT_ID slotID = 1; /* hardcoded for now */ + char * slotname = (char *)malloc(SLOTSIZE); + snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID); + slot = PK11_FindSlotByName(slotname); + privKey = PK11_FindPrivateKeyFromCert(slot, cert, NULL); + PK11_FreeSlot(slot); + free(slotname); + if(privKey) { + secStatus = SECSuccess; + } } else { - CERT_DestroyCertificate(cert); + privKey = PK11_FindKeyByAnyCert(cert, proto_win); + if(privKey) + secStatus = SECSuccess; } } @@ -328,6 +707,10 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc * socket, *pRetCert = cert; *pRetKey = privKey; } + else { + if (cert) + CERT_DestroyCertificate(cert); + } return secStatus; } @@ -390,6 +773,10 @@ void Curl_nss_close(struct connectdata *conn, int sockindex) if(connssl->handle) { PR_Close(connssl->handle); + if(connssl->client_nickname != NULL) { + free(connssl->client_nickname); + connssl->client_nickname = NULL; + } connssl->handle = NULL; } } @@ -413,31 +800,59 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; SECStatus rv; - int curlerr = CURLE_SSL_CONNECT_ERROR; + char *configstring = NULL; + char *certDir = NULL; + int curlerr; + + curlerr = CURLE_SSL_CONNECT_ERROR; /* FIXME. NSS doesn't support multiple databases open at the same time. */ if(!initialized) { - if(!data->set.ssl.CAfile) { - if(data->set.ssl.verifypeer) { - failf(data, "No NSS cacert database specified."); - return CURLE_SSL_CACERT_BADFILE; - } - else { - rv = NSS_NoDB_Init(NULL); - noverify = 1; - } + initialized = 1; + + certDir = getenv("SSL_DIR"); /* Look in $SSL_DIR */ + + if (!certDir) { + struct stat st; + + if (stat(SSL_DIR, &st) == 0) + if (S_ISDIR(st.st_mode)) { + certDir = (char *)SSL_DIR; + } + } + + if(!certDir) { + rv = NSS_NoDB_Init(NULL); } else { - rv = NSS_Initialize(data->set.ssl.CAfile, NULL, NULL, "secmod.db", + rv = NSS_Initialize(certDir, NULL, NULL, "secmod.db", NSS_INIT_READONLY); } if(rv != SECSuccess) { + infof(conn->data, "Unable to initialize NSS database\n"); curlerr = CURLE_SSL_CACERT_BADFILE; goto error; } - } - NSS_SetDomesticPolicy(); + NSS_SetDomesticPolicy(); + +#ifdef HAVE_PK11_CREATEGENERICOBJECT + configstring = (char *)malloc(PATH_MAX); + + PR_snprintf(configstring, PATH_MAX, "library=%s name=PEM", pem_library); + + mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); + free(configstring); + if (!mod || !mod->loaded) { + if (mod) { + SECMOD_DestroyModule(mod); + mod = NULL; + } + infof(data, "WARNING: failed to load NSS PEM library %s. Using OpenSSL " + "PEM certificates will not work.\n", pem_library); + } +#endif + } model = PR_NewTCPSocket(); if(!model) @@ -477,34 +892,112 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) goto error; if(data->set.ssl.cipher_list) { - if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) + if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) { + curlerr = CURLE_SSL_CIPHER; goto error; + } } - if(SSL_BadCertHook(model, (SSLBadCertHandler) BadCertHandler, NULL) - != SECSuccess) + data->set.ssl.certverifyresult=0; /* not checked yet */ + if(SSL_BadCertHook(model, (SSLBadCertHandler) BadCertHandler, conn) + != SECSuccess) { goto error; + } if(SSL_HandshakeCallback(model, (SSLHandshakeCallback) HandshakeCallback, NULL) != SECSuccess) goto error; + if (data->set.ssl.CAfile) { + rv = nss_load_cert(data->set.ssl.CAfile, PR_TRUE); + if (!rv) { + curlerr = CURLE_SSL_CACERT_BADFILE; + goto error; + } + } + else if (data->set.ssl.CApath) { + struct stat st; + PRDir *dir; + PRDirEntry *entry; + + if (stat(data->set.ssl.CApath, &st) == -1) { + curlerr = CURLE_SSL_CACERT_BADFILE; + goto error; + } + + if (S_ISDIR(st.st_mode)) { + int rv; + + dir = PR_OpenDir(data->set.ssl.CApath); + do { + entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN); + + if (entry) { + char fullpath[PATH_MAX]; + + snprintf(fullpath, sizeof(fullpath), "%s/%s", data->set.ssl.CApath, + entry->name); + rv = nss_load_cert(fullpath, PR_TRUE); + } + /* This is purposefully tolerant of errors so non-PEM files + * can be in the same directory */ + } while (entry != NULL); + PR_CloseDir(dir); + } + } + infof(data, + " CAfile: %s\n" + " CApath: %s\n", + data->set.ssl.CAfile ? data->set.ssl.CAfile : "none", + data->set.ssl.CApath ? data->set.ssl.CApath : "none"); + if(data->set.str[STRING_CERT]) { + char * n; + char * nickname; + + nickname = (char *)malloc(PATH_MAX); + if(is_file(data->set.str[STRING_CERT])) { + n = strrchr(data->set.str[STRING_CERT], '/'); + if (n) { + n++; /* skip last slash */ + snprintf(nickname, PATH_MAX, "PEM Token #%ld:%s", 1, n); + } + } + else { + strncpy(nickname, data->set.str[STRING_CERT], PATH_MAX); + } + if(nss_Init_Tokens(conn) != SECSuccess) { + free(nickname); + goto error; + } + if (!cert_stuff(conn, data->set.str[STRING_CERT], + data->set.str[STRING_KEY])) { + /* failf() is already done in cert_stuff() */ + free(nickname); + return CURLE_SSL_CERTPROBLEM; + } + + connssl->client_nickname = strdup(nickname); if(SSL_GetClientAuthDataHook(model, (SSLGetClientAuthData) SelectClientCert, - (void *)data->set.str[STRING_CERT]) != - SECSuccess) { + (void *)connssl->client_nickname) != + SECSuccess) { curlerr = CURLE_SSL_CERTPROBLEM; goto error; } - if(nss_Init_Tokens(conn) != SECSuccess) - goto error; + + free(nickname); + + PK11_SetPasswordFunc(nss_no_password); } + else + connssl->client_nickname = NULL; /* Import our model socket onto the existing file descriptor */ connssl->handle = PR_ImportTCPSocket(sockfd); connssl->handle = SSL_ImportFD(model, connssl->handle); if(!connssl->handle) goto error; + PR_Close(model); /* We don't need this any more */ /* Force handshake on next I/O */ SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE); @@ -514,14 +1007,19 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) /* Force the handshake now */ if (SSL_ForceHandshakeWithTimeout(connssl->handle, PR_SecondsToInterval(HANDSHAKE_TIMEOUT)) - != SECSuccess) + != SECSuccess) { + if (conn->data->set.ssl.certverifyresult!=0) + curlerr = CURLE_SSL_CACERT; goto error; + } + + display_conn_info(conn, connssl->handle); return CURLE_OK; error: err = PR_GetError(); - failf(data, "NSS error %d", err); + infof(data, "NSS error %d\n", err); if(model) PR_Close(model); return curlerr; -- cgit v1.2.1 From 4b96ac504cf0a717958f48ef568d933a7553b546 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 25 Oct 2007 20:54:46 +0000 Subject: prevent compiler warnings about shadowing and one case of unused variable --- lib/nss.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 663234902..8429ed885 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -548,7 +548,7 @@ static SECStatus nss_Init_Tokens(struct connectdata * conn) return status; } -static SECStatus BadCertHandler(void *arg, PRFileDesc * socket) +static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) { SECStatus success = SECSuccess; struct connectdata *conn = (struct connectdata *)arg; @@ -560,7 +560,7 @@ static SECStatus BadCertHandler(void *arg, PRFileDesc * socket) return success; conn->data->set.ssl.certverifyresult=err; - cert = SSL_PeerCertificate(socket); + cert = SSL_PeerCertificate(sock); subject = CERT_NameToAscii(&cert->subject); issuer = CERT_NameToAscii(&cert->issuer); CERT_DestroyCertificate(cert); @@ -606,14 +606,14 @@ static SECStatus BadCertHandler(void *arg, PRFileDesc * socket) /** * Inform the application that the handshake is complete. */ -static SECStatus HandshakeCallback(PRFileDesc * socket, void *arg) +static SECStatus HandshakeCallback(PRFileDesc *sock, void *arg) { - (void)socket; + (void)sock; (void)arg; return SECSuccess; } -static void display_conn_info(struct connectdata *conn, PRFileDesc * socket) +static void display_conn_info(struct connectdata *conn, PRFileDesc *sock) { SSLChannelInfo channel; SSLCipherSuiteInfo suite; @@ -623,7 +623,7 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc * socket) char timeString[256]; PRTime notBefore, notAfter; - if (SSL_GetChannelInfo(socket, &channel, sizeof channel) == + if (SSL_GetChannelInfo(sock, &channel, sizeof channel) == SECSuccess && channel.length == sizeof channel && channel.cipherSuite) { if (SSL_GetCipherSuiteInfo(channel.cipherSuite, @@ -634,7 +634,7 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc * socket) infof(conn->data, "Server certificate:\n"); - cert = SSL_PeerCertificate(socket); + cert = SSL_PeerCertificate(sock); subject = CERT_NameToAscii(&cert->subject); issuer = CERT_NameToAscii(&cert->issuer); common_name = CERT_GetCommonName(&cert->subject); @@ -663,10 +663,10 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc * socket) * * Callback to pick the SSL client certificate. */ -static SECStatus SelectClientCert(void *arg, PRFileDesc * socket, - struct CERTDistNamesStr * caNames, - struct CERTCertificateStr ** pRetCert, - struct SECKEYPrivateKeyStr ** pRetKey) +static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, + struct CERTDistNamesStr *caNames, + struct CERTCertificateStr **pRetCert, + struct SECKEYPrivateKeyStr **pRetKey) { CERTCertificate *cert; SECKEYPrivateKey *privKey; @@ -676,7 +676,7 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc * socket, PK11SlotInfo *slot; (void)caNames; - proto_win = SSL_RevealPinArg(socket); + proto_win = SSL_RevealPinArg(sock); if (!nickname) return secStatus; @@ -800,7 +800,9 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; SECStatus rv; +#ifdef HAVE_PK11_CREATEGENERICOBJECT char *configstring = NULL; +#endif char *certDir = NULL; int curlerr; @@ -925,7 +927,7 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) } if (S_ISDIR(st.st_mode)) { - int rv; + int rc; dir = PR_OpenDir(data->set.ssl.CApath); do { @@ -936,7 +938,8 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) snprintf(fullpath, sizeof(fullpath), "%s/%s", data->set.ssl.CApath, entry->name); - rv = nss_load_cert(fullpath, PR_TRUE); + rc = nss_load_cert(fullpath, PR_TRUE); + /* FIXME: check this return value! */ } /* This is purposefully tolerant of errors so non-PEM files * can be in the same directory */ -- cgit v1.2.1 From 6a17cae4f66fbf4b68b44cc95ae5ab772386ec54 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 25 Oct 2007 21:08:55 +0000 Subject: Made libcurl built with NSS possible to ignore the peer verification. Previously it would fail if the ca bundle wasn't present, even if the code ignored the verification results. --- lib/nss.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 8429ed885..52a25def3 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -909,9 +909,12 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) NULL) != SECSuccess) goto error; - if (data->set.ssl.CAfile) { - rv = nss_load_cert(data->set.ssl.CAfile, PR_TRUE); - if (!rv) { + if(!data->set.ssl.verifypeer) + /* skip the verifying of the peer */ + ; + else if (data->set.ssl.CAfile) { + int rc = nss_load_cert(data->set.ssl.CAfile, PR_TRUE); + if (!rc) { curlerr = CURLE_SSL_CACERT_BADFILE; goto error; } @@ -954,8 +957,8 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) data->set.ssl.CApath ? data->set.ssl.CApath : "none"); if(data->set.str[STRING_CERT]) { - char * n; - char * nickname; + char *n; + char *nickname; nickname = (char *)malloc(PATH_MAX); if(is_file(data->set.str[STRING_CERT])) { @@ -973,7 +976,7 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) goto error; } if (!cert_stuff(conn, data->set.str[STRING_CERT], - data->set.str[STRING_KEY])) { + data->set.str[STRING_KEY])) { /* failf() is already done in cert_stuff() */ free(nickname); return CURLE_SSL_CERTPROBLEM; @@ -983,7 +986,7 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) if(SSL_GetClientAuthDataHook(model, (SSLGetClientAuthData) SelectClientCert, (void *)connssl->client_nickname) != - SECSuccess) { + SECSuccess) { curlerr = CURLE_SSL_CERTPROBLEM; goto error; } -- cgit v1.2.1 From ad6e28073c985a42e8b15d2234baa7ef67ffcb35 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 5 Nov 2007 09:45:09 +0000 Subject: removed space after if and while before the parenthesis for better source code consistency --- lib/nss.c | 68 +++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 34 insertions(+), 34 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 52a25def3..dcf955b15 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -283,7 +283,7 @@ nss_load_cert(const char *filename, PRBool cacert) } else { /* A nickname from the NSS internal database */ - if (cacert) + if(cacert) return 0; /* You can't specify an NSS CA nickname this way */ nickname = strdup(filename); goto done; @@ -296,7 +296,7 @@ nss_load_cert(const char *filename, PRBool cacert) * for storing certificates. With each new user certificate we increment * the slot count. We only support 1 user certificate right now. */ - if (cacert) + if(cacert) slotID = 0; else slotID = 1; @@ -308,7 +308,7 @@ nss_load_cert(const char *filename, PRBool cacert) slot = PK11_FindSlotByName(slotname); - if (!slot) { + if(!slot) { free(slotname); free(nickname); return 0; @@ -318,7 +318,7 @@ nss_load_cert(const char *filename, PRBool cacert) PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL) ); attrs++; PK11_SETATTRS(attrs, CKA_LABEL, (unsigned char *)filename, strlen(filename)+1); attrs++; - if (cacert) { + if(cacert) { PK11_SETATTRS(attrs, CKA_TRUST, &cktrue, sizeof(CK_BBOOL) ); attrs++; } else { @@ -348,11 +348,11 @@ done: /* Double-check that the certificate or nickname requested exists in * either the token or the NSS certificate database. */ - if (!cacert) { + if(!cacert) { cert = PK11_FindCertFromNickname((char *)nickname, NULL); /* An invalid nickname was passed in */ - if (cert == NULL) { + if(cert == NULL) { free(nickname); PR_SetError(SEC_ERROR_UNKNOWN_CERT, 0); return 0; @@ -533,7 +533,7 @@ static SECStatus nss_Init_Tokens(struct connectdata * conn) ret = PK11_Authenticate(slot, PR_TRUE, parg); if(SECSuccess != ret) { - if (PR_GetError() == SEC_ERROR_BAD_PASSWORD) + if(PR_GetError() == SEC_ERROR_BAD_PASSWORD) infof(conn->data, "The password for token '%s' is incorrect\n", PK11_GetTokenName(slot)); status = SECFailure; @@ -556,7 +556,7 @@ static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) CERTCertificate *cert = NULL; char *subject, *issuer; - if (conn->data->set.ssl.certverifyresult!=0) + if(conn->data->set.ssl.certverifyresult!=0) return success; conn->data->set.ssl.certverifyresult=err; @@ -568,34 +568,34 @@ 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) + if(conn->data->set.ssl.verifypeer) success = SECFailure; break; case SEC_ERROR_UNTRUSTED_ISSUER: - if (conn->data->set.ssl.verifypeer) + if(conn->data->set.ssl.verifypeer) success = SECFailure; infof(conn->data, "Certificate is signed by an untrusted issuer: '%s'\n", issuer); break; case SSL_ERROR_BAD_CERT_DOMAIN: - if (conn->data->set.ssl.verifypeer) + if(conn->data->set.ssl.verifypeer) success = SECFailure; infof(conn->data, "common name: %s (does not match '%s')\n", subject, conn->host.dispname); break; case SEC_ERROR_EXPIRED_CERTIFICATE: - if (conn->data->set.ssl.verifypeer) + if(conn->data->set.ssl.verifypeer) success = SECFailure; infof(conn->data, "Remote Certificate has expired.\n"); break; default: - if (conn->data->set.ssl.verifypeer) + 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(success == SECSuccess) infof(conn->data, "SSL certificate verify ok.\n"); PR_Free(subject); PR_Free(issuer); @@ -623,10 +623,10 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc *sock) char timeString[256]; PRTime notBefore, notAfter; - if (SSL_GetChannelInfo(sock, &channel, sizeof channel) == + if(SSL_GetChannelInfo(sock, &channel, sizeof channel) == SECSuccess && channel.length == sizeof channel && channel.cipherSuite) { - if (SSL_GetCipherSuiteInfo(channel.cipherSuite, + if(SSL_GetCipherSuiteInfo(channel.cipherSuite, &suite, sizeof suite) == SECSuccess) { infof(conn->data, "SSL connection using %s\n", suite.cipherSuiteName); } @@ -678,7 +678,7 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, proto_win = SSL_RevealPinArg(sock); - if (!nickname) + if(!nickname) return secStatus; cert = PK11_FindCertFromNickname(nickname, proto_win); @@ -708,7 +708,7 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, *pRetKey = privKey; } else { - if (cert) + if(cert) CERT_DestroyCertificate(cert); } @@ -814,11 +814,11 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) certDir = getenv("SSL_DIR"); /* Look in $SSL_DIR */ - if (!certDir) { + if(!certDir) { struct stat st; - if (stat(SSL_DIR, &st) == 0) - if (S_ISDIR(st.st_mode)) { + if(stat(SSL_DIR, &st) == 0) + if(S_ISDIR(st.st_mode)) { certDir = (char *)SSL_DIR; } } @@ -845,8 +845,8 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); free(configstring); - if (!mod || !mod->loaded) { - if (mod) { + if(!mod || !mod->loaded) { + if(mod) { SECMOD_DestroyModule(mod); mod = NULL; } @@ -912,31 +912,31 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) if(!data->set.ssl.verifypeer) /* skip the verifying of the peer */ ; - else if (data->set.ssl.CAfile) { + else if(data->set.ssl.CAfile) { int rc = nss_load_cert(data->set.ssl.CAfile, PR_TRUE); - if (!rc) { + if(!rc) { curlerr = CURLE_SSL_CACERT_BADFILE; goto error; } } - else if (data->set.ssl.CApath) { + else if(data->set.ssl.CApath) { struct stat st; PRDir *dir; PRDirEntry *entry; - if (stat(data->set.ssl.CApath, &st) == -1) { + if(stat(data->set.ssl.CApath, &st) == -1) { curlerr = CURLE_SSL_CACERT_BADFILE; goto error; } - if (S_ISDIR(st.st_mode)) { + if(S_ISDIR(st.st_mode)) { int rc; dir = PR_OpenDir(data->set.ssl.CApath); do { entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN); - if (entry) { + if(entry) { char fullpath[PATH_MAX]; snprintf(fullpath, sizeof(fullpath), "%s/%s", data->set.ssl.CApath, @@ -946,7 +946,7 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) } /* This is purposefully tolerant of errors so non-PEM files * can be in the same directory */ - } while (entry != NULL); + } while(entry != NULL); PR_CloseDir(dir); } } @@ -963,7 +963,7 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) nickname = (char *)malloc(PATH_MAX); if(is_file(data->set.str[STRING_CERT])) { n = strrchr(data->set.str[STRING_CERT], '/'); - if (n) { + if(n) { n++; /* skip last slash */ snprintf(nickname, PATH_MAX, "PEM Token #%ld:%s", 1, n); } @@ -975,7 +975,7 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) free(nickname); goto error; } - if (!cert_stuff(conn, data->set.str[STRING_CERT], + if(!cert_stuff(conn, data->set.str[STRING_CERT], data->set.str[STRING_KEY])) { /* failf() is already done in cert_stuff() */ free(nickname); @@ -1011,10 +1011,10 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) SSL_SetURL(connssl->handle, conn->host.name); /* Force the handshake now */ - if (SSL_ForceHandshakeWithTimeout(connssl->handle, + if(SSL_ForceHandshakeWithTimeout(connssl->handle, PR_SecondsToInterval(HANDSHAKE_TIMEOUT)) != SECSuccess) { - if (conn->data->set.ssl.certverifyresult!=0) + if(conn->data->set.ssl.certverifyresult!=0) curlerr = CURLE_SSL_CACERT; goto error; } -- cgit v1.2.1 From ed6466d17603aa39bb4b1685544747f4c64ec7b0 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 15 Jan 2008 23:19:02 +0000 Subject: Calls to Curl_failf() are not supposed to provide a trailing newline as the function itself adds that. Fixed on 50 or something strings! --- lib/nss.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index dcf955b15..b8f2ddd5c 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2007, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -436,10 +436,10 @@ static int display_error(struct connectdata *conn, PRInt32 err, { switch(err) { case SEC_ERROR_BAD_PASSWORD: - failf(conn->data, "Unable to load client key: Incorrect password\n"); + failf(conn->data, "Unable to load client key: Incorrect password"); return 1; case SEC_ERROR_UNKNOWN_CERT: - failf(conn->data, "Unable to load certificate %s\n", filename); + failf(conn->data, "Unable to load certificate %s", filename); return 1; default: break; @@ -521,10 +521,10 @@ static SECStatus nss_Init_Tokens(struct connectdata * conn) if(PK11_NeedLogin(slot) && PK11_NeedUserInit(slot)) { if(slot == PK11_GetInternalKeySlot()) { - failf(conn->data, "The NSS database has not been initialized.\n"); + failf(conn->data, "The NSS database has not been initialized"); } else { - failf(conn->data, "The token %s has not been initialized.", + failf(conn->data, "The token %s has not been initialized", PK11_GetTokenName(slot)); } PK11_FreeSlot(slot); @@ -1057,7 +1057,7 @@ int Curl_nss_send(struct connectdata *conn, /* connection data */ return CURLE_OPERATION_TIMEDOUT; } - failf(conn->data, "SSL write: error %d\n", err); + failf(conn->data, "SSL write: error %d", err); return -1; } return rc; /* number of bytes */ -- cgit v1.2.1 From f9a60620818b6a19ebe3e6f15e1b57d7012e6fb0 Mon Sep 17 00:00:00 2001 From: Gunter Knauf Date: Tue, 19 Feb 2008 23:10:07 +0000 Subject: applied patch to disable SSLv2 by default; discussion: http://sourceforge.net/tracker/index.php?func=detail&aid=1767276&group_id=976&atid=350976 Submitted by Kaspar Brand. --- lib/nss.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index b8f2ddd5c..6e3ee8604 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -873,7 +873,7 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) switch (data->set.ssl.version) { default: case CURL_SSLVERSION_DEFAULT: - ssl2 = ssl3 = tlsv1 = PR_TRUE; + ssl3 = tlsv1 = PR_TRUE; break; case CURL_SSLVERSION_TLSv1: tlsv1 = PR_TRUE; @@ -893,6 +893,9 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) if(SSL_OptionSet(model, SSL_ENABLE_TLS, tlsv1) != SECSuccess) goto error; + if(SSL_OptionSet(model, SSL_V2_COMPATIBLE_HELLO, ssl2) != SECSuccess) + goto error; + if(data->set.ssl.cipher_list) { if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) { curlerr = CURLE_SSL_CIPHER; -- cgit v1.2.1 From 53a549000c3634f6b0a5ed262d5834c3145885d7 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 20 Feb 2008 09:56:26 +0000 Subject: - Based on initial work done by Gautam Kachroo to address a bug, we now keep better control at the exact state of the connection's SSL status so that we know exactly when it has completed the SSL negotiation or not so that there won't be accidental re-uses of connections that are wrongly believed to be in SSL-completed-negotiate state. --- lib/nss.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 6e3ee8604..c8d728d3e 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1022,6 +1022,8 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) goto error; } + connssl->state = ssl_connection_complete; + display_conn_info(conn, connssl->handle); return CURLE_OK; -- cgit v1.2.1 From d6f8f1606850c8d814dec0dd65818960af88bbca Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Mon, 26 May 2008 01:59:00 +0000 Subject: fix: preprocessor complaining about macro redefinition --- lib/nss.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index c8d728d3e..20f8770d2 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -66,10 +66,6 @@ /* The last #include file should be: */ #include "memdebug.h" -#ifndef min -#define min(a, b) ((a) < (b) ? (a) : (b)) -#endif - #define SSL_DIR "/etc/pki/nssdb" /* enough to fit the string "PEM Token #[0|1]" */ -- cgit v1.2.1 From 3fe8251dfbb533803e25cc38365114b28c5a1c85 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 6 Jun 2008 18:40:21 +0000 Subject: - Axel Tillequin and Arnaud Ebalard added support for CURLOPT_CRLFILE, for OpenSSL, NSS and GnuTLS-built libcurls. --- lib/nss.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 20f8770d2..a5fc795f8 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -59,6 +59,9 @@ #include #include #include +#include +#include +#include #include "memory.h" #include "easyif.h" /* for Curl_convert_from_utf8 prototype */ @@ -362,6 +365,69 @@ done: return 1; } +static int nss_load_crl(char* crlfilename, PRBool ascii) +{ + PRFileDesc *infile; + PRStatus prstat; + PRFileInfo info; + PRInt32 nb; + int rv; + SECItem crlDER; + CERTSignedCrl *crl=NULL; + PK11SlotInfo *slot=NULL; + + infile = PR_Open(crlfilename,PR_RDONLY,0); + if (!infile) { + return 0; + } + crlDER.data = NULL; + prstat = PR_GetOpenFileInfo(infile,&info); + if (prstat!=PR_SUCCESS) return 0; + if (ascii) { + SECItem filedata; + char *asc,*body; + filedata.data = NULL; + if (!SECITEM_AllocItem(NULL,&filedata,info.size)) return 0; + nb = PR_Read(infile,filedata.data,info.size); + if (nb!=info.size) return 0; + asc = (char*)filedata.data; + if (!asc) { + return 0; + } + if ((body=strstr(asc,"-----BEGIN")) != NULL) { + char *trailer=NULL; + asc = body; + body = PORT_Strchr(asc,'\n'); + if (!body) body = PORT_Strchr(asc,'\r'); + if (body) trailer = strstr(++body,"-----END"); + if (trailer!=NULL) *trailer='\0'; + else return 0; + } + else { + body = asc; + } + rv = ATOB_ConvertAsciiToItem(&crlDER,body); + PORT_Free(filedata.data); + if (rv) return 0; + } + else { + if (!SECITEM_AllocItem(NULL,&crlDER,info.size)) return 0; + nb = PR_Read(infile,crlDER.data,info.size); + if (nb!=info.size) return 0; + } + + slot = PK11_GetInternalKeySlot(); + crl = PK11_ImportCRL(slot,&crlDER, + NULL,SEC_CRL_TYPE, + NULL,CRL_IMPORT_DEFAULT_OPTIONS, + NULL,(CRL_DECODE_DEFAULT_OPTIONS| + CRL_DECODE_DONT_COPY_DER)); + if (slot) PK11_FreeSlot(slot); + if (!crl) return 0; + SEC_DestroyCrl(crl); + return 1; +} + static int nss_load_key(struct connectdata *conn, char *key_file) { #ifdef HAVE_PK11_CREATEGENERICOBJECT @@ -955,6 +1021,17 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) data->set.ssl.CAfile ? data->set.ssl.CAfile : "none", data->set.ssl.CApath ? data->set.ssl.CApath : "none"); + if (data->set.ssl.CRLfile) { + int rc = nss_load_crl(data->set.ssl.CRLfile, PR_FALSE); + if (!rc) { + curlerr = CURLE_SSL_CRL_BADFILE; + goto error; + } + infof(data, + " CRLfile: %s\n", + data->set.ssl.CRLfile ? data->set.ssl.CRLfile : "none"); + } + if(data->set.str[STRING_CERT]) { char *n; char *nickname; -- cgit v1.2.1 From 621c2b901527248b4822895bc0305373a7d2dd63 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 6 Jun 2008 20:52:32 +0000 Subject: - Axel Tillequin and Arnaud Ebalard added support for CURLOPT_ISSUERCERT, for OpenSSL, NSS and GnuTLS-built libcurls. --- lib/nss.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index a5fc795f8..5e01c4448 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -721,6 +721,43 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc *sock) return; } +/** + * + * Check that the Peer certificate's issuer certificate matches the one found + * by issuer_nickname. This is not exactly the way OpenSSL and GNU TLS do the + * issuer check, so we provide comments that mimic the OpenSSL + * X509_check_issued function (in x509v3/v3_purp.c) + */ +static SECStatus check_issuer_cert(struct connectdata *conn, PRFileDesc *sock, char* issuer_nickname) +{ + CERTCertificate *cert,*cert_issuer,*issuer; + SECStatus res=SECSuccess; + void *proto_win = NULL; + + /* + PRArenaPool *tmpArena = NULL; + CERTAuthKeyID *authorityKeyID = NULL; + SECITEM *caname = NULL; + */ + + cert = SSL_PeerCertificate(sock); + cert_issuer = CERT_FindCertIssuer(cert,PR_Now(),certUsageObjectSigner); + + proto_win = SSL_RevealPinArg(sock); + issuer = NULL; + issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win); + + if ((!cert_issuer) || (!issuer)) + res = SECFailure; + else if (CERT_CompareCerts(cert_issuer,issuer)==PR_FALSE) + res = SECFailure; + + CERT_DestroyCertificate(cert); + CERT_DestroyCertificate(issuer); + CERT_DestroyCertificate(cert_issuer); + return res; +} + /** * * Callback to pick the SSL client certificate. @@ -853,7 +890,7 @@ int Curl_nss_close_all(struct SessionHandle *data) return 0; } -CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) +CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) { PRInt32 err; PRFileDesc *model = NULL; @@ -1046,6 +1083,7 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) } else { strncpy(nickname, data->set.str[STRING_CERT], PATH_MAX); + nickname[PATH_MAX-1]=0; /* make sure this is zero terminated */ } if(nss_Init_Tokens(conn) != SECSuccess) { free(nickname); @@ -1061,7 +1099,7 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) connssl->client_nickname = strdup(nickname); if(SSL_GetClientAuthDataHook(model, (SSLGetClientAuthData) SelectClientCert, - (void *)connssl->client_nickname) != + (void *)connssl) != SECSuccess) { curlerr = CURLE_SSL_CERTPROBLEM; goto error; @@ -1074,6 +1112,7 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) else connssl->client_nickname = NULL; + /* Import our model socket onto the existing file descriptor */ connssl->handle = PR_ImportTCPSocket(sockfd); connssl->handle = SSL_ImportFD(model, connssl->handle); @@ -1099,6 +1138,32 @@ CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex) display_conn_info(conn, connssl->handle); + if (data->set.str[STRING_SSL_ISSUERCERT]) { + char *n; + char *nickname; + nickname = (char *)malloc(PATH_MAX); + if(is_file(data->set.str[STRING_SSL_ISSUERCERT])) { + n = strrchr(data->set.str[STRING_SSL_ISSUERCERT], '/'); + if (n) { + n++; /* skip last slash */ + snprintf(nickname, PATH_MAX, "PEM Token #%ld:%s", 1, n); + } + } + else { + strncpy(nickname, data->set.str[STRING_SSL_ISSUERCERT], PATH_MAX); + nickname[PATH_MAX-1]=0; /* make sure this is zero terminated */ + } + if (check_issuer_cert(conn,connssl->handle,nickname)==SECFailure) { + infof(data,"SSL certificate issuer check failed\n"); + free(nickname); + curlerr = CURLE_SSL_ISSUER_ERROR; + goto error; + } + else { + infof("SSL certificate issuer check ok\n"); + } + } + return CURLE_OK; error: -- cgit v1.2.1 From a2e45a2211cd4e120460f9e9befcc00dad201d7d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 6 Jun 2008 20:57:32 +0000 Subject: code style cleanup --- lib/nss.c | 64 +++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 24 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 5e01c4448..97797842d 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -313,16 +313,20 @@ nss_load_cert(const char *filename, PRBool cacert) return 0; } - PK11_SETATTRS(attrs, CKA_CLASS, &objClass, sizeof(objClass) ); attrs++; - PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_CLASS, &objClass, sizeof(objClass) ); + attrs++; + PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL) ); + attrs++; PK11_SETATTRS(attrs, CKA_LABEL, (unsigned char *)filename, - strlen(filename)+1); attrs++; + strlen(filename)+1); + attrs++; if(cacert) { - PK11_SETATTRS(attrs, CKA_TRUST, &cktrue, sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_TRUST, &cktrue, sizeof(CK_BBOOL) ); } else { - PK11_SETATTRS(attrs, CKA_TRUST, &ckfalse, sizeof(CK_BBOOL) ); attrs++; + PK11_SETATTRS(attrs, CKA_TRUST, &ckfalse, sizeof(CK_BBOOL) ); } + attrs++; /* This load the certificate in our PEM module into the appropriate * slot. @@ -382,38 +386,49 @@ static int nss_load_crl(char* crlfilename, PRBool ascii) } crlDER.data = NULL; prstat = PR_GetOpenFileInfo(infile,&info); - if (prstat!=PR_SUCCESS) return 0; + if (prstat!=PR_SUCCESS) + return 0; if (ascii) { SECItem filedata; char *asc,*body; filedata.data = NULL; - if (!SECITEM_AllocItem(NULL,&filedata,info.size)) return 0; + if (!SECITEM_AllocItem(NULL,&filedata,info.size)) + return 0; nb = PR_Read(infile,filedata.data,info.size); - if (nb!=info.size) return 0; + if (nb!=info.size) + return 0; asc = (char*)filedata.data; - if (!asc) { + if (!asc) return 0; - } - if ((body=strstr(asc,"-----BEGIN")) != NULL) { + + body=strstr(asc,"-----BEGIN"); + if (body != NULL) { char *trailer=NULL; asc = body; body = PORT_Strchr(asc,'\n'); - if (!body) body = PORT_Strchr(asc,'\r'); - if (body) trailer = strstr(++body,"-----END"); - if (trailer!=NULL) *trailer='\0'; - else return 0; + if (!body) + body = PORT_Strchr(asc,'\r'); + if (body) + trailer = strstr(++body,"-----END"); + if (trailer!=NULL) + *trailer='\0'; + else + return 0; } else { body = asc; } rv = ATOB_ConvertAsciiToItem(&crlDER,body); PORT_Free(filedata.data); - if (rv) return 0; + if (rv) + return 0; } else { - if (!SECITEM_AllocItem(NULL,&crlDER,info.size)) return 0; + if (!SECITEM_AllocItem(NULL,&crlDER,info.size)) + return 0; nb = PR_Read(infile,crlDER.data,info.size); - if (nb!=info.size) return 0; + if (nb!=info.size) + return 0; } slot = PK11_GetInternalKeySlot(); @@ -686,10 +701,10 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc *sock) PRTime notBefore, notAfter; if(SSL_GetChannelInfo(sock, &channel, sizeof channel) == - SECSuccess && channel.length == sizeof channel && - channel.cipherSuite) { + SECSuccess && channel.length == sizeof channel && + channel.cipherSuite) { if(SSL_GetCipherSuiteInfo(channel.cipherSuite, - &suite, sizeof suite) == SECSuccess) { + &suite, sizeof suite) == SECSuccess) { infof(conn->data, "SSL connection using %s\n", suite.cipherSuiteName); } } @@ -728,7 +743,8 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc *sock) * issuer check, so we provide comments that mimic the OpenSSL * X509_check_issued function (in x509v3/v3_purp.c) */ -static SECStatus check_issuer_cert(struct connectdata *conn, PRFileDesc *sock, char* issuer_nickname) +static SECStatus check_issuer_cert(struct connectdata *conn, PRFileDesc *sock, + char* issuer_nickname) { CERTCertificate *cert,*cert_issuer,*issuer; SECStatus res=SECSuccess; @@ -1046,8 +1062,8 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) rc = nss_load_cert(fullpath, PR_TRUE); /* FIXME: check this return value! */ } - /* This is purposefully tolerant of errors so non-PEM files - * can be in the same directory */ + /* This is purposefully tolerant of errors so non-PEM files + * can be in the same directory */ } while(entry != NULL); PR_CloseDir(dir); } -- cgit v1.2.1 From 04d5c8fb779afdb4f7e85a701f8b4b987a4016ee Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 11 Jun 2008 17:01:58 +0000 Subject: - I did a cleanup of the internal generic SSL layer and how the various SSL libraries are supported. Starting now, each underlying SSL library support code does a set of defines for the 16 functions the generic layer (sslgen.c) uses (all these new function defines use the prefix "curlssl_"). This greatly simplified the generic layer in readability by involving much less #ifdefs and other preprocessor stuff and should make it easier for people to make libcurl work with new SSL libraries. Hopefully I can later on document these 16 functions somewhat as well. I also made most of the internal SSL-dependent functions (using Curl_ssl_ prefix) #defined to nothing when no SSL support is requested - previously they would unnecessarily call mostly empty functions. --- lib/nss.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 97797842d..4267b2492 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -743,7 +743,8 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc *sock) * issuer check, so we provide comments that mimic the OpenSSL * X509_check_issued function (in x509v3/v3_purp.c) */ -static SECStatus check_issuer_cert(struct connectdata *conn, PRFileDesc *sock, +static SECStatus check_issuer_cert(struct connectdata *conn, + PRFileDesc *sock, char* issuer_nickname) { CERTCertificate *cert,*cert_issuer,*issuer; -- cgit v1.2.1 From 5c56bdf2291c5d80ea32d808978afeced2ee824f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 12 Jun 2008 22:00:35 +0000 Subject: fixed bad infof() usage! --- lib/nss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 4267b2492..62fd191d8 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1177,7 +1177,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) goto error; } else { - infof("SSL certificate issuer check ok\n"); + infof(data, "SSL certificate issuer check ok\n"); } } -- cgit v1.2.1 From 74e3def5b3c81556d36c58242e8284405f19e0fe Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 18 Jun 2008 21:48:51 +0000 Subject: check_issuer_cert() now builds and there's one warning less. Still one compiler warning in the code though but we need NSS' base64.h header for that and we don't currently have a suitable way to include it as our own base64.h header kind of "blocks" it. --- lib/nss.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 62fd191d8..fcdda0492 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -62,6 +62,7 @@ #include #include #include +#include #include "memory.h" #include "easyif.h" /* for Curl_convert_from_utf8 prototype */ @@ -766,7 +767,8 @@ static SECStatus check_issuer_cert(struct connectdata *conn, if ((!cert_issuer) || (!issuer)) res = SECFailure; - else if (CERT_CompareCerts(cert_issuer,issuer)==PR_FALSE) + else if (SECITEM_CompareItem(&cert_issuer->derCert, + &issuer->derCert)!=SECEqual) res = SECFailure; CERT_DestroyCertificate(cert); -- cgit v1.2.1 From 36ddb13d1fe4d63b8025ebcbc0eeafcc18ac5a4a Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 18 Jun 2008 21:50:40 +0000 Subject: removed warning about unused argument by simply removing that argument from the check_issuer_cert() proto --- lib/nss.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index fcdda0492..cd52af156 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -744,8 +744,7 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc *sock) * issuer check, so we provide comments that mimic the OpenSSL * X509_check_issued function (in x509v3/v3_purp.c) */ -static SECStatus check_issuer_cert(struct connectdata *conn, - PRFileDesc *sock, +static SECStatus check_issuer_cert(PRFileDesc *sock, char* issuer_nickname) { CERTCertificate *cert,*cert_issuer,*issuer; @@ -1172,7 +1171,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) strncpy(nickname, data->set.str[STRING_SSL_ISSUERCERT], PATH_MAX); nickname[PATH_MAX-1]=0; /* make sure this is zero terminated */ } - if (check_issuer_cert(conn,connssl->handle,nickname)==SECFailure) { + if (check_issuer_cert(connssl->handle, nickname) == SECFailure) { infof(data,"SSL certificate issuer check failed\n"); free(nickname); curlerr = CURLE_SSL_ISSUER_ERROR; -- cgit v1.2.1 From e547bfa933b251263e73ac845c466196e0dec271 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 18 Jun 2008 22:01:55 +0000 Subject: - Rob Crittenden brought a fix for the NSS layer that makes libcurl no longer always fire up a new connection rather than using the existing one when the multi interface is used. Original bug report: https://bugzilla.redhat.com/show_bug.cgi?id=450140 --- lib/nss.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index cd52af156..39c716228 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -925,6 +925,9 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) curlerr = CURLE_SSL_CONNECT_ERROR; + if (connssl->state == ssl_connection_complete) + return CURLE_OK; + /* FIXME. NSS doesn't support multiple databases open at the same time. */ if(!initialized) { initialized = 1; -- cgit v1.2.1 From c1e2341f0a9df2f93ffac00c8cb400fb3b29b207 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Thu, 19 Jun 2008 05:42:45 +0000 Subject: s/strcasecmp/strequal to make it more portable --- lib/nss.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 39c716228..f3f5f6148 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -43,6 +43,7 @@ #include "strequal.h" #include "select.h" #include "sslgen.h" +#include "strequal.h" #define _MPRINTF_REPLACE /* use the internal *printf() functions */ #include @@ -203,7 +204,7 @@ static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model, found = PR_FALSE; for(i=0; i Date: Thu, 19 Jun 2008 05:47:27 +0000 Subject: Removed the #define of ciphernum since keeping a define updated to be the number of entries in a provided table is doomed to fail in the long run. Now we use the NUM_OF_CIPHERS define instead to figure out the amount. --- lib/nss.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index f3f5f6148..093f127cf 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -93,12 +93,6 @@ typedef struct { PRInt32 version; /* protocol version valid for this cipher */ } cipher_s; -#ifdef NSS_ENABLE_ECC -#define ciphernum 48 -#else -#define ciphernum 23 -#endif - #define PK11_SETATTRS(x,id,v,l) (x)->type = (id); \ (x)->pValue=(v); (x)->ulValueLen = (l) @@ -106,7 +100,8 @@ typedef struct { enum sslversion { SSL2 = 1, SSL3 = 2, TLS = 4 }; -static const cipher_s cipherlist[ciphernum] = { +#define NUM_OF_CIPHERS sizeof(cipherlist)/sizeof(cipherlist[0]) +static const cipher_s cipherlist[] = { /* SSL2 cipher suites */ {"rc4", SSL_EN_RC4_128_WITH_MD5, SSL2}, {"rc4export", SSL_EN_RC4_128_EXPORT40_WITH_MD5, SSL2}, @@ -172,8 +167,8 @@ SECMODModule* mod = NULL; static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model, char *cipher_list) { - int i; - PRBool cipher_state[ciphernum]; + unsigned int i; + PRBool cipher_state[NUM_OF_CIPHERS]; PRBool found; char *cipher; SECStatus rv; @@ -187,7 +182,7 @@ static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model, } /* Set every entry in our list to false */ - for(i=0; i Date: Fri, 20 Jun 2008 11:15:54 +0000 Subject: - Phil Pellouchoud found a case where libcurl built with NSS failed to handshake with a SSLv2 server, and it turned out to be because it didn't recognize the cipher named "rc4-md5". In our list that cipher was named plainly "rc4". I've now added rc4-md5 to work as an alias as Phil reported that it made things work for him again. --- lib/nss.c | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 093f127cf..07bb2edd0 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -104,6 +104,7 @@ enum sslversion { SSL2 = 1, SSL3 = 2, TLS = 4 }; static const cipher_s cipherlist[] = { /* SSL2 cipher suites */ {"rc4", SSL_EN_RC4_128_WITH_MD5, SSL2}, + {"rc4-md5", SSL_EN_RC4_128_WITH_MD5, SSL2}, {"rc4export", SSL_EN_RC4_128_EXPORT40_WITH_MD5, SSL2}, {"rc2", SSL_EN_RC2_128_CBC_WITH_MD5, SSL2}, {"rc2export", SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, SSL2}, -- cgit v1.2.1 From 98042b858d9175c99596867ec79c58ff04cf4996 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 21 Jun 2008 21:19:42 +0000 Subject: made Curl_nss_send() take const data to kill compiler warning --- lib/nss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 07bb2edd0..b6ed59c8b 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1195,7 +1195,7 @@ error: /* return number of sent (non-SSL) bytes */ int Curl_nss_send(struct connectdata *conn, /* connection data */ int sockindex, /* socketindex */ - void *mem, /* send this data */ + const void *mem, /* send this data */ size_t len) /* amount to write */ { PRInt32 err; -- cgit v1.2.1 From 3dcd2b82c4095e34342c8d0778d45d547c23b06d Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Thu, 4 Sep 2008 18:59:05 +0000 Subject: fix print formatting string directives --- lib/nss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index b6ed59c8b..c82bbfac7 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1096,7 +1096,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) n = strrchr(data->set.str[STRING_CERT], '/'); if(n) { n++; /* skip last slash */ - snprintf(nickname, PATH_MAX, "PEM Token #%ld:%s", 1, n); + snprintf(nickname, PATH_MAX, "PEM Token #%d:%s", 1, n); } } else { @@ -1164,7 +1164,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) n = strrchr(data->set.str[STRING_SSL_ISSUERCERT], '/'); if (n) { n++; /* skip last slash */ - snprintf(nickname, PATH_MAX, "PEM Token #%ld:%s", 1, n); + snprintf(nickname, PATH_MAX, "PEM Token #%d:%s", 1, n); } } else { -- cgit v1.2.1 From bb67388bbea127222d41019dad393436a253fc77 Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Thu, 4 Sep 2008 19:43:35 +0000 Subject: Made some variables const --- lib/nss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index c82bbfac7..301bd3e96 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -367,7 +367,7 @@ done: return 1; } -static int nss_load_crl(char* crlfilename, PRBool ascii) +static int nss_load_crl(const char* crlfilename, PRBool ascii) { PRFileDesc *infile; PRStatus prstat; -- cgit v1.2.1 From 59e378f48fed849e8e41f0bc6a10bf7a1732ae8a Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Sat, 6 Sep 2008 05:29:05 +0000 Subject: remove unnecessary typecasting of malloc() --- lib/nss.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 301bd3e96..0b60486dd 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -298,8 +298,8 @@ nss_load_cert(const char *filename, PRBool cacert) else slotID = 1; - slotname = (char *)malloc(SLOTSIZE); - nickname = (char *)malloc(PATH_MAX); + slotname = malloc(SLOTSIZE); + nickname = malloc(PATH_MAX); snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID); snprintf(nickname, PATH_MAX, "PEM Token #%ld:%s", slotID, n); @@ -460,7 +460,7 @@ static int nss_load_key(struct connectdata *conn, char *key_file) slotID = 1; /* hardcoded for now */ - slotname = (char *)malloc(SLOTSIZE); + slotname = malloc(SLOTSIZE); snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID); slot = PK11_FindSlotByName(slotname); @@ -485,7 +485,7 @@ static int nss_load_key(struct connectdata *conn, char *key_file) SECMOD_WaitForAnyTokenEvent(mod, 0, 0); PK11_IsPresent(slot); - parg = (pphrase_arg_t *) malloc(sizeof(*parg)); + parg = malloc(sizeof(pphrase_arg_t)); parg->retryCount = 0; parg->data = conn->data; /* parg is initialized in nss_Init_Tokens() */ @@ -581,7 +581,7 @@ static SECStatus nss_Init_Tokens(struct connectdata * conn) SECStatus ret, status = SECSuccess; pphrase_arg_t *parg = NULL; - parg = (pphrase_arg_t *) malloc(sizeof(*parg)); + parg = malloc(sizeof(pphrase_arg_t)); parg->retryCount = 0; parg->data = conn->data; @@ -800,7 +800,7 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, if(!strncmp(nickname, "PEM Token", 9)) { CK_SLOT_ID slotID = 1; /* hardcoded for now */ - char * slotname = (char *)malloc(SLOTSIZE); + char * slotname = malloc(SLOTSIZE); snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID); slot = PK11_FindSlotByName(slotname); privKey = PK11_FindPrivateKeyFromCert(slot, cert, NULL); @@ -956,7 +956,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) NSS_SetDomesticPolicy(); #ifdef HAVE_PK11_CREATEGENERICOBJECT - configstring = (char *)malloc(PATH_MAX); + configstring = malloc(PATH_MAX); PR_snprintf(configstring, PATH_MAX, "library=%s name=PEM", pem_library); @@ -1091,7 +1091,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) char *n; char *nickname; - nickname = (char *)malloc(PATH_MAX); + nickname = malloc(PATH_MAX); if(is_file(data->set.str[STRING_CERT])) { n = strrchr(data->set.str[STRING_CERT], '/'); if(n) { @@ -1159,7 +1159,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if (data->set.str[STRING_SSL_ISSUERCERT]) { char *n; char *nickname; - nickname = (char *)malloc(PATH_MAX); + nickname = malloc(PATH_MAX); if(is_file(data->set.str[STRING_SSL_ISSUERCERT])) { n = strrchr(data->set.str[STRING_SSL_ISSUERCERT], '/'); if (n) { -- cgit v1.2.1 From 23e5402becfb97ab13480cd33e05ab549dc5efa4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 23 Sep 2008 10:27:04 +0000 Subject: - Rob Crittenden brought a patch to "add some locking for thread-safety to NSS implementation". --- lib/nss.c | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 0b60486dd..a868fc382 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -78,7 +78,9 @@ PRFileDesc *PR_ImportTCPSocket(PRInt32 osfd); -int initialized = 0; +PRLock * nss_initlock = NULL; + +volatile int initialized = 0; #define HANDSHAKE_TIMEOUT 30 @@ -837,8 +839,11 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, */ int Curl_nss_init(void) { - if(!initialized) + /* curl_global_init() is not thread-safe so this test is ok */ + if (nss_initlock == NULL) { PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 256); + nss_initlock = PR_NewLock(); + } /* We will actually initialize NSS later */ @@ -848,7 +853,17 @@ int Curl_nss_init(void) /* Global cleanup */ void Curl_nss_cleanup(void) { - NSS_Shutdown(); + /* This function isn't required to be threadsafe and this is only done + * as a safety feature. + */ + PR_Lock(nss_initlock); + if (initialized) + NSS_Shutdown(); + PR_Unlock(nss_initlock); + + PR_DestroyLock(nss_initlock); + nss_initlock = NULL; + initialized = 0; } @@ -926,7 +941,8 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) return CURLE_OK; /* FIXME. NSS doesn't support multiple databases open at the same time. */ - if(!initialized) { + PR_Lock(nss_initlock); + if(!initialized && !NSS_IsInitialized()) { initialized = 1; certDir = getenv("SSL_DIR"); /* Look in $SSL_DIR */ @@ -950,6 +966,8 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if(rv != SECSuccess) { infof(conn->data, "Unable to initialize NSS database\n"); curlerr = CURLE_SSL_CACERT_BADFILE; + initialized = 0; + PR_Unlock(nss_initlock); goto error; } @@ -972,6 +990,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) } #endif } + PR_Unlock(nss_initlock); model = PR_NewTCPSocket(); if(!model) -- cgit v1.2.1 From a579d6706436615845f57692921e0891fb6e3719 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 15 Oct 2008 21:43:48 +0000 Subject: - Pascal Terjan filed bug #2154627 (http://curl.haxx.se/bug/view.cgi?id=2154627) which pointed out that libcurl uses strcasecmp() in multiple places where it causes failures when the Turkish locale is used. This is because 'i' and 'I' isn't the same letter so strcasecmp() on those letters are different in Turkish than in English (or just about all other languages). I thus introduced a totally new internal function in libcurl (called Curl_ascii_equal) for doing case insentive comparisons for english-(ascii?) style strings that thus will make "file" and "FILE" match even if the Turkish locale is selected. --- lib/nss.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index a868fc382..409b0462d 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -43,7 +43,6 @@ #include "strequal.h" #include "select.h" #include "sslgen.h" -#include "strequal.h" #define _MPRINTF_REPLACE /* use the internal *printf() functions */ #include @@ -202,7 +201,7 @@ static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model, found = PR_FALSE; for(i=0; i Date: Thu, 16 Oct 2008 08:23:48 +0000 Subject: Renamed Curl_ascii_equal to Curl_raw_equal and bugfixed the my_toupper function used in strequal.c so now all test cases run fine for me again. --- lib/nss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 409b0462d..689326f48 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -201,7 +201,7 @@ static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model, found = PR_FALSE; for(i=0; i Date: Sat, 15 Nov 2008 23:43:10 +0000 Subject: based on a report by Jim Meyering, I went over and added checks for return codes for all calls to malloc and strdup that were missing. I also changed a few malloc(13) to use arrays on the stack and a few malloc(PATH_MAX) to instead use aprintf() to lower memory use. I also fixed a memory leak in Curl_nss_connect() when CURLOPT_ISSUERCERT is in use. --- lib/nss.c | 92 ++++++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 55 insertions(+), 37 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 689326f48..dcbf27620 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -65,6 +65,7 @@ #include #include "memory.h" +#include "rawstr.h" #include "easyif.h" /* for Curl_convert_from_utf8 prototype */ /* The last #include file should be: */ @@ -263,7 +264,7 @@ nss_load_cert(const char *filename, PRBool cacert) CK_BBOOL cktrue = CK_TRUE; CK_BBOOL ckfalse = CK_FALSE; CK_OBJECT_CLASS objClass = CKO_CERTIFICATE; - char *slotname = NULL; + char slotname[SLOTSIZE]; #endif CERTCertificate *cert; char *nickname = NULL; @@ -284,6 +285,8 @@ nss_load_cert(const char *filename, PRBool cacert) if(cacert) return 0; /* You can't specify an NSS CA nickname this way */ nickname = strdup(filename); + if(!nickname) + return 0; goto done; } @@ -299,15 +302,15 @@ nss_load_cert(const char *filename, PRBool cacert) else slotID = 1; - slotname = malloc(SLOTSIZE); - nickname = malloc(PATH_MAX); snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID); - snprintf(nickname, PATH_MAX, "PEM Token #%ld:%s", slotID, n); + + nickname = aprintf("PEM Token #%ld:%s", slotID, n); + if(!nickname) + return 0; slot = PK11_FindSlotByName(slotname); if(!slot) { - free(slotname); free(nickname); return 0; } @@ -334,7 +337,6 @@ nss_load_cert(const char *filename, PRBool cacert) PK11_FreeSlot(slot); - free(slotname); if(rv == NULL) { free(nickname); return 0; @@ -452,8 +454,8 @@ static int nss_load_key(struct connectdata *conn, char *key_file) CK_BBOOL cktrue = CK_TRUE; CK_OBJECT_CLASS objClass = CKO_PRIVATE_KEY; CK_SLOT_ID slotID; - char *slotname = NULL; pphrase_arg_t *parg = NULL; + char slotname[SLOTSIZE]; attrs = theTemplate; @@ -461,11 +463,8 @@ static int nss_load_key(struct connectdata *conn, char *key_file) slotID = 1; /* hardcoded for now */ - slotname = malloc(SLOTSIZE); - snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID); - + snprintf(slotname, sizeof(slotname), "PEM Token #%ld", slotID); slot = PK11_FindSlotByName(slotname); - free(slotname); if(!slot) return 0; @@ -487,6 +486,8 @@ static int nss_load_key(struct connectdata *conn, char *key_file) PK11_IsPresent(slot); parg = malloc(sizeof(pphrase_arg_t)); + if(!parg) + return 0; parg->retryCount = 0; parg->data = conn->data; /* parg is initialized in nss_Init_Tokens() */ @@ -583,6 +584,9 @@ static SECStatus nss_Init_Tokens(struct connectdata * conn) pphrase_arg_t *parg = NULL; parg = malloc(sizeof(pphrase_arg_t)); + if(!parg) + return SECFailure; + parg->retryCount = 0; parg->data = conn->data; @@ -743,7 +747,7 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc *sock) * X509_check_issued function (in x509v3/v3_purp.c) */ static SECStatus check_issuer_cert(PRFileDesc *sock, - char* issuer_nickname) + char *issuer_nickname) { CERTCertificate *cert,*cert_issuer,*issuer; SECStatus res=SECSuccess; @@ -801,12 +805,11 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, if(!strncmp(nickname, "PEM Token", 9)) { CK_SLOT_ID slotID = 1; /* hardcoded for now */ - char * slotname = malloc(SLOTSIZE); + char slotname[SLOTSIZE]; snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID); slot = PK11_FindSlotByName(slotname); privKey = PK11_FindPrivateKeyFromCert(slot, cert, NULL); PK11_FreeSlot(slot); - free(slotname); if(privKey) { secStatus = SECSuccess; } @@ -973,12 +976,12 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) NSS_SetDomesticPolicy(); #ifdef HAVE_PK11_CREATEGENERICOBJECT - configstring = malloc(PATH_MAX); - - PR_snprintf(configstring, PATH_MAX, "library=%s name=PEM", pem_library); - + configstring = aprintf("library=%s name=PEM", pem_library); + if(!configstring) + goto error; mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); free(configstring); + if(!mod || !mod->loaded) { if(mod) { SECMOD_DestroyModule(mod); @@ -1108,41 +1111,48 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if(data->set.str[STRING_CERT]) { char *n; char *nickname; + bool nickname_alloc = FALSE; - nickname = malloc(PATH_MAX); if(is_file(data->set.str[STRING_CERT])) { n = strrchr(data->set.str[STRING_CERT], '/'); if(n) { n++; /* skip last slash */ - snprintf(nickname, PATH_MAX, "PEM Token #%d:%s", 1, n); + nickname = aprintf(nickname, "PEM Token #%d:%s", 1, n); + if(!nickname) + return CURLE_OUT_OF_MEMORY; + + nickname_alloc = TRUE; } } else { - strncpy(nickname, data->set.str[STRING_CERT], PATH_MAX); - nickname[PATH_MAX-1]=0; /* make sure this is zero terminated */ + nickname = data->set.str[STRING_CERT]; } if(nss_Init_Tokens(conn) != SECSuccess) { - free(nickname); + if(nickname_alloc) + free(nickname); goto error; } if(!cert_stuff(conn, data->set.str[STRING_CERT], data->set.str[STRING_KEY])) { /* failf() is already done in cert_stuff() */ - free(nickname); + if(nickname_alloc) + free(nickname); return CURLE_SSL_CERTPROBLEM; } - connssl->client_nickname = strdup(nickname); + /* this "takes over" the pointer to the allocated name or makes a + dup of it */ + connssl->client_nickname = nickname_alloc?nickname:strdup(nickname); + if(!connssl->client_nickname) + return CURLE_OUT_OF_MEMORY; + if(SSL_GetClientAuthDataHook(model, (SSLGetClientAuthData) SelectClientCert, - (void *)connssl) != - SECSuccess) { + (void *)connssl) != SECSuccess) { curlerr = CURLE_SSL_CERTPROBLEM; goto error; } - free(nickname); - PK11_SetPasswordFunc(nss_no_password); } else @@ -1177,21 +1187,29 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if (data->set.str[STRING_SSL_ISSUERCERT]) { char *n; char *nickname; - nickname = malloc(PATH_MAX); + bool nickname_alloc = FALSE; + SECStatus ret; + if(is_file(data->set.str[STRING_SSL_ISSUERCERT])) { n = strrchr(data->set.str[STRING_SSL_ISSUERCERT], '/'); if (n) { n++; /* skip last slash */ - snprintf(nickname, PATH_MAX, "PEM Token #%d:%s", 1, n); + nickname = aprintf("PEM Token #%d:%s", 1, n); + if(!nickname) + return CURLE_OUT_OF_MEMORY; + nickname_alloc = TRUE; } } - else { - strncpy(nickname, data->set.str[STRING_SSL_ISSUERCERT], PATH_MAX); - nickname[PATH_MAX-1]=0; /* make sure this is zero terminated */ - } - if (check_issuer_cert(connssl->handle, nickname) == SECFailure) { - infof(data,"SSL certificate issuer check failed\n"); + else + nickname = data->set.str[STRING_SSL_ISSUERCERT]; + + ret = check_issuer_cert(connssl->handle, nickname); + + if(nickname_alloc) free(nickname); + + if(SECFailure == ret) { + infof(data,"SSL certificate issuer check failed\n"); curlerr = CURLE_SSL_ISSUER_ERROR; goto error; } -- cgit v1.2.1 From 3c2ad4022c009ff06e5e62049c7a5f78d3cfdb01 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 7 Jan 2009 14:10:35 +0000 Subject: - Rob Crittenden did once again provide an NSS update: I have to jump through a few hoops now with the NSS library initialization since another part of an application may have already initialized NSS by the time Curl gets invoked. This patch is more careful to only shutdown the NSS library if Curl did the initialization. It also adds in a bit of code to set the default ciphers if the app that call NSS_Init* did not call NSS_SetDomesticPolicy() or set specific ciphers. One might argue that this lets other application developers get lazy and/or they aren't using the NSS API correctly, and you'd be right. But still, this will avoid terribly difficult-to-trace crashes and is generally helpful. --- lib/nss.c | 59 +++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 18 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index dcbf27620..55f3169e9 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -231,6 +231,24 @@ static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model, return SECSuccess; } +/* + * Get the number of ciphers that are enabled. We use this to determine + * if we need to call NSS_SetDomesticPolicy() to enable the default ciphers. + */ +static int num_enabled_ciphers() +{ + PRInt32 policy = 0; + int count = 0; + int i; + + for(i=0; idata, "Unable to initialize NSS database\n"); - curlerr = CURLE_SSL_CACERT_BADFILE; - initialized = 0; - PR_Unlock(nss_initlock); - goto error; + if (!NSS_IsInitialized()) { + initialized = 1; + if(!certDir) { + rv = NSS_NoDB_Init(NULL); + } + else { + rv = NSS_Initialize(certDir, NULL, NULL, "secmod.db", + NSS_INIT_READONLY); + } + if(rv != SECSuccess) { + infof(conn->data, "Unable to initialize NSS database\n"); + curlerr = CURLE_SSL_CACERT_BADFILE; + initialized = 0; + PR_Unlock(nss_initlock); + goto error; + } } - NSS_SetDomesticPolicy(); + if(num_enabled_ciphers() == 0) + NSS_SetDomesticPolicy(); #ifdef HAVE_PK11_CREATEGENERICOBJECT configstring = aprintf("library=%s name=PEM", pem_library); - if(!configstring) + if(!configstring) { + PR_Unlock(nss_initlock); goto error; + } mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); free(configstring); -- cgit v1.2.1 From dd058b8de60ed079367676ca91299f8c0a306715 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 7 Jan 2009 14:12:01 +0000 Subject: fix compiler warnings --- lib/nss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 55f3169e9..98ed31149 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -235,11 +235,11 @@ static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model, * Get the number of ciphers that are enabled. We use this to determine * if we need to call NSS_SetDomesticPolicy() to enable the default ciphers. */ -static int num_enabled_ciphers() +static int num_enabled_ciphers(void) { PRInt32 policy = 0; int count = 0; - int i; + unsigned int i; for(i=0; i Date: Tue, 17 Feb 2009 12:18:34 +0000 Subject: - Kamil Dudka made NSS-powered builds compile and run again! --- lib/nss.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 98ed31149..83ad6ebbc 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1140,7 +1140,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) n = strrchr(data->set.str[STRING_CERT], '/'); if(n) { n++; /* skip last slash */ - nickname = aprintf(nickname, "PEM Token #%d:%s", 1, n); + nickname = aprintf("PEM Token #%d:%s", 1, n); if(!nickname) return CURLE_OUT_OF_MEMORY; @@ -1171,7 +1171,8 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if(SSL_GetClientAuthDataHook(model, (SSLGetClientAuthData) SelectClientCert, - (void *)connssl) != SECSuccess) { + (void *)connssl->client_nickname) != + SECSuccess) { curlerr = CURLE_SSL_CERTPROBLEM; goto error; } -- cgit v1.2.1 From 794b4da840852ef7ad6c25ac3f6b1e2f8284dbc3 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 27 Feb 2009 08:53:10 +0000 Subject: Indentation fixes, untabify and related whitespace-cleanup. No code changed. --- lib/nss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 83ad6ebbc..ce9e0da5f 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -455,7 +455,7 @@ static int nss_load_crl(const char* crlfilename, PRBool ascii) NULL,SEC_CRL_TYPE, NULL,CRL_IMPORT_DEFAULT_OPTIONS, NULL,(CRL_DECODE_DEFAULT_OPTIONS| - CRL_DECODE_DONT_COPY_DER)); + CRL_DECODE_DONT_COPY_DER)); if (slot) PK11_FreeSlot(slot); if (!crl) return 0; SEC_DestroyCrl(crl); -- cgit v1.2.1 From 5f19822e37501e1e168408c947b1c757e8f06327 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 18 Mar 2009 12:48:51 +0000 Subject: - Kamil Dudka brought a patch that enables 6 additional crypto algorithms when NSS is used. These ciphers were added in NSS 3.4 and require to be enabled explicitly. --- lib/nss.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index ce9e0da5f..373c28390 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -162,6 +162,18 @@ static const cipher_s cipherlist[] = { #endif }; +/* following ciphers are new in NSS 3.4 and not enabled by default, therefor + they are enabled explicitly */ +static const int enable_ciphers_by_default[] = { + TLS_DHE_DSS_WITH_AES_128_CBC_SHA, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + SSL_NULL_WITH_NULL_NULL +}; + #ifdef HAVE_PK11_CREATEGENERICOBJECT static const char* pem_library = "libnsspem.so"; #endif @@ -954,6 +966,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) #endif char *certDir = NULL; int curlerr; + const int *cipher_to_enable; curlerr = CURLE_SSL_CONNECT_ERROR; @@ -1057,6 +1070,16 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if(SSL_OptionSet(model, SSL_V2_COMPATIBLE_HELLO, ssl2) != SECSuccess) goto error; + /* enable all ciphers from enable_ciphers_by_default */ + cipher_to_enable = enable_ciphers_by_default; + while (SSL_NULL_WITH_NULL_NULL != *cipher_to_enable) { + if (SSL_CipherPrefSet(model, *cipher_to_enable, PR_TRUE) != SECSuccess) { + curlerr = CURLE_SSL_CIPHER; + goto error; + } + cipher_to_enable++; + } + if(data->set.ssl.cipher_list) { if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) { curlerr = CURLE_SSL_CIPHER; -- cgit v1.2.1 From 235c0077b8648a3b941090991a0ce6ac24d681ab Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 13 Apr 2009 17:42:10 +0000 Subject: - Toshio Kuratomi reported a memory leak problem with libcurl+NSS that turned out to be leaking cacerts. Kamil Dudka helped me complete the fix. The issue is found in Redhat's bug tracker: https://bugzilla.redhat.com/show_bug.cgi?id=453612 There are still memory leaks present, but they seem to have other reasons. --- lib/nss.c | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 373c28390..6e0a91bfd 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -282,13 +282,12 @@ static int is_file(const char *filename) return 0; } -static int -nss_load_cert(const char *filename, PRBool cacert) +static int nss_load_cert(struct ssl_connect_data *ssl, + const char *filename, PRBool cacert) { #ifdef HAVE_PK11_CREATEGENERICOBJECT CK_SLOT_ID slotID; PK11SlotInfo * slot = NULL; - PK11GenericObject *rv; CK_ATTRIBUTE *attrs; CK_ATTRIBUTE theTemplate[20]; CK_BBOOL cktrue = CK_TRUE; @@ -363,11 +362,12 @@ nss_load_cert(const char *filename, PRBool cacert) /* This load the certificate in our PEM module into the appropriate * slot. */ - rv = PK11_CreateGenericObject(slot, theTemplate, 4, PR_FALSE /* isPerm */); + ssl->cacert[slotID] = PK11_CreateGenericObject(slot, theTemplate, 4, + PR_FALSE /* isPerm */); PK11_FreeSlot(slot); - if(rv == NULL) { + if(ssl->cacert == NULL) { free(nickname); return 0; } @@ -474,11 +474,10 @@ static int nss_load_crl(const char* crlfilename, PRBool ascii) return 1; } -static int nss_load_key(struct connectdata *conn, char *key_file) +static int nss_load_key(struct connectdata *conn, int sockindex, char *key_file) { #ifdef HAVE_PK11_CREATEGENERICOBJECT PK11SlotInfo * slot = NULL; - PK11GenericObject *rv; CK_ATTRIBUTE *attrs; CK_ATTRIBUTE theTemplate[20]; CK_BBOOL cktrue = CK_TRUE; @@ -486,6 +485,7 @@ static int nss_load_key(struct connectdata *conn, char *key_file) CK_SLOT_ID slotID; pphrase_arg_t *parg = NULL; char slotname[SLOTSIZE]; + struct ssl_connect_data *sslconn = &conn->ssl[sockindex]; attrs = theTemplate; @@ -505,8 +505,9 @@ static int nss_load_key(struct connectdata *conn, char *key_file) strlen(key_file)+1); attrs++; /* When adding an encrypted key the PKCS#11 will be set as removed */ - rv = PK11_CreateGenericObject(slot, theTemplate, 3, PR_FALSE /* isPerm */); - if(rv == NULL) { + sslconn->key = PK11_CreateGenericObject(slot, theTemplate, 3, + PR_FALSE /* isPerm */); + if(sslconn->key == NULL) { PR_SetError(SEC_ERROR_BAD_KEY, 0); return 0; } @@ -554,13 +555,14 @@ static int display_error(struct connectdata *conn, PRInt32 err, return 0; /* The caller will print a generic error */ } -static int cert_stuff(struct connectdata *conn, char *cert_file, char *key_file) +static int cert_stuff(struct connectdata *conn, + int sockindex, char *cert_file, char *key_file) { struct SessionHandle *data = conn->data; int rv = 0; if(cert_file) { - rv = nss_load_cert(cert_file, PR_FALSE); + rv = nss_load_cert(&conn->ssl[sockindex], cert_file, PR_FALSE); if(!rv) { if(!display_error(conn, PR_GetError(), cert_file)) failf(data, "Unable to load client cert %d.", PR_GetError()); @@ -569,10 +571,10 @@ static int cert_stuff(struct connectdata *conn, char *cert_file, char *key_file) } if(key_file || (is_file(cert_file))) { if(key_file) - rv = nss_load_key(conn, key_file); + rv = nss_load_key(conn, sockindex, key_file); else /* In case the cert file also has the key */ - rv = nss_load_key(conn, cert_file); + rv = nss_load_key(conn, sockindex, cert_file); if(!rv) { if(!display_error(conn, PR_GetError(), key_file)) failf(data, "Unable to load client key %d.", PR_GetError()); @@ -938,6 +940,12 @@ void Curl_nss_close(struct connectdata *conn, int sockindex) free(connssl->client_nickname); connssl->client_nickname = NULL; } + if(connssl->key) + (void)PK11_DestroyGenericObject(connssl->key); + if(connssl->cacert[1]) + (void)PK11_DestroyGenericObject(connssl->cacert[1]); + if(connssl->cacert[0]) + (void)PK11_DestroyGenericObject(connssl->cacert[0]); connssl->handle = NULL; } } @@ -973,6 +981,10 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if (connssl->state == ssl_connection_complete) return CURLE_OK; + connssl->cacert[0] = NULL; + connssl->cacert[1] = NULL; + connssl->key = NULL; + /* FIXME. NSS doesn't support multiple databases open at the same time. */ PR_Lock(nss_initlock); if(!initialized) { @@ -1100,7 +1112,8 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) /* skip the verifying of the peer */ ; else if(data->set.ssl.CAfile) { - int rc = nss_load_cert(data->set.ssl.CAfile, PR_TRUE); + int rc = nss_load_cert(&conn->ssl[sockindex], data->set.ssl.CAfile, + PR_TRUE); if(!rc) { curlerr = CURLE_SSL_CACERT_BADFILE; goto error; @@ -1128,7 +1141,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) snprintf(fullpath, sizeof(fullpath), "%s/%s", data->set.ssl.CApath, entry->name); - rc = nss_load_cert(fullpath, PR_TRUE); + rc = nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE); /* FIXME: check this return value! */ } /* This is purposefully tolerant of errors so non-PEM files @@ -1178,7 +1191,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) free(nickname); goto error; } - if(!cert_stuff(conn, data->set.str[STRING_CERT], + if(!cert_stuff(conn, sockindex, data->set.str[STRING_CERT], data->set.str[STRING_KEY])) { /* failf() is already done in cert_stuff() */ if(nickname_alloc) -- cgit v1.2.1 From 97f27ea585c3e7e5b18a14581580b8bac52a0d2b Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 14 Apr 2009 09:40:53 +0000 Subject: Kamil Dudka's follow-up fix --- lib/nss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 6e0a91bfd..40e6a1945 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -367,7 +367,7 @@ static int nss_load_cert(struct ssl_connect_data *ssl, PK11_FreeSlot(slot); - if(ssl->cacert == NULL) { + if(ssl->cacert[slotID] == NULL) { free(nickname); return 0; } -- cgit v1.2.1 From 33a3753c3f41d546ebf3350685eb7201d25783f4 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Tue, 21 Apr 2009 11:46:16 +0000 Subject: libcurl's memory.h renamed to curl_memory.h --- lib/nss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 40e6a1945..e8d2b555b 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -64,7 +64,7 @@ #include #include -#include "memory.h" +#include "curl_memory.h" #include "rawstr.h" #include "easyif.h" /* for Curl_convert_from_utf8 prototype */ -- cgit v1.2.1 From 828a26286d22b61b28de6cf356806731560b3805 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 24 Apr 2009 21:55:18 +0000 Subject: - Kamil Dudka fixed another NSS-related leak when client certs were used. --- lib/nss.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index e8d2b555b..3fdc18dad 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -527,6 +527,7 @@ static int nss_load_key(struct connectdata *conn, int sockindex, char *key_file) return 0; } free(parg); + PK11_FreeSlot(slot); return 1; #else @@ -819,9 +820,9 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, struct CERTCertificateStr **pRetCert, struct SECKEYPrivateKeyStr **pRetKey) { - CERTCertificate *cert; SECKEYPrivateKey *privKey; - char *nickname = (char *)arg; + struct ssl_connect_data *connssl = (struct ssl_connect_data *) arg; + char *nickname = connssl->client_nickname; void *proto_win = NULL; SECStatus secStatus = SECFailure; PK11SlotInfo *slot; @@ -832,34 +833,35 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, if(!nickname) return secStatus; - cert = PK11_FindCertFromNickname(nickname, proto_win); - if(cert) { + connssl->client_cert = PK11_FindCertFromNickname(nickname, proto_win); + if(connssl->client_cert) { if(!strncmp(nickname, "PEM Token", 9)) { CK_SLOT_ID slotID = 1; /* hardcoded for now */ char slotname[SLOTSIZE]; snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID); slot = PK11_FindSlotByName(slotname); - privKey = PK11_FindPrivateKeyFromCert(slot, cert, NULL); + privKey = PK11_FindPrivateKeyFromCert(slot, connssl->client_cert, NULL); PK11_FreeSlot(slot); if(privKey) { secStatus = SECSuccess; } } else { - privKey = PK11_FindKeyByAnyCert(cert, proto_win); + privKey = PK11_FindKeyByAnyCert(connssl->client_cert, proto_win); if(privKey) secStatus = SECSuccess; } } if(secStatus == SECSuccess) { - *pRetCert = cert; + *pRetCert = connssl->client_cert; *pRetKey = privKey; } else { - if(cert) - CERT_DestroyCertificate(cert); + if(connssl->client_cert) + CERT_DestroyCertificate(connssl->client_cert); + connssl->client_cert = NULL; } return secStatus; @@ -891,8 +893,12 @@ void Curl_nss_cleanup(void) * as a safety feature. */ PR_Lock(nss_initlock); - if (initialized) + if (initialized) { + if(mod) + SECMOD_DestroyModule(mod); + mod = NULL; NSS_Shutdown(); + } PR_Unlock(nss_initlock); PR_DestroyLock(nss_initlock); @@ -940,6 +946,8 @@ void Curl_nss_close(struct connectdata *conn, int sockindex) free(connssl->client_nickname); connssl->client_nickname = NULL; } + if(connssl->client_cert) + CERT_DestroyCertificate(connssl->client_cert); if(connssl->key) (void)PK11_DestroyGenericObject(connssl->key); if(connssl->cacert[1]) @@ -981,6 +989,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if (connssl->state == ssl_connection_complete) return CURLE_OK; + connssl->client_cert = NULL; connssl->cacert[0] = NULL; connssl->cacert[1] = NULL; connssl->key = NULL; @@ -1207,8 +1216,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if(SSL_GetClientAuthDataHook(model, (SSLGetClientAuthData) SelectClientCert, - (void *)connssl->client_nickname) != - SECSuccess) { + (void *)connssl) != SECSuccess) { curlerr = CURLE_SSL_CERTPROBLEM; goto error; } -- cgit v1.2.1 From 6e1632c60688a7c801bc25c8bb79bb937b605da8 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 11 May 2009 09:13:49 +0000 Subject: - Kamil Dudka provided a fix for libcurl-NSS reported by Michael Cronenworth at https://bugzilla.redhat.com/show_bug.cgi?id=453612#c12 If an incorrect password is given while loading a private key, libcurl ends up in an infinite loop consuming memory. The bug is critical. --- lib/nss.c | 52 ++++++++-------------------------------------------- 1 file changed, 8 insertions(+), 44 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 3fdc18dad..15978190a 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -84,11 +84,6 @@ volatile int initialized = 0; #define HANDSHAKE_TIMEOUT 30 -typedef struct { - PRInt32 retryCount; - struct SessionHandle *data; -} pphrase_arg_t; - typedef struct { const char *name; int num; @@ -483,7 +478,6 @@ static int nss_load_key(struct connectdata *conn, int sockindex, char *key_file) CK_BBOOL cktrue = CK_TRUE; CK_OBJECT_CLASS objClass = CKO_PRIVATE_KEY; CK_SLOT_ID slotID; - pphrase_arg_t *parg = NULL; char slotname[SLOTSIZE]; struct ssl_connect_data *sslconn = &conn->ssl[sockindex]; @@ -516,17 +510,13 @@ static int nss_load_key(struct connectdata *conn, int sockindex, char *key_file) SECMOD_WaitForAnyTokenEvent(mod, 0, 0); PK11_IsPresent(slot); - parg = malloc(sizeof(pphrase_arg_t)); - if(!parg) - return 0; - parg->retryCount = 0; - parg->data = conn->data; /* parg is initialized in nss_Init_Tokens() */ - if(PK11_Authenticate(slot, PR_TRUE, parg) != SECSuccess) { - free(parg); + if(PK11_Authenticate(slot, PR_TRUE, + conn->data->set.str[STRING_KEY_PASSWD]) != SECSuccess) { + + PK11_FreeSlot(slot); return 0; } - free(parg); PK11_FreeSlot(slot); return 1; @@ -588,25 +578,11 @@ static int cert_stuff(struct connectdata *conn, static char * nss_get_password(PK11SlotInfo * slot, PRBool retry, void *arg) { - pphrase_arg_t *parg; - parg = (pphrase_arg_t *) arg; - (void)slot; /* unused */ - if(retry > 2) + if(retry || NULL == arg) return NULL; - if(parg->data->set.str[STRING_KEY_PASSWD]) - return (char *)PORT_Strdup((char *)parg->data->set.str[STRING_KEY_PASSWD]); else - return NULL; -} - -/* No longer ask for the password, parg has been freed */ -static char * nss_no_password(PK11SlotInfo *slot, PRBool retry, void *arg) -{ - (void)slot; /* unused */ - (void)retry; /* unused */ - (void)arg; /* unused */ - return NULL; + return (char *)PORT_Strdup((char *)arg); } static SECStatus nss_Init_Tokens(struct connectdata * conn) @@ -614,14 +590,6 @@ static SECStatus nss_Init_Tokens(struct connectdata * conn) PK11SlotList *slotList; PK11SlotListElement *listEntry; SECStatus ret, status = SECSuccess; - pphrase_arg_t *parg = NULL; - - parg = malloc(sizeof(pphrase_arg_t)); - if(!parg) - return SECFailure; - - parg->retryCount = 0; - parg->data = conn->data; PK11_SetPasswordFunc(nss_get_password); @@ -644,7 +612,8 @@ static SECStatus nss_Init_Tokens(struct connectdata * conn) continue; } - ret = PK11_Authenticate(slot, PR_TRUE, parg); + ret = PK11_Authenticate(slot, PR_TRUE, + conn->data->set.str[STRING_KEY_PASSWD]); if(SECSuccess != ret) { if(PR_GetError() == SEC_ERROR_BAD_PASSWORD) infof(conn->data, "The password for token '%s' is incorrect\n", @@ -652,12 +621,9 @@ static SECStatus nss_Init_Tokens(struct connectdata * conn) status = SECFailure; break; } - parg->retryCount = 0; /* reset counter to 0 for the next token */ PK11_FreeSlot(slot); } - free(parg); - return status; } @@ -1220,8 +1186,6 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) curlerr = CURLE_SSL_CERTPROBLEM; goto error; } - - PK11_SetPasswordFunc(nss_no_password); } else connssl->client_nickname = NULL; -- cgit v1.2.1 From 0bf9c1e8815ad1bf07ef875b3e7a5a3acc9f3e8c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 27 May 2009 22:01:03 +0000 Subject: - Claes Jakobsson fixed libcurl-NSS to build fine even without the PK11_CreateGenericObject() function. --- lib/nss.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 15978190a..509a311ec 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -914,12 +914,14 @@ void Curl_nss_close(struct connectdata *conn, int sockindex) } if(connssl->client_cert) CERT_DestroyCertificate(connssl->client_cert); +#ifdef HAVE_PK11_CREATEGENERICOBJECT if(connssl->key) (void)PK11_DestroyGenericObject(connssl->key); if(connssl->cacert[1]) (void)PK11_DestroyGenericObject(connssl->cacert[1]); if(connssl->cacert[0]) (void)PK11_DestroyGenericObject(connssl->cacert[0]); +#endif connssl->handle = NULL; } } @@ -956,9 +958,11 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) return CURLE_OK; connssl->client_cert = NULL; +#ifdef HAVE_PK11_CREATEGENERICOBJECT connssl->cacert[0] = NULL; connssl->cacert[1] = NULL; connssl->key = NULL; +#endif /* FIXME. NSS doesn't support multiple databases open at the same time. */ PR_Lock(nss_initlock); -- cgit v1.2.1 From 15eaf27bc7b4ad33af275b9cd0601ef19d54ac9c Mon Sep 17 00:00:00 2001 From: Dan Fandrich Date: Thu, 28 May 2009 21:32:31 +0000 Subject: Fixed a few comment typos (from the FreeBSD ports) --- lib/nss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 509a311ec..3bfaf7109 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -157,7 +157,7 @@ static const cipher_s cipherlist[] = { #endif }; -/* following ciphers are new in NSS 3.4 and not enabled by default, therefor +/* following ciphers are new in NSS 3.4 and not enabled by default, therefore they are enabled explicitly */ static const int enable_ciphers_by_default[] = { TLS_DHE_DSS_WITH_AES_128_CBC_SHA, -- cgit v1.2.1 From 3e0c067e43d548bee09b836e95deb0278e96d203 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Mon, 8 Jun 2009 21:25:16 +0000 Subject: - Claes Jakobsson provided a patch for libcurl-NSS that fixed a bad refcount issue with client certs that caused issues like segfaults. http://curl.haxx.se/mail/lib-2009-05/0316.html --- lib/nss.c | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 3bfaf7109..a976b71ca 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -786,7 +786,8 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, struct CERTCertificateStr **pRetCert, struct SECKEYPrivateKeyStr **pRetKey) { - SECKEYPrivateKey *privKey; + SECKEYPrivateKey *privKey = NULL; + CERTCertificate *cert; struct ssl_connect_data *connssl = (struct ssl_connect_data *) arg; char *nickname = connssl->client_nickname; void *proto_win = NULL; @@ -799,36 +800,32 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, if(!nickname) return secStatus; - connssl->client_cert = PK11_FindCertFromNickname(nickname, proto_win); - if(connssl->client_cert) { - + cert = PK11_FindCertFromNickname(nickname, proto_win); + if(cert) { if(!strncmp(nickname, "PEM Token", 9)) { CK_SLOT_ID slotID = 1; /* hardcoded for now */ char slotname[SLOTSIZE]; snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID); slot = PK11_FindSlotByName(slotname); - privKey = PK11_FindPrivateKeyFromCert(slot, connssl->client_cert, NULL); + privKey = PK11_FindPrivateKeyFromCert(slot, cert, NULL); PK11_FreeSlot(slot); if(privKey) { secStatus = SECSuccess; } } else { - privKey = PK11_FindKeyByAnyCert(connssl->client_cert, proto_win); + privKey = PK11_FindKeyByAnyCert(cert, proto_win); if(privKey) secStatus = SECSuccess; } } - if(secStatus == SECSuccess) { - *pRetCert = connssl->client_cert; - *pRetKey = privKey; - } - else { - if(connssl->client_cert) - CERT_DestroyCertificate(connssl->client_cert); - connssl->client_cert = NULL; - } + *pRetCert = cert; + *pRetKey = privKey; + + /* There's no need to destroy either cert or privKey as + * NSS will do that for us even if returning SECFailure + */ return secStatus; } @@ -912,8 +909,6 @@ void Curl_nss_close(struct connectdata *conn, int sockindex) free(connssl->client_nickname); connssl->client_nickname = NULL; } - if(connssl->client_cert) - CERT_DestroyCertificate(connssl->client_cert); #ifdef HAVE_PK11_CREATEGENERICOBJECT if(connssl->key) (void)PK11_DestroyGenericObject(connssl->key); @@ -957,7 +952,6 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if (connssl->state == ssl_connection_complete) return CURLE_OK; - connssl->client_cert = NULL; #ifdef HAVE_PK11_CREATEGENERICOBJECT connssl->cacert[0] = NULL; connssl->cacert[1] = NULL; -- cgit v1.2.1 From 5f0cae8037e340892372248dfc8b3b5344360087 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Mon, 20 Jul 2009 21:50:21 +0000 Subject: - Claes Jakobsson improved the support for client certificates handling in NSS-powered libcurl. Now the client certificates can be selected automatically by a NSS built-in hook. Additionally pre-login to all PKCS11 slots is no more performed. It used to cause problems with HW tokens. - Fixed reference counting for NSS client certificates. Now the PEM reader module should be always properly unloaded on Curl_nss_cleanup(). If the unload fails though, libcurl will try to reuse the already loaded instance. --- lib/nss.c | 253 ++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 124 insertions(+), 129 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index a976b71ca..c93535ee3 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -585,48 +585,6 @@ static char * nss_get_password(PK11SlotInfo * slot, PRBool retry, void *arg) return (char *)PORT_Strdup((char *)arg); } -static SECStatus nss_Init_Tokens(struct connectdata * conn) -{ - PK11SlotList *slotList; - PK11SlotListElement *listEntry; - SECStatus ret, status = SECSuccess; - - PK11_SetPasswordFunc(nss_get_password); - - slotList = - PK11_GetAllTokens(CKM_INVALID_MECHANISM, PR_FALSE, PR_TRUE, NULL); - - for(listEntry = PK11_GetFirstSafe(slotList); - listEntry; listEntry = listEntry->next) { - PK11SlotInfo *slot = listEntry->slot; - - if(PK11_NeedLogin(slot) && PK11_NeedUserInit(slot)) { - if(slot == PK11_GetInternalKeySlot()) { - failf(conn->data, "The NSS database has not been initialized"); - } - else { - failf(conn->data, "The token %s has not been initialized", - PK11_GetTokenName(slot)); - } - PK11_FreeSlot(slot); - continue; - } - - ret = PK11_Authenticate(slot, PR_TRUE, - conn->data->set.str[STRING_KEY_PASSWD]); - if(SECSuccess != ret) { - if(PR_GetError() == SEC_ERROR_BAD_PASSWORD) - infof(conn->data, "The password for token '%s' is incorrect\n", - PK11_GetTokenName(slot)); - status = SECFailure; - break; - } - PK11_FreeSlot(slot); - } - - return status; -} - static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) { SECStatus success = SECSuccess; @@ -692,15 +650,37 @@ static SECStatus HandshakeCallback(PRFileDesc *sock, void *arg) return SECSuccess; } +static void display_cert_info(struct SessionHandle *data, CERTCertificate *cert) { + char *subject, *issuer, *common_name; + PRExplodedTime printableTime; + char timeString[256]; + PRTime notBefore, notAfter; + + subject = CERT_NameToAscii(&cert->subject); + issuer = CERT_NameToAscii(&cert->issuer); + common_name = CERT_GetCommonName(&cert->subject); + infof(data, "\tsubject: %s\n", subject); + + CERT_GetCertTimes(cert, ¬Before, ¬After); + PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime); + PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); + infof(data, "\tstart date: %s\n", timeString); + PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime); + PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); + infof(data, "\texpire date: %s\n", timeString); + infof(data, "\tcommon name: %s\n", common_name); + infof(data, "\tissuer: %s\n", issuer); + + PR_Free(subject); + PR_Free(issuer); + PR_Free(common_name); +} + static void display_conn_info(struct connectdata *conn, PRFileDesc *sock) { SSLChannelInfo channel; SSLCipherSuiteInfo suite; CERTCertificate *cert; - char *subject, *issuer, *common_name; - PRExplodedTime printableTime; - char timeString[256]; - PRTime notBefore, notAfter; if(SSL_GetChannelInfo(sock, &channel, sizeof channel) == SECSuccess && channel.length == sizeof channel && @@ -714,25 +694,7 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc *sock) infof(conn->data, "Server certificate:\n"); cert = SSL_PeerCertificate(sock); - subject = CERT_NameToAscii(&cert->subject); - issuer = CERT_NameToAscii(&cert->issuer); - common_name = CERT_GetCommonName(&cert->subject); - infof(conn->data, "\tsubject: %s\n", subject); - - CERT_GetCertTimes(cert, ¬Before, ¬After); - PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime); - PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); - infof(conn->data, "\tstart date: %s\n", timeString); - PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime); - PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); - infof(conn->data, "\texpire date: %s\n", timeString); - infof(conn->data, "\tcommon name: %s\n", common_name); - infof(conn->data, "\tissuer: %s\n", issuer); - - PR_Free(subject); - PR_Free(issuer); - PR_Free(common_name); - + display_cert_info(conn->data, cert); CERT_DestroyCertificate(cert); return; @@ -786,48 +748,71 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, struct CERTCertificateStr **pRetCert, struct SECKEYPrivateKeyStr **pRetKey) { - SECKEYPrivateKey *privKey = NULL; - CERTCertificate *cert; - struct ssl_connect_data *connssl = (struct ssl_connect_data *) arg; - char *nickname = connssl->client_nickname; - void *proto_win = NULL; - SECStatus secStatus = SECFailure; - PK11SlotInfo *slot; - (void)caNames; + static const char pem_nickname[] = "PEM Token #1"; + const char *pem_slotname = pem_nickname; - proto_win = SSL_RevealPinArg(sock); + struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg; + struct SessionHandle *data = connssl->data; + const char *nickname = connssl->client_nickname; - if(!nickname) - return secStatus; - - cert = PK11_FindCertFromNickname(nickname, proto_win); - if(cert) { - if(!strncmp(nickname, "PEM Token", 9)) { - CK_SLOT_ID slotID = 1; /* hardcoded for now */ - char slotname[SLOTSIZE]; - snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID); - slot = PK11_FindSlotByName(slotname); - privKey = PK11_FindPrivateKeyFromCert(slot, cert, NULL); - PK11_FreeSlot(slot); - if(privKey) { - secStatus = SECSuccess; - } + if (mod && nickname && + 0 == strncmp(nickname, pem_nickname, /* length of "PEM Token" */ 9)) { + + /* use the cert/key provided by PEM reader */ + PK11SlotInfo *slot; + void *proto_win = SSL_RevealPinArg(sock); + *pRetKey = NULL; + + *pRetCert = PK11_FindCertFromNickname(nickname, proto_win); + if (NULL == *pRetCert) { + failf(data, "NSS: client certificate not found: %s", nickname); + return SECFailure; } - else { - privKey = PK11_FindKeyByAnyCert(cert, proto_win); - if(privKey) - secStatus = SECSuccess; + + slot = PK11_FindSlotByName(pem_slotname); + if (NULL == slot) { + failf(data, "NSS: PK11 slot not found: %s", pem_slotname); + return SECFailure; } + + *pRetKey = PK11_FindPrivateKeyFromCert(slot, *pRetCert, NULL); + PK11_FreeSlot(slot); + if (NULL == *pRetKey) { + failf(data, "NSS: private key not found for certificate: %s", nickname); + return SECFailure; + } + + infof(data, "NSS: Client client certificate: %s\n", nickname); + display_cert_info(data, *pRetCert); + return SECSuccess; } - *pRetCert = cert; - *pRetKey = privKey; - - /* There's no need to destroy either cert or privKey as - * NSS will do that for us even if returning SECFailure - */ + /* use the default NSS hook */ + if (SECSuccess != NSS_GetClientAuthData((void *)nickname, sock, caNames, + pRetCert, pRetKey) + || NULL == *pRetCert) { + + if (NULL == nickname) + failf(data, "NSS: client certificate not found (nickname not specified)"); + else + failf(data, "NSS: client certificate not found: %s", nickname); + + return SECFailure; + } + + /* get certificate nickname if any */ + nickname = (*pRetCert)->nickname; + if (NULL == nickname) + nickname = "[unknown]"; - return secStatus; + if (NULL == *pRetKey) { + failf(data, "NSS: private key not found for certificate: %s", nickname); + return SECFailure; + } + + infof(data, "NSS: using client certificate: %s\n", nickname); + display_cert_info(data, *pRetCert); + return SECSuccess; } /** @@ -857,9 +842,15 @@ void Curl_nss_cleanup(void) */ PR_Lock(nss_initlock); if (initialized) { - if(mod) + /* Free references to client certificates held in the SSL session cache. + * Omitting this hampers destruction of the security module owning + * the certificates. */ + SSL_ClearSessionCache(); + + if(mod && SECSuccess == SECMOD_UnloadUserModule(mod)) { SECMOD_DestroyModule(mod); - mod = NULL; + mod = NULL; + } NSS_Shutdown(); } PR_Unlock(nss_initlock); @@ -940,9 +931,6 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; SECStatus rv; -#ifdef HAVE_PK11_CREATEGENERICOBJECT - char *configstring = NULL; -#endif char *certDir = NULL; int curlerr; const int *cipher_to_enable; @@ -952,6 +940,8 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if (connssl->state == ssl_connection_complete) return CURLE_OK; + connssl->data = data; + #ifdef HAVE_PK11_CREATEGENERICOBJECT connssl->cacert[0] = NULL; connssl->cacert[1] = NULL; @@ -995,23 +985,28 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) NSS_SetDomesticPolicy(); #ifdef HAVE_PK11_CREATEGENERICOBJECT - configstring = aprintf("library=%s name=PEM", pem_library); - if(!configstring) { - PR_Unlock(nss_initlock); - goto error; - } - mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); - free(configstring); + if(!mod) { + char *configstring = aprintf("library=%s name=PEM", pem_library); + if(!configstring) { + PR_Unlock(nss_initlock); + goto error; + } + mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); + free(configstring); - if(!mod || !mod->loaded) { - if(mod) { - SECMOD_DestroyModule(mod); - mod = NULL; + if(!mod || !mod->loaded) { + if(mod) { + SECMOD_DestroyModule(mod); + mod = NULL; + } + infof(data, "WARNING: failed to load NSS PEM library %s. Using OpenSSL " + "PEM certificates will not work.\n", pem_library); } - infof(data, "WARNING: failed to load NSS PEM library %s. Using OpenSSL " - "PEM certificates will not work.\n", pem_library); } #endif + + PK11_SetPasswordFunc(nss_get_password); + } PR_Unlock(nss_initlock); @@ -1159,11 +1154,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) else { nickname = data->set.str[STRING_CERT]; } - if(nss_Init_Tokens(conn) != SECSuccess) { - if(nickname_alloc) - free(nickname); - goto error; - } + if(!cert_stuff(conn, sockindex, data->set.str[STRING_CERT], data->set.str[STRING_KEY])) { /* failf() is already done in cert_stuff() */ @@ -1178,16 +1169,15 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if(!connssl->client_nickname) return CURLE_OUT_OF_MEMORY; - if(SSL_GetClientAuthDataHook(model, - (SSLGetClientAuthData) SelectClientCert, - (void *)connssl) != SECSuccess) { - curlerr = CURLE_SSL_CERTPROBLEM; - goto error; - } } else connssl->client_nickname = NULL; + if(SSL_GetClientAuthDataHook(model, SelectClientCert, + (void *)connssl) != SECSuccess) { + curlerr = CURLE_SSL_CERTPROBLEM; + goto error; + } /* Import our model socket onto the existing file descriptor */ connssl->handle = PR_ImportTCPSocket(sockfd); @@ -1196,6 +1186,11 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) goto error; PR_Close(model); /* We don't need this any more */ + /* 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]); + } + /* Force handshake on next I/O */ SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE); -- cgit v1.2.1 From 6293fe98a030dc776f38dec97e8241cb09cdd170 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Thu, 13 Aug 2009 16:04:51 +0000 Subject: - Changed NSS code to not ignore the value of ssl.verifyhost and produce more verbose error messages. Originally reported at: https://bugzilla.redhat.com/show_bug.cgi?id=516056 --- lib/nss.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index c93535ee3..6ee655678 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -615,16 +615,26 @@ static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) issuer); break; case SSL_ERROR_BAD_CERT_DOMAIN: - if(conn->data->set.ssl.verifypeer) + if(conn->data->set.ssl.verifyhost) { + failf(conn->data, "common name '%s' does not match '%s'", + subject, conn->host.dispname); success = SECFailure; - infof(conn->data, "common name: %s (does not match '%s')\n", - subject, conn->host.dispname); + } else { + infof(conn->data, "warning: common name '%s' does not match '%s'\n", + subject, 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; @@ -1067,6 +1077,9 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) } } + if(data->set.ssl.verifyhost == 1) + infof(data, "warning: ignoring unsupported value (1) of ssl.verifyhost\n"); + data->set.ssl.certverifyresult=0; /* not checked yet */ if(SSL_BadCertHook(model, (SSLBadCertHandler) BadCertHandler, conn) != SECSuccess) { @@ -1200,7 +1213,9 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if(SSL_ForceHandshakeWithTimeout(connssl->handle, PR_SecondsToInterval(HANDSHAKE_TIMEOUT)) != SECSuccess) { - if(conn->data->set.ssl.certverifyresult!=0) + if(conn->data->set.ssl.certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN) + curlerr = CURLE_PEER_FAILED_VERIFICATION; + else if(conn->data->set.ssl.certverifyresult!=0) curlerr = CURLE_SSL_CACERT; goto error; } -- cgit v1.2.1 From 1a255e0e280f6ca48a4f2290642ede2966c007da Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Fri, 28 Aug 2009 12:06:51 +0000 Subject: - Improved error message for not matching certificate subject name in libcurl-NSS. Originally reported at: https://bugzilla.redhat.com/show_bug.cgi?id=516056#c9 --- lib/nss.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 6ee655678..02fa06d9c 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -591,7 +591,7 @@ static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) struct connectdata *conn = (struct connectdata *)arg; PRErrorCode err = PR_GetError(); CERTCertificate *cert = NULL; - char *subject, *issuer; + char *subject, *subject_cn, *issuer; if(conn->data->set.ssl.certverifyresult!=0) return success; @@ -599,6 +599,7 @@ static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) conn->data->set.ssl.certverifyresult=err; cert = SSL_PeerCertificate(sock); subject = CERT_NameToAscii(&cert->subject); + subject_cn = CERT_GetCommonName(&cert->subject); issuer = CERT_NameToAscii(&cert->issuer); CERT_DestroyCertificate(cert); @@ -616,12 +617,12 @@ static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) break; case SSL_ERROR_BAD_CERT_DOMAIN: if(conn->data->set.ssl.verifyhost) { - failf(conn->data, "common name '%s' does not match '%s'", - subject, conn->host.dispname); + failf(conn->data, "SSL: certificate subject name '%s' does not match " + "target host name '%s'", subject_cn, conn->host.dispname); success = SECFailure; } else { - infof(conn->data, "warning: common name '%s' does not match '%s'\n", - subject, conn->host.dispname); + 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: @@ -645,6 +646,7 @@ static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) if(success == SECSuccess) infof(conn->data, "SSL certificate verify ok.\n"); PR_Free(subject); + PR_Free(subject_cn); PR_Free(issuer); return success; -- cgit v1.2.1 From 2786ecaeefc42418875c9e0a064991ce32bba34f Mon Sep 17 00:00:00 2001 From: Gunter Knauf Date: Sun, 6 Sep 2009 17:59:46 +0000 Subject: added base64.h include to silent warnings about missing prototype for ATOB_ConvertAsciiToItem. --- lib/nss.c | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 02fa06d9c..15942a3e6 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -63,6 +63,7 @@ #include #include #include +#include #include "curl_memory.h" #include "rawstr.h" -- cgit v1.2.1 From 56a161e09a54727812440442758338a15b746459 Mon Sep 17 00:00:00 2001 From: Gunter Knauf Date: Sun, 6 Sep 2009 19:45:08 +0000 Subject: use our define struct_stat to be compatible with largefile support. --- lib/nss.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 15942a3e6..d70d1beaf 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -266,7 +266,7 @@ static int num_enabled_ciphers(void) */ static int is_file(const char *filename) { - struct stat st; + struct_stat st; if(filename == NULL) return 0; @@ -968,7 +968,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) certDir = getenv("SSL_DIR"); /* Look in $SSL_DIR */ if(!certDir) { - struct stat st; + struct_stat st; if(stat(SSL_DIR, &st) == 0) if(S_ISDIR(st.st_mode)) { @@ -1104,7 +1104,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) } } else if(data->set.ssl.CApath) { - struct stat st; + struct_stat st; PRDir *dir; PRDirEntry *entry; -- cgit v1.2.1 From 5e3796349a1974e80bfedc2bdd4c336bf6e164c7 Mon Sep 17 00:00:00 2001 From: Gunter Knauf Date: Sun, 6 Sep 2009 20:16:59 +0000 Subject: added casts to silent compiler warning on 64bit systems. --- lib/nss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index d70d1beaf..9353879f7 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1283,7 +1283,7 @@ int Curl_nss_send(struct connectdata *conn, /* connection data */ int rc; if(data->set.timeout) - timeout = PR_MillisecondsToInterval(data->set.timeout); + timeout = PR_MillisecondsToInterval((PRUint32)data->set.timeout); else timeout = PR_MillisecondsToInterval(DEFAULT_CONNECT_TIMEOUT); @@ -1319,7 +1319,7 @@ ssize_t Curl_nss_recv(struct connectdata * conn, /* connection data */ PRInt32 timeout; if(data->set.timeout) - timeout = PR_SecondsToInterval(data->set.timeout); + timeout = PR_SecondsToInterval((PRUint32)data->set.timeout); else timeout = PR_MillisecondsToInterval(DEFAULT_CONNECT_TIMEOUT); -- cgit v1.2.1 From 5d4a1e245b505283b1c3ec44e0a781353b335637 Mon Sep 17 00:00:00 2001 From: Gunter Knauf Date: Tue, 8 Sep 2009 01:13:49 +0000 Subject: added debug output for NSS certpath. --- lib/nss.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 9353879f7..ae0730479 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -978,6 +978,8 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if (!NSS_IsInitialized()) { initialized = 1; + infof(conn->data, "Initializing NSS with certpath: %s\n", + certDir ? certDir : "none"); if(!certDir) { rv = NSS_NoDB_Init(NULL); } -- cgit v1.2.1 From 40027148255c6fc07c7c6edb059020350a513720 Mon Sep 17 00:00:00 2001 From: Gunter Knauf Date: Mon, 21 Sep 2009 22:46:38 +0000 Subject: added aditional check for the directory specified with SSL_DIR, and fall back to hardcoded directory if not a valid directory. --- lib/nss.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index ae0730479..dbb9a57fa 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -964,16 +964,23 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) /* FIXME. NSS doesn't support multiple databases open at the same time. */ PR_Lock(nss_initlock); if(!initialized) { + struct_stat st; - certDir = getenv("SSL_DIR"); /* Look in $SSL_DIR */ + /* First we check if $SSL_DIR points to a valid dir */ + certDir = getenv("SSL_DIR"); + if(certDir) { + if((stat(certDir, &st) != 0) || + (!S_ISDIR(st.st_mode))) { + certDir = NULL; + } + } + /* Now we check if the default location is a valid dir */ if(!certDir) { - struct_stat st; - - if(stat(SSL_DIR, &st) == 0) - if(S_ISDIR(st.st_mode)) { - certDir = (char *)SSL_DIR; - } + if((stat(SSL_DIR, &st) == 0) && + (S_ISDIR(st.st_mode))) { + certDir = (char *)SSL_DIR; + } } if (!NSS_IsInitialized()) { -- cgit v1.2.1 From 9448659fc60a272cdd6dec0e2ee18dbadf1e3a88 Mon Sep 17 00:00:00 2001 From: Gunter Knauf Date: Mon, 21 Sep 2009 22:52:59 +0000 Subject: added support for new SQLite cert database format: added a runtime check for version 3.12.0, and depending on the result add 'sql:' prefix to cert database directory so that newer SQLIte database format works. --- lib/nss.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index dbb9a57fa..d6f3fcd89 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -991,8 +991,11 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) rv = NSS_NoDB_Init(NULL); } else { - rv = NSS_Initialize(certDir, NULL, NULL, "secmod.db", - NSS_INIT_READONLY); + char *certpath = PR_smprintf("%s%s", + NSS_VersionCheck("3.12.0") ? "sql:" : "", + certDir); + rv = NSS_Initialize(certpath, "", "", "", NSS_INIT_READONLY); + PR_smprintf_free(certpath); } if(rv != SECSuccess) { infof(conn->data, "Unable to initialize NSS database\n"); -- cgit v1.2.1 From b38e28b6bc02c468bd4be4bb04a8bf5220b929f3 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Wed, 7 Oct 2009 20:34:08 +0000 Subject: fix gcc warnings in lib/nss.c --- lib/nss.c | 60 +++++++++++++++++++++++++++--------------------------------- 1 file changed, 27 insertions(+), 33 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index d6f3fcd89..abc1a4c31 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -278,6 +278,24 @@ static int is_file(const char *filename) return 0; } +static char *fmt_nickname(char *str, bool *nickname_alloc) +{ + char *nickname = NULL; + *nickname_alloc = FALSE; + + if(is_file(str)) { + char *n = strrchr(str, '/'); + if(n) { + *nickname_alloc = TRUE; + n++; /* skip last slash */ + nickname = aprintf("PEM Token #%d:%s", 1, n); + } + return nickname; + } + + return str; +} + static int nss_load_cert(struct ssl_connect_data *ssl, const char *filename, PRBool cacert) { @@ -795,7 +813,7 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, return SECFailure; } - infof(data, "NSS: Client client certificate: %s\n", nickname); + infof(data, "NSS: client certificate: %s\n", nickname); display_cert_info(data, *pRetCert); return SECSuccess; } @@ -1164,24 +1182,10 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) } if(data->set.str[STRING_CERT]) { - char *n; - char *nickname; bool nickname_alloc = FALSE; - - if(is_file(data->set.str[STRING_CERT])) { - n = strrchr(data->set.str[STRING_CERT], '/'); - if(n) { - n++; /* skip last slash */ - nickname = aprintf("PEM Token #%d:%s", 1, n); - if(!nickname) - return CURLE_OUT_OF_MEMORY; - - nickname_alloc = TRUE; - } - } - else { - nickname = data->set.str[STRING_CERT]; - } + char *nickname = fmt_nickname(data->set.str[STRING_CERT], &nickname_alloc); + if(!nickname) + return CURLE_OUT_OF_MEMORY; if(!cert_stuff(conn, sockindex, data->set.str[STRING_CERT], data->set.str[STRING_KEY])) { @@ -1240,23 +1244,13 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) display_conn_info(conn, connssl->handle); if (data->set.str[STRING_SSL_ISSUERCERT]) { - char *n; - char *nickname; - bool nickname_alloc = FALSE; SECStatus ret; + bool nickname_alloc = FALSE; + char *nickname = fmt_nickname(data->set.str[STRING_SSL_ISSUERCERT], + &nickname_alloc); - if(is_file(data->set.str[STRING_SSL_ISSUERCERT])) { - n = strrchr(data->set.str[STRING_SSL_ISSUERCERT], '/'); - if (n) { - n++; /* skip last slash */ - nickname = aprintf("PEM Token #%d:%s", 1, n); - if(!nickname) - return CURLE_OUT_OF_MEMORY; - nickname_alloc = TRUE; - } - } - else - nickname = data->set.str[STRING_SSL_ISSUERCERT]; + if(!nickname) + return CURLE_OUT_OF_MEMORY; ret = check_issuer_cert(connssl->handle, nickname); -- cgit v1.2.1 From 167a92810a77b3abd973ea987a1306cfa155d65c Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 18 Oct 2009 00:10:13 +0000 Subject: - Kevin Baughman found a double close() problem with libcurl-NSS, as when libcurl called NSS to close the SSL "session" it also closed the actual socket. --- lib/nss.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index abc1a4c31..866b1d0c8 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -927,11 +927,15 @@ void Curl_nss_close(struct connectdata *conn, int sockindex) if(connssl->handle) { PR_Close(connssl->handle); + + /* NSS closes the socket we previously handed to it, so we must mark it + as closed to avoid double close */ + conn->sock[sockindex] = CURL_SOCKET_BAD; if(connssl->client_nickname != NULL) { free(connssl->client_nickname); connssl->client_nickname = NULL; } -#ifdef HAVE_PK11_CREATEGENERICOBJECT +#ifdef HAVE_PK11_CREATEGENERICOBJECT if(connssl->key) (void)PK11_DestroyGenericObject(connssl->key); if(connssl->cacert[1]) @@ -973,7 +977,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) connssl->data = data; -#ifdef HAVE_PK11_CREATEGENERICOBJECT +#ifdef HAVE_PK11_CREATEGENERICOBJECT connssl->cacert[0] = NULL; connssl->cacert[1] = NULL; connssl->key = NULL; -- cgit v1.2.1 From 6a79b0e8591ec94adcc49809bf1ab8cf66f1bb41 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 28 Oct 2009 20:30:23 +0000 Subject: Since the NSS lib closes the socket the memory tracking system wrongly gets a false positive on a leaked socket, so this introduces a way to tell the system that the socket is indeed closed without explicitly closing it! --- lib/nss.c | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 866b1d0c8..7408585da 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -930,6 +930,7 @@ void Curl_nss_close(struct connectdata *conn, int sockindex) /* 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) { free(connssl->client_nickname); -- cgit v1.2.1 From 676e0c28e7f6c3565d500ed17c0605359595016e Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Thu, 5 Nov 2009 15:41:31 +0000 Subject: - Dropped misleading timeouts in libcurl-NSS and made sure the SSL socket works in non-blocking mode. --- lib/nss.c | 47 +++++++++++++---------------------------------- 1 file changed, 13 insertions(+), 34 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 7408585da..10d07def8 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -83,8 +83,6 @@ PRLock * nss_initlock = NULL; volatile int initialized = 0; -#define HANDSHAKE_TIMEOUT 30 - typedef struct { const char *name; int num; @@ -970,6 +968,8 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) char *certDir = NULL; int curlerr; const int *cipher_to_enable; + PRSocketOptionData sock_opt; + PRUint32 timeout; curlerr = CURLE_SSL_CONNECT_ERROR; @@ -1063,6 +1063,12 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) goto error; model = SSL_ImportFD(NULL, model); + /* make the socket nonblocking */ + sock_opt.option = PR_SockOpt_Nonblocking; + sock_opt.value.non_blocking = PR_TRUE; + if(PR_SetSocketOption(model, &sock_opt) != SECSuccess) + goto error; + if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess) goto error; if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess) @@ -1234,9 +1240,8 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) SSL_SetURL(connssl->handle, conn->host.name); /* Force the handshake now */ - if(SSL_ForceHandshakeWithTimeout(connssl->handle, - PR_SecondsToInterval(HANDSHAKE_TIMEOUT)) - != SECSuccess) { + timeout = PR_MillisecondsToInterval(Curl_timeleft(conn, NULL, TRUE)); + if(SSL_ForceHandshakeWithTimeout(connssl->handle, timeout) != SECSuccess) { if(conn->data->set.ssl.certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN) curlerr = CURLE_PEER_FAILED_VERIFICATION; else if(conn->data->set.ssl.certverifyresult!=0) @@ -1288,27 +1293,12 @@ int Curl_nss_send(struct connectdata *conn, /* connection data */ const void *mem, /* send this data */ size_t len) /* amount to write */ { - PRInt32 err; - struct SessionHandle *data = conn->data; - PRInt32 timeout; int rc; - if(data->set.timeout) - timeout = PR_MillisecondsToInterval((PRUint32)data->set.timeout); - else - timeout = PR_MillisecondsToInterval(DEFAULT_CONNECT_TIMEOUT); - - rc = PR_Send(conn->ssl[sockindex].handle, mem, (int)len, 0, timeout); + rc = PR_Send(conn->ssl[sockindex].handle, mem, (int)len, 0, -1); if(rc < 0) { - err = PR_GetError(); - - if(err == PR_IO_TIMEOUT_ERROR) { - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } - - failf(conn->data, "SSL write: error %d", err); + failf(conn->data, "SSL write: error %d", PR_GetError()); return -1; } return rc; /* number of bytes */ @@ -1326,15 +1316,8 @@ ssize_t Curl_nss_recv(struct connectdata * conn, /* connection data */ bool * wouldblock) { ssize_t nread; - struct SessionHandle *data = conn->data; - PRInt32 timeout; - if(data->set.timeout) - timeout = PR_SecondsToInterval((PRUint32)data->set.timeout); - else - timeout = PR_MillisecondsToInterval(DEFAULT_CONNECT_TIMEOUT); - - nread = PR_Recv(conn->ssl[num].handle, buf, (int)buffersize, 0, timeout); + nread = PR_Recv(conn->ssl[num].handle, buf, (int)buffersize, 0, -1); *wouldblock = FALSE; if(nread < 0) { /* failed SSL read */ @@ -1344,10 +1327,6 @@ ssize_t Curl_nss_recv(struct connectdata * conn, /* connection data */ *wouldblock = TRUE; return -1; /* basically EWOULDBLOCK */ } - if(err == PR_IO_TIMEOUT_ERROR) { - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEDOUT; - } failf(conn->data, "SSL read: errno %d", err); return -1; } -- cgit v1.2.1 From d547d00f2cfa69e07d6873bbebbc82f3132f1b82 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Thu, 12 Nov 2009 10:54:10 +0000 Subject: - Kevin Baughman provided a fix preventing libcurl-NSS from crash on doubly closed NSPR descriptor. The issue was hard to find, reported several times before and always closed unresolved. More info at the RH bug: https://bugzilla.redhat.com/534176 --- lib/nss.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 10d07def8..5c1340fa9 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1227,7 +1227,9 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) connssl->handle = SSL_ImportFD(model, connssl->handle); if(!connssl->handle) goto error; + PR_Close(model); /* We don't need this any more */ + model = NULL; /* This is the password associated with the cert that we're using */ if (data->set.str[STRING_KEY_PASSWD]) { -- cgit v1.2.1 From 571309dc3edaf1c03ff6fdfbcf551875644b5a7f Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Thu, 12 Nov 2009 11:16:31 +0000 Subject: - libcurl-NSS now tries to reconnect with TLS disabled in case it detects a broken TLS server. However it does not happen if SSL version is selected manually. The approach was originally taken from PSM. Kaspar Brand helped me to complete the patch. Original bug reports: https://bugzilla.redhat.com/525496 https://bugzilla.redhat.com/527771 --- lib/nss.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 5c1340fa9..893ca2edc 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -844,6 +844,36 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, return SECSuccess; } +/* This function is supposed to decide, which error codes should be used + * to conclude server is TLS intolerant. + * + * taken from xulrunner - nsNSSIOLayer.cpp + */ +static PRBool +isTLSIntoleranceError(PRInt32 err) +{ + switch (err) { + case SSL_ERROR_BAD_MAC_ALERT: + case SSL_ERROR_BAD_MAC_READ: + case SSL_ERROR_HANDSHAKE_FAILURE_ALERT: + case SSL_ERROR_HANDSHAKE_UNEXPECTED_ALERT: + case SSL_ERROR_CLIENT_KEY_EXCHANGE_FAILURE: + case SSL_ERROR_ILLEGAL_PARAMETER_ALERT: + case SSL_ERROR_NO_CYPHER_OVERLAP: + case SSL_ERROR_BAD_SERVER: + case SSL_ERROR_BAD_BLOCK_PADDING: + case SSL_ERROR_UNSUPPORTED_VERSION: + case SSL_ERROR_PROTOCOL_VERSION_ALERT: + case SSL_ERROR_RX_MALFORMED_FINISHED: + case SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE: + case SSL_ERROR_DECODE_ERROR_ALERT: + case SSL_ERROR_RX_UNKNOWN_ALERT: + return PR_TRUE; + default: + return PR_FALSE; + } +} + /** * Global SSL init * @@ -1081,7 +1111,11 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) switch (data->set.ssl.version) { default: case CURL_SSLVERSION_DEFAULT: - ssl3 = tlsv1 = PR_TRUE; + ssl3 = PR_TRUE; + if (data->state.ssl_connect_retry) + infof(data, "TLS disabled due to previous handshake failure\n"); + else + tlsv1 = PR_TRUE; break; case CURL_SSLVERSION_TLSv1: tlsv1 = PR_TRUE; @@ -1104,6 +1138,9 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if(SSL_OptionSet(model, SSL_V2_COMPATIBLE_HELLO, ssl2) != SECSuccess) goto error; + /* reset the flag to avoid an infinite loop */ + data->state.ssl_connect_retry = FALSE; + /* enable all ciphers from enable_ciphers_by_default */ cipher_to_enable = enable_ciphers_by_default; while (SSL_NULL_WITH_NULL_NULL != *cipher_to_enable) { @@ -1282,10 +1319,21 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) return CURLE_OK; error: + /* reset the flag to avoid an infinite loop */ + data->state.ssl_connect_retry = FALSE; + err = PR_GetError(); infof(data, "NSS error %d\n", err); if(model) PR_Close(model); + + if (ssl3 && tlsv1 && isTLSIntoleranceError(err)) { + /* schedule reconnect through Curl_retry_request() */ + data->state.ssl_connect_retry = TRUE; + infof(data, "Error in TLS handshake, trying SSLv3...\n"); + return CURLE_OK; + } + return curlerr; } -- cgit v1.2.1 From fb2425b1479c124f7c8b88cde8be91053c036264 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Wed, 2 Dec 2009 17:24:38 +0000 Subject: lib/nss.c: avoid use of uninitialized value --- lib/nss.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 893ca2edc..30a55faf0 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -990,7 +990,9 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) { PRInt32 err; PRFileDesc *model = NULL; - PRBool ssl2, ssl3, tlsv1; + PRBool ssl2 = PR_FALSE; + PRBool ssl3 = PR_FALSE; + PRBool tlsv1 = PR_FALSE; struct SessionHandle *data = conn->data; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; @@ -1106,8 +1108,6 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess) goto error; - ssl2 = ssl3 = tlsv1 = PR_FALSE; - switch (data->set.ssl.version) { default: case CURL_SSLVERSION_DEFAULT: -- cgit v1.2.1 From 23bab783d4e8b01ce3084f06a5d43a96cd00decd Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 17 Feb 2010 12:13:55 +0000 Subject: use curl standard indentation and line lengths --- lib/nss.c | 114 ++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 59 insertions(+), 55 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 30a55faf0..d3f31bddb 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -89,8 +89,8 @@ typedef struct { PRInt32 version; /* protocol version valid for this cipher */ } cipher_s; -#define PK11_SETATTRS(x,id,v,l) (x)->type = (id); \ - (x)->pValue=(v); (x)->ulValueLen = (l) +#define PK11_SETATTRS(x,id,v,l) (x)->type = (id); \ + (x)->pValue=(v); (x)->ulValueLen = (l) #define CERT_NewTempCertificate __CERT_NewTempCertificate @@ -278,20 +278,20 @@ static int is_file(const char *filename) static char *fmt_nickname(char *str, bool *nickname_alloc) { - char *nickname = NULL; - *nickname_alloc = FALSE; - - if(is_file(str)) { - char *n = strrchr(str, '/'); - if(n) { - *nickname_alloc = TRUE; - n++; /* skip last slash */ - nickname = aprintf("PEM Token #%d:%s", 1, n); - } - return nickname; + char *nickname = NULL; + *nickname_alloc = FALSE; + + if(is_file(str)) { + char *n = strrchr(str, '/'); + if(n) { + *nickname_alloc = TRUE; + n++; /* skip last slash */ + nickname = aprintf("PEM Token #%d:%s", 1, n); } + return nickname; + } - return str; + return str; } static int nss_load_cert(struct ssl_connect_data *ssl, @@ -375,7 +375,7 @@ static int nss_load_cert(struct ssl_connect_data *ssl, * slot. */ ssl->cacert[slotID] = PK11_CreateGenericObject(slot, theTemplate, 4, - PR_FALSE /* isPerm */); + PR_FALSE /* isPerm */); PK11_FreeSlot(slot); @@ -390,7 +390,7 @@ static int nss_load_cert(struct ssl_connect_data *ssl, return 0; #endif -done: + done: /* Double-check that the certificate or nickname requested exists in * either the token or the NSS certificate database. */ @@ -486,7 +486,8 @@ static int nss_load_crl(const char* crlfilename, PRBool ascii) return 1; } -static int nss_load_key(struct connectdata *conn, int sockindex, char *key_file) +static int nss_load_key(struct connectdata *conn, int sockindex, + char *key_file) { #ifdef HAVE_PK11_CREATEGENERICOBJECT PK11SlotInfo * slot = NULL; @@ -679,30 +680,32 @@ static SECStatus HandshakeCallback(PRFileDesc *sock, void *arg) return SECSuccess; } -static void display_cert_info(struct SessionHandle *data, CERTCertificate *cert) { - char *subject, *issuer, *common_name; - PRExplodedTime printableTime; - char timeString[256]; - PRTime notBefore, notAfter; - - subject = CERT_NameToAscii(&cert->subject); - issuer = CERT_NameToAscii(&cert->issuer); - common_name = CERT_GetCommonName(&cert->subject); - infof(data, "\tsubject: %s\n", subject); - - CERT_GetCertTimes(cert, ¬Before, ¬After); - PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime); - PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); - infof(data, "\tstart date: %s\n", timeString); - PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime); - PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); - infof(data, "\texpire date: %s\n", timeString); - infof(data, "\tcommon name: %s\n", common_name); - infof(data, "\tissuer: %s\n", issuer); - - PR_Free(subject); - PR_Free(issuer); - PR_Free(common_name); +static void display_cert_info(struct SessionHandle *data, + CERTCertificate *cert) +{ + char *subject, *issuer, *common_name; + PRExplodedTime printableTime; + char timeString[256]; + PRTime notBefore, notAfter; + + subject = CERT_NameToAscii(&cert->subject); + issuer = CERT_NameToAscii(&cert->issuer); + common_name = CERT_GetCommonName(&cert->subject); + infof(data, "\tsubject: %s\n", subject); + + CERT_GetCertTimes(cert, ¬Before, ¬After); + PR_ExplodeTime(notBefore, PR_GMTParameters, &printableTime); + PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); + infof(data, "\tstart date: %s\n", timeString); + PR_ExplodeTime(notAfter, PR_GMTParameters, &printableTime); + PR_FormatTime(timeString, 256, "%b %d %H:%M:%S %Y GMT", &printableTime); + infof(data, "\texpire date: %s\n", timeString); + infof(data, "\tcommon name: %s\n", common_name); + infof(data, "\tissuer: %s\n", issuer); + + PR_Free(subject); + PR_Free(issuer); + PR_Free(common_name); } static void display_conn_info(struct connectdata *conn, PRFileDesc *sock) @@ -744,9 +747,9 @@ static SECStatus check_issuer_cert(PRFileDesc *sock, void *proto_win = NULL; /* - PRArenaPool *tmpArena = NULL; - CERTAuthKeyID *authorityKeyID = NULL; - SECITEM *caname = NULL; + PRArenaPool *tmpArena = NULL; + CERTAuthKeyID *authorityKeyID = NULL; + SECITEM *caname = NULL; */ cert = SSL_PeerCertificate(sock); @@ -822,7 +825,8 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, || NULL == *pRetCert) { if (NULL == nickname) - failf(data, "NSS: client certificate not found (nickname not specified)"); + failf(data, "NSS: client certificate not found (nickname not " + "specified)"); else failf(data, "NSS: client certificate not found: %s", nickname); @@ -1025,7 +1029,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) certDir = getenv("SSL_DIR"); if(certDir) { if((stat(certDir, &st) != 0) || - (!S_ISDIR(st.st_mode))) { + (!S_ISDIR(st.st_mode))) { certDir = NULL; } } @@ -1033,7 +1037,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) /* Now we check if the default location is a valid dir */ if(!certDir) { if((stat(SSL_DIR, &st) == 0) && - (S_ISDIR(st.st_mode))) { + (S_ISDIR(st.st_mode))) { certDir = (char *)SSL_DIR; } } @@ -1047,8 +1051,8 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) } else { char *certpath = PR_smprintf("%s%s", - NSS_VersionCheck("3.12.0") ? "sql:" : "", - certDir); + NSS_VersionCheck("3.12.0") ? "sql:" : "", + certDir); rv = NSS_Initialize(certpath, "", "", "", NSS_INIT_READONLY); PR_smprintf_free(certpath); } @@ -1079,8 +1083,8 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) SECMOD_DestroyModule(mod); mod = NULL; } - infof(data, "WARNING: failed to load NSS PEM library %s. Using OpenSSL " - "PEM certificates will not work.\n", pem_library); + infof(data, "WARNING: failed to load NSS PEM library %s. Using " + "OpenSSL PEM certificates will not work.\n", pem_library); } } #endif @@ -1236,7 +1240,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) return CURLE_OUT_OF_MEMORY; if(!cert_stuff(conn, sockindex, data->set.str[STRING_CERT], - data->set.str[STRING_KEY])) { + data->set.str[STRING_KEY])) { /* failf() is already done in cert_stuff() */ if(nickname_alloc) free(nickname); @@ -1270,7 +1274,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) /* 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]); + SSL_SetPKCS11PinArg(connssl->handle, data->set.str[STRING_KEY_PASSWD]); } /* Force handshake on next I/O */ @@ -1318,7 +1322,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) return CURLE_OK; -error: + error: /* reset the flag to avoid an infinite loop */ data->state.ssl_connect_retry = FALSE; -- cgit v1.2.1 From 2309b4e330b96bc2e1f8e36b6184015e59544037 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 24 Mar 2010 11:02:54 +0100 Subject: remove the CVSish $Id$ lines --- lib/nss.c | 1 - 1 file changed, 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index d3f31bddb..bff539490 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -18,7 +18,6 @@ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * - * $Id$ ***************************************************************************/ /* -- cgit v1.2.1 From 7b913444ec68d00c96c1ea0926fbeb07c8ea19b2 Mon Sep 17 00:00:00 2001 From: Guenter Knauf Date: Wed, 31 Mar 2010 02:09:49 +0200 Subject: fix compiler warning with a cast. --- lib/nss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index bff539490..2babfdf1d 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1282,7 +1282,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) SSL_SetURL(connssl->handle, conn->host.name); /* Force the handshake now */ - timeout = PR_MillisecondsToInterval(Curl_timeleft(conn, NULL, TRUE)); + timeout = PR_MillisecondsToInterval((PRUint32)Curl_timeleft(conn, NULL, TRUE)); if(SSL_ForceHandshakeWithTimeout(connssl->handle, timeout) != SECSuccess) { if(conn->data->set.ssl.certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN) curlerr = CURLE_PEER_FAILED_VERIFICATION; -- cgit v1.2.1 From ff8711135e9311d5a54c7210a5a87a86077271cb Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Sun, 4 Apr 2010 23:37:18 +0200 Subject: refactorize interface of Curl_ssl_recv/Curl_ssl_send --- lib/nss.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 2babfdf1d..560154dd1 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1340,47 +1340,50 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) return curlerr; } -/* return number of sent (non-SSL) bytes */ +/* for documentation see Curl_ssl_send() in sslgen.h */ int Curl_nss_send(struct connectdata *conn, /* connection data */ int sockindex, /* socketindex */ const void *mem, /* send this data */ - size_t len) /* amount to write */ + size_t len, /* amount to write */ + int *curlcode) { int rc; rc = PR_Send(conn->ssl[sockindex].handle, mem, (int)len, 0, -1); if(rc < 0) { - failf(conn->data, "SSL write: error %d", PR_GetError()); + PRInt32 err = PR_GetError(); + if(err == PR_WOULD_BLOCK_ERROR) + *curlcode = -1; /* EWOULDBLOCK */ + else { + failf(conn->data, "SSL write: error %d", err); + *curlcode = CURLE_SEND_ERROR; + } return -1; } return rc; /* number of bytes */ } -/* - * If the read would block we return -1 and set 'wouldblock' to TRUE. - * Otherwise we return the amount of data read. Other errors should return -1 - * and set 'wouldblock' to FALSE. - */ +/* for documentation see Curl_ssl_recv() in sslgen.h */ ssize_t Curl_nss_recv(struct connectdata * conn, /* connection data */ int num, /* socketindex */ char *buf, /* store read data here */ size_t buffersize, /* max amount to read */ - bool * wouldblock) + int *curlcode) { ssize_t nread; nread = PR_Recv(conn->ssl[num].handle, buf, (int)buffersize, 0, -1); - *wouldblock = FALSE; if(nread < 0) { /* failed SSL read */ PRInt32 err = PR_GetError(); - if(err == PR_WOULD_BLOCK_ERROR) { - *wouldblock = TRUE; - return -1; /* basically EWOULDBLOCK */ + if(err == PR_WOULD_BLOCK_ERROR) + *curlcode = -1; /* EWOULDBLOCK */ + else { + failf(conn->data, "SSL read: errno %d", err); + *curlcode = CURLE_RECV_ERROR; } - failf(conn->data, "SSL read: errno %d", err); return -1; } return nread; -- cgit v1.2.1 From ef1ac363ee5ece033b32653b5c00a915b68ab8ad Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Tue, 6 Apr 2010 13:42:11 +0200 Subject: nss: handle client certificate related errors --- lib/nss.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 560154dd1..0f8ebd527 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -989,6 +989,27 @@ int Curl_nss_close_all(struct SessionHandle *data) return 0; } +/* handle client certificate related errors if any; return false otherwise */ +static bool handle_cc_error(PRInt32 err, struct SessionHandle *data) +{ + switch(err) { + case SSL_ERROR_BAD_CERT_ALERT: + failf(data, "SSL error: SSL_ERROR_BAD_CERT_ALERT"); + return true; + + case SSL_ERROR_REVOKED_CERT_ALERT: + failf(data, "SSL error: SSL_ERROR_REVOKED_CERT_ALERT"); + return true; + + case SSL_ERROR_EXPIRED_CERT_ALERT: + failf(data, "SSL error: SSL_ERROR_EXPIRED_CERT_ALERT"); + return true; + + default: + return false; + } +} + CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) { PRInt32 err; @@ -1326,7 +1347,11 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) data->state.ssl_connect_retry = FALSE; err = PR_GetError(); - infof(data, "NSS error %d\n", err); + if(handle_cc_error(err, data)) + curlerr = CURLE_SSL_CERTPROBLEM; + else + infof(data, "NSS error %d\n", err); + if(model) PR_Close(model); @@ -1355,6 +1380,8 @@ int Curl_nss_send(struct connectdata *conn, /* connection data */ PRInt32 err = PR_GetError(); if(err == PR_WOULD_BLOCK_ERROR) *curlcode = -1; /* EWOULDBLOCK */ + else if(handle_cc_error(err, conn->data)) + *curlcode = CURLE_SSL_CERTPROBLEM; else { failf(conn->data, "SSL write: error %d", err); *curlcode = CURLE_SEND_ERROR; @@ -1380,6 +1407,8 @@ ssize_t Curl_nss_recv(struct connectdata * conn, /* connection data */ if(err == PR_WOULD_BLOCK_ERROR) *curlcode = -1; /* EWOULDBLOCK */ + else if(handle_cc_error(err, conn->data)) + *curlcode = CURLE_SSL_CERTPROBLEM; else { failf(conn->data, "SSL read: errno %d", err); *curlcode = CURLE_RECV_ERROR; -- cgit v1.2.1 From 82e9b78a388ab539c8784cd853adf6e4a97d52c5 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Sat, 24 Apr 2010 23:21:13 +0200 Subject: nss: fix SSL handshake timeout underflow --- lib/nss.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 0f8ebd527..addb94b64 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1025,6 +1025,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) int curlerr; const int *cipher_to_enable; PRSocketOptionData sock_opt; + long time_left; PRUint32 timeout; curlerr = CURLE_SSL_CONNECT_ERROR; @@ -1302,8 +1303,15 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) SSL_SetURL(connssl->handle, conn->host.name); + /* check timeout situation */ + time_left = Curl_timeleft(conn, NULL, TRUE); + if(time_left < 0L) { + failf(data, "timed out before SSL handshake"); + goto error; + } + timeout = PR_MillisecondsToInterval((PRUint32) time_left); + /* Force the handshake now */ - timeout = PR_MillisecondsToInterval((PRUint32)Curl_timeleft(conn, NULL, TRUE)); if(SSL_ForceHandshakeWithTimeout(connssl->handle, timeout) != SECSuccess) { if(conn->data->set.ssl.certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN) curlerr = CURLE_PEER_FAILED_VERIFICATION; -- cgit v1.2.1 From d64bd82bdcb169d0647a80f00068cedd761f8163 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Fri, 7 May 2010 15:05:34 +0200 Subject: sendrecv: split the I/O handling into private handler Howard Chu brought the bulk work of this patch that properly moves out the sending and recving of data to the parts of the code that are properly responsible for the various ways of doing so. Daniel Stenberg assisted with polishing a few bits and fixed some minor flaws in the original patch. Another upside of this patch is that we now abuse CURLcodes less with the "magic" -1 return codes and instead use CURLE_AGAIN more consistently. --- lib/nss.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index addb94b64..a2488d04b 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1010,6 +1010,9 @@ static bool handle_cc_error(PRInt32 err, struct SessionHandle *data) } } +static Curl_recv nss_recv; +static Curl_send nss_send; + CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) { PRInt32 err; @@ -1321,6 +1324,8 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) } connssl->state = ssl_connection_complete; + conn->recv = nss_recv; + conn->send = nss_send; display_conn_info(conn, connssl->handle); @@ -1373,12 +1378,11 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) return curlerr; } -/* for documentation see Curl_ssl_send() in sslgen.h */ -int Curl_nss_send(struct connectdata *conn, /* connection data */ - int sockindex, /* socketindex */ - const void *mem, /* send this data */ - size_t len, /* amount to write */ - int *curlcode) +static ssize_t nss_send(struct connectdata *conn, /* connection data */ + int sockindex, /* socketindex */ + const void *mem, /* send this data */ + size_t len, /* amount to write */ + CURLcode *curlcode) { int rc; @@ -1387,7 +1391,7 @@ int Curl_nss_send(struct connectdata *conn, /* connection data */ if(rc < 0) { PRInt32 err = PR_GetError(); if(err == PR_WOULD_BLOCK_ERROR) - *curlcode = -1; /* EWOULDBLOCK */ + *curlcode = CURLE_AGAIN; else if(handle_cc_error(err, conn->data)) *curlcode = CURLE_SSL_CERTPROBLEM; else { @@ -1399,12 +1403,11 @@ int Curl_nss_send(struct connectdata *conn, /* connection data */ return rc; /* number of bytes */ } -/* for documentation see Curl_ssl_recv() in sslgen.h */ -ssize_t Curl_nss_recv(struct connectdata * conn, /* connection data */ - int num, /* socketindex */ - char *buf, /* store read data here */ - size_t buffersize, /* max amount to read */ - int *curlcode) +static ssize_t nss_recv(struct connectdata * conn, /* connection data */ + int num, /* socketindex */ + char *buf, /* store read data here */ + size_t buffersize, /* max amount to read */ + CURLcode *curlcode) { ssize_t nread; @@ -1414,7 +1417,7 @@ ssize_t Curl_nss_recv(struct connectdata * conn, /* connection data */ PRInt32 err = PR_GetError(); if(err == PR_WOULD_BLOCK_ERROR) - *curlcode = -1; /* EWOULDBLOCK */ + *curlcode = CURLE_AGAIN; else if(handle_cc_error(err, conn->data)) *curlcode = CURLE_SSL_CERTPROBLEM; else { -- cgit v1.2.1 From 2e8b21833a581cc5389833ec4fdeeaa6fb7be538 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Tue, 11 May 2010 14:10:27 +0200 Subject: nss: add CRL to cache instead of read-only NSS db --- lib/nss.c | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index a2488d04b..b273b667b 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -63,6 +63,7 @@ #include #include #include +#include #include "curl_memory.h" #include "rawstr.h" @@ -79,6 +80,7 @@ PRFileDesc *PR_ImportTCPSocket(PRInt32 osfd); PRLock * nss_initlock = NULL; +PRLock * nss_crllock = NULL; volatile int initialized = 0; @@ -411,6 +413,31 @@ static int nss_load_cert(struct ssl_connect_data *ssl, return 1; } +/* add given CRL to cache if it is not already there */ +static SECStatus nss_cache_crl(SECItem *crlDER) +{ + CERTCertDBHandle *db = CERT_GetDefaultCertDB(); + CERTSignedCrl *crl = SEC_FindCrlByDERCert(db, crlDER, 0); + if(crl) { + /* CRL already cached */ + SEC_DestroyCrl(crl); + return SECSuccess; + } + + /* acquire lock before call of CERT_CacheCRL() */ + PR_Lock(nss_crllock); + if(SECSuccess != CERT_CacheCRL(db, crlDER)) { + /* unable to cache CRL */ + PR_Unlock(nss_crllock); + return SECFailure; + } + + /* we need to clear session cache, so that the CRL could take effect */ + SSL_ClearSessionCache(); + PR_Unlock(nss_crllock); + return SECSuccess; +} + static int nss_load_crl(const char* crlfilename, PRBool ascii) { PRFileDesc *infile; @@ -419,8 +446,6 @@ static int nss_load_crl(const char* crlfilename, PRBool ascii) PRInt32 nb; int rv; SECItem crlDER; - CERTSignedCrl *crl=NULL; - PK11SlotInfo *slot=NULL; infile = PR_Open(crlfilename,PR_RDONLY,0); if (!infile) { @@ -473,16 +498,7 @@ static int nss_load_crl(const char* crlfilename, PRBool ascii) return 0; } - slot = PK11_GetInternalKeySlot(); - crl = PK11_ImportCRL(slot,&crlDER, - NULL,SEC_CRL_TYPE, - NULL,CRL_IMPORT_DEFAULT_OPTIONS, - NULL,(CRL_DECODE_DEFAULT_OPTIONS| - CRL_DECODE_DONT_COPY_DER)); - if (slot) PK11_FreeSlot(slot); - if (!crl) return 0; - SEC_DestroyCrl(crl); - return 1; + return (SECSuccess == nss_cache_crl(&crlDER)); } static int nss_load_key(struct connectdata *conn, int sockindex, @@ -889,6 +905,7 @@ int Curl_nss_init(void) if (nss_initlock == NULL) { PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 256); nss_initlock = PR_NewLock(); + nss_crllock = PR_NewLock(); } /* We will actually initialize NSS later */ @@ -918,6 +935,7 @@ void Curl_nss_cleanup(void) PR_Unlock(nss_initlock); PR_DestroyLock(nss_initlock); + PR_DestroyLock(nss_crllock); nss_initlock = NULL; initialized = 0; -- cgit v1.2.1 From 3e759f4fb6018b353bd4a1d608be3a3d7b2c9645 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Tue, 11 May 2010 14:39:08 +0200 Subject: nss: make it possible to read ASCII and DER CRL --- lib/nss.c | 109 ++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 53 insertions(+), 56 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index b273b667b..3312e8a4f 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -421,6 +421,7 @@ static SECStatus nss_cache_crl(SECItem *crlDER) if(crl) { /* CRL already cached */ SEC_DestroyCrl(crl); + SECITEM_FreeItem(crlDER, PR_FALSE); return SECSuccess; } @@ -429,6 +430,7 @@ static SECStatus nss_cache_crl(SECItem *crlDER) if(SECSuccess != CERT_CacheCRL(db, crlDER)) { /* unable to cache CRL */ PR_Unlock(nss_crllock); + SECITEM_FreeItem(crlDER, PR_FALSE); return SECFailure; } @@ -438,67 +440,63 @@ static SECStatus nss_cache_crl(SECItem *crlDER) return SECSuccess; } -static int nss_load_crl(const char* crlfilename, PRBool ascii) +static SECStatus nss_load_crl(const char* crlfilename) { PRFileDesc *infile; - PRStatus prstat; PRFileInfo info; - PRInt32 nb; - int rv; - SECItem crlDER; + SECItem filedata = { 0, NULL, 0 }; + SECItem crlDER = { 0, NULL, 0 }; + char *body; - infile = PR_Open(crlfilename,PR_RDONLY,0); - if (!infile) { - return 0; - } - crlDER.data = NULL; - prstat = PR_GetOpenFileInfo(infile,&info); - if (prstat!=PR_SUCCESS) - return 0; - if (ascii) { - SECItem filedata; - char *asc,*body; - filedata.data = NULL; - if (!SECITEM_AllocItem(NULL,&filedata,info.size)) - return 0; - nb = PR_Read(infile,filedata.data,info.size); - if (nb!=info.size) - return 0; - asc = (char*)filedata.data; - if (!asc) - return 0; + infile = PR_Open(crlfilename, PR_RDONLY, 0); + if(!infile) + return SECFailure; - body=strstr(asc,"-----BEGIN"); - if (body != NULL) { - char *trailer=NULL; - asc = body; - body = PORT_Strchr(asc,'\n'); - if (!body) - body = PORT_Strchr(asc,'\r'); - if (body) - trailer = strstr(++body,"-----END"); - if (trailer!=NULL) - *trailer='\0'; - else - return 0; - } - else { - body = asc; - } - rv = ATOB_ConvertAsciiToItem(&crlDER,body); - PORT_Free(filedata.data); - if (rv) - return 0; - } - else { - if (!SECITEM_AllocItem(NULL,&crlDER,info.size)) - return 0; - nb = PR_Read(infile,crlDER.data,info.size); - if (nb!=info.size) - return 0; + if(PR_SUCCESS != PR_GetOpenFileInfo(infile, &info)) + goto fail; + + if(!SECITEM_AllocItem(NULL, &filedata, info.size + /* zero ended */ 1)) + goto fail; + + if(info.size != PR_Read(infile, filedata.data, info.size)) + goto fail; + + /* place a trailing zero right after the visible data */ + body = (char*)filedata.data; + body[--filedata.len] = '\0'; + + body = strstr(body, "-----BEGIN"); + if(body) { + /* assume ASCII */ + char *trailer; + char *begin = PORT_Strchr(body, '\n'); + if(!begin) + begin = PORT_Strchr(body, '\r'); + if(!begin) + goto fail; + + trailer = strstr(++begin, "-----END"); + if(!trailer) + goto fail; + + /* retrieve DER from ASCII */ + *trailer = '\0'; + if(ATOB_ConvertAsciiToItem(&crlDER, begin)) + goto fail; + + SECITEM_FreeItem(&filedata, PR_FALSE); } + else + /* assume DER */ + crlDER = filedata; + + PR_Close(infile); + return nss_cache_crl(&crlDER); - return (SECSuccess == nss_cache_crl(&crlDER)); +fail: + PR_Close(infile); + SECITEM_FreeItem(&filedata, PR_FALSE); + return SECFailure; } static int nss_load_key(struct connectdata *conn, int sockindex, @@ -1265,8 +1263,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) data->set.ssl.CApath ? data->set.ssl.CApath : "none"); if (data->set.ssl.CRLfile) { - int rc = nss_load_crl(data->set.ssl.CRLfile, PR_FALSE); - if (!rc) { + if(SECSuccess != nss_load_crl(data->set.ssl.CRLfile)) { curlerr = CURLE_SSL_CRL_BADFILE; goto error; } -- cgit v1.2.1 From bc8fc9803fbd0fa9daf0dba796d42d03faf49120 Mon Sep 17 00:00:00 2001 From: Howard Chu Date: Tue, 11 May 2010 22:48:38 +0200 Subject: sendrecv: make them two pairs of send/recv to properly deal with FTPS FTP(S) use two connections that can be set to different recv and send functions independently, so by introducing recv+send pairs in the same manner we already have sockets/connections we can work with FTPS fine. This commit fixes the FTPS regression introduced in change d64bd82. --- lib/nss.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 3312e8a4f..4058d7867 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1339,8 +1339,8 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) } connssl->state = ssl_connection_complete; - conn->recv = nss_recv; - conn->send = nss_send; + conn->recv[sockindex] = nss_recv; + conn->send[sockindex] = nss_send; display_conn_info(conn, connssl->handle); -- cgit v1.2.1 From f3b77e5611d860739c0cffbc394172adf1f14b57 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Sun, 27 Jun 2010 03:38:01 +0200 Subject: http_ntlm: add support for NSS When configured with '--without-ssl --with-nss', NTLM authentication now uses NSS crypto library for MD5 and DES. For MD4 we have a local implementation in that case. More details are available at https://bugzilla.redhat.com/603783 In order to get it working, curl_global_init() must be called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL. That's necessary because NSS needs to be initialized globally and we do so only when the NSS library is actually required by protocol. The mentioned call of curl_global_init() is responsible for creating of the initialization mutex. There was also slightly changed the NSS initialization scenario, in particular, loading of the NSS PEM module. It used to be loaded always right after the NSS library was initialized. Now the library is initialized as soon as any SSL or NTLM is required, while the PEM module is prevented from being loaded until the SSL is actually required. --- lib/nss.c | 162 +++++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 96 insertions(+), 66 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 4058d7867..6d3f12c03 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -891,6 +891,57 @@ isTLSIntoleranceError(PRInt32 err) } } +static CURLcode init_nss(struct SessionHandle *data) +{ + char *cert_dir; + struct_stat st; + if(initialized) + return CURLE_OK; + + /* First we check if $SSL_DIR points to a valid dir */ + cert_dir = getenv("SSL_DIR"); + if(cert_dir) { + if((stat(cert_dir, &st) != 0) || + (!S_ISDIR(st.st_mode))) { + cert_dir = NULL; + } + } + + /* Now we check if the default location is a valid dir */ + if(!cert_dir) { + if((stat(SSL_DIR, &st) == 0) && + (S_ISDIR(st.st_mode))) { + cert_dir = (char *)SSL_DIR; + } + } + + if(!NSS_IsInitialized()) { + SECStatus rv; + initialized = 1; + infof(data, "Initializing NSS with certpath: %s\n", + cert_dir ? cert_dir : "none"); + if(!cert_dir) { + rv = NSS_NoDB_Init(NULL); + } + else { + char *certpath = + PR_smprintf("%s%s", NSS_VersionCheck("3.12.0") ? "sql:" : "", cert_dir); + rv = NSS_Initialize(certpath, "", "", "", NSS_INIT_READONLY); + PR_smprintf_free(certpath); + } + if(rv != SECSuccess) { + infof(data, "Unable to initialize NSS database\n"); + initialized = 0; + return CURLE_SSL_CACERT_BADFILE; + } + } + + if(num_enabled_ciphers() == 0) + NSS_SetDomesticPolicy(); + + return CURLE_OK; +} + /** * Global SSL init * @@ -911,6 +962,21 @@ int Curl_nss_init(void) return 1; } +CURLcode Curl_nss_force_init(struct SessionHandle *data) +{ + CURLcode rv; + if(!nss_initlock) { + failf(data, "unable to initialize NSS, curl_global_init() should have been " + "called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL"); + return CURLE_OUT_OF_MEMORY; + } + + PR_Lock(nss_initlock); + rv = init_nss(data); + PR_Unlock(nss_initlock); + return rv; +} + /* Global cleanup */ void Curl_nss_cleanup(void) { @@ -1039,16 +1105,12 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) struct SessionHandle *data = conn->data; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - SECStatus rv; - char *certDir = NULL; int curlerr; const int *cipher_to_enable; PRSocketOptionData sock_opt; long time_left; PRUint32 timeout; - curlerr = CURLE_SSL_CONNECT_ERROR; - if (connssl->state == ssl_connection_complete) return CURLE_OK; @@ -1062,76 +1124,36 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) /* FIXME. NSS doesn't support multiple databases open at the same time. */ PR_Lock(nss_initlock); - if(!initialized) { - struct_stat st; - - /* First we check if $SSL_DIR points to a valid dir */ - certDir = getenv("SSL_DIR"); - if(certDir) { - if((stat(certDir, &st) != 0) || - (!S_ISDIR(st.st_mode))) { - certDir = NULL; - } - } - - /* Now we check if the default location is a valid dir */ - if(!certDir) { - if((stat(SSL_DIR, &st) == 0) && - (S_ISDIR(st.st_mode))) { - certDir = (char *)SSL_DIR; - } - } - - if (!NSS_IsInitialized()) { - initialized = 1; - infof(conn->data, "Initializing NSS with certpath: %s\n", - certDir ? certDir : "none"); - if(!certDir) { - rv = NSS_NoDB_Init(NULL); - } - else { - char *certpath = PR_smprintf("%s%s", - NSS_VersionCheck("3.12.0") ? "sql:" : "", - certDir); - rv = NSS_Initialize(certpath, "", "", "", NSS_INIT_READONLY); - PR_smprintf_free(certpath); - } - if(rv != SECSuccess) { - infof(conn->data, "Unable to initialize NSS database\n"); - curlerr = CURLE_SSL_CACERT_BADFILE; - initialized = 0; - PR_Unlock(nss_initlock); - goto error; - } - } + curlerr = init_nss(conn->data); + if(CURLE_OK != curlerr) { + PR_Unlock(nss_initlock); + goto error; + } - if(num_enabled_ciphers() == 0) - NSS_SetDomesticPolicy(); + curlerr = CURLE_SSL_CONNECT_ERROR; #ifdef HAVE_PK11_CREATEGENERICOBJECT - if(!mod) { - char *configstring = aprintf("library=%s name=PEM", pem_library); - if(!configstring) { - PR_Unlock(nss_initlock); - goto error; - } - mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); - free(configstring); + if(!mod) { + char *configstring = aprintf("library=%s name=PEM", pem_library); + if(!configstring) { + PR_Unlock(nss_initlock); + goto error; + } + mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE); + free(configstring); - if(!mod || !mod->loaded) { - if(mod) { - SECMOD_DestroyModule(mod); - mod = NULL; - } - infof(data, "WARNING: failed to load NSS PEM library %s. Using " - "OpenSSL PEM certificates will not work.\n", pem_library); + if(!mod || !mod->loaded) { + if(mod) { + SECMOD_DestroyModule(mod); + mod = NULL; } + infof(data, "WARNING: failed to load NSS PEM library %s. Using " + "OpenSSL PEM certificates will not work.\n", pem_library); } + } #endif - PK11_SetPasswordFunc(nss_get_password); - - } + PK11_SetPasswordFunc(nss_get_password); PR_Unlock(nss_initlock); model = PR_NewTCPSocket(); @@ -1448,4 +1470,12 @@ size_t Curl_nss_version(char *buffer, size_t size) { return snprintf(buffer, size, "NSS/%s", NSS_VERSION); } + +int Curl_nss_seed(struct SessionHandle *data) +{ + /* TODO: implement? */ + (void) data; + return 0; +} + #endif /* USE_NSS */ -- cgit v1.2.1 From 2b3fbc8cdb3ddaec159d4ad693474eb84e5ee34d Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 2 Jan 2011 23:41:49 +0100 Subject: Curl_nss_connect: avoid PATH_MAX Since some systems don't have PATH_MAX and it isn't that clever to assume a fixed maximum path length, the code now allocates buffer space instead of using stack. Reported by: Samuel Thibault Bug: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=608521 --- lib/nss.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 6d3f12c03..26bc6e4d9 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -1265,12 +1265,21 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN); if(entry) { - char fullpath[PATH_MAX]; - - snprintf(fullpath, sizeof(fullpath), "%s/%s", data->set.ssl.CApath, + char *fullpath; + size_t pathlen = strlen(data->set.ssl.CApath) + + strlen(entry->name) + 2; /* add two, for slash and trailing zero */ + fullpath = malloc(pathlen); + if(!fullpath) { + PR_CloseDir(dir); + curlerr = CURLE_OUT_OF_MEMORY; + goto error; + } + + snprintf(fullpath, pathlen, "%s/%s", data->set.ssl.CApath, entry->name); rc = nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE); /* FIXME: check this return value! */ + free(fullpath); } /* This is purposefully tolerant of errors so non-PEM files * can be in the same directory */ -- cgit v1.2.1 From d8f6d1c3341cfc5a1263e1f3f339b64e10b75dc3 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Tue, 4 Jan 2011 13:52:54 +0100 Subject: nss: avoid CURLE_OUT_OF_MEMORY given a file name without any slash Bug: https://bugzilla.redhat.com/623663 --- lib/nss.c | 73 ++++++++++++++++++++++++++++++++++----------------------------- 1 file changed, 40 insertions(+), 33 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 26bc6e4d9..7db2d8d76 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -277,22 +277,35 @@ static int is_file(const char *filename) return 0; } -static char *fmt_nickname(char *str, bool *nickname_alloc) +/* Return on heap allocated filename/nickname of a certificate. The returned + * string should be later deallocated using free(). *is_nickname is set to TRUE + * if the given string is treated as nickname; FALSE if the given string is + * treated as file name. + */ +static char *fmt_nickname(struct SessionHandle *data, enum dupstring cert_kind, + bool *is_nickname) { - char *nickname = NULL; - *nickname_alloc = FALSE; - - if(is_file(str)) { - char *n = strrchr(str, '/'); - if(n) { - *nickname_alloc = TRUE; - n++; /* skip last slash */ - nickname = aprintf("PEM Token #%d:%s", 1, n); - } - return nickname; + const char *str = data->set.str[cert_kind]; + const char *n; + *is_nickname = TRUE; + + if(!is_file(str)) + /* no such file exists, use the string as nickname */ + return strdup(str); + + /* search the last slash; we require at least one slash in a file name */ + n = strrchr(str, '/'); + if(!n) { + infof(data, "warning: certificate file name \"%s\" handled as nickname; " + "please use \"./%s\" to force file name\n", str, str); + return strdup(str); } - return str; + /* we'll use the PEM reader to read the certificate from file */ + *is_nickname = FALSE; + + n++; /* skip last slash */ + return aprintf("PEM Token #%d:%s", 1, n); } static int nss_load_cert(struct ssl_connect_data *ssl, @@ -1304,25 +1317,20 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) } if(data->set.str[STRING_CERT]) { - bool nickname_alloc = FALSE; - char *nickname = fmt_nickname(data->set.str[STRING_CERT], &nickname_alloc); + bool is_nickname; + char *nickname = fmt_nickname(data, STRING_CERT, &is_nickname); if(!nickname) return CURLE_OUT_OF_MEMORY; - if(!cert_stuff(conn, sockindex, data->set.str[STRING_CERT], - data->set.str[STRING_KEY])) { + if(!is_nickname && !cert_stuff(conn, sockindex, data->set.str[STRING_CERT], + data->set.str[STRING_KEY])) { /* failf() is already done in cert_stuff() */ - if(nickname_alloc) - free(nickname); + free(nickname); return CURLE_SSL_CERTPROBLEM; } - /* this "takes over" the pointer to the allocated name or makes a - dup of it */ - connssl->client_nickname = nickname_alloc?nickname:strdup(nickname); - if(!connssl->client_nickname) - return CURLE_OUT_OF_MEMORY; - + /* store the nickname for SelectClientCert() called during handshake */ + connssl->client_nickname = nickname; } else connssl->client_nickname = NULL; @@ -1376,18 +1384,17 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) display_conn_info(conn, connssl->handle); if (data->set.str[STRING_SSL_ISSUERCERT]) { - SECStatus ret; - bool nickname_alloc = FALSE; - char *nickname = fmt_nickname(data->set.str[STRING_SSL_ISSUERCERT], - &nickname_alloc); - + SECStatus ret = SECFailure; + bool is_nickname; + char *nickname = fmt_nickname(data, STRING_SSL_ISSUERCERT, &is_nickname); if(!nickname) return CURLE_OUT_OF_MEMORY; - ret = check_issuer_cert(connssl->handle, nickname); + if(is_nickname) + /* we support only nicknames in case of STRING_SSL_ISSUERCERT for now */ + ret = check_issuer_cert(connssl->handle, nickname); - if(nickname_alloc) - free(nickname); + free(nickname); if(SECFailure == ret) { infof(data,"SSL certificate issuer check failed\n"); -- cgit v1.2.1 From adb49ad8bb280b586b387ba930c0681afee03923 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 4 Jan 2011 23:07:58 +0100 Subject: Curl_timeleft: s/conn/data in first argument As the function doesn't really use the connectdata struct but only the SessionHanadle struct I modified what argument it wants. --- lib/nss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 7db2d8d76..a5e11e2fc 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1361,7 +1361,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) SSL_SetURL(connssl->handle, conn->host.name); /* check timeout situation */ - time_left = Curl_timeleft(conn, NULL, TRUE); + time_left = Curl_timeleft(data, NULL, TRUE); if(time_left < 0L) { failf(data, "timed out before SSL handshake"); goto error; -- cgit v1.2.1 From fc77790bcd451f32a0f60a5e4073b2be54fb40e9 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Tue, 18 Jan 2011 13:53:43 +0100 Subject: nss: fix a bug in handling of CURLOPT_CAPATH ... and update the curl.1 and curl_easy_setopt.3 man pages such that they do not suggest to use an OpenSSL utility if curl is not built against OpenSSL. Bug: https://bugzilla.redhat.com/669702 --- lib/nss.c | 108 ++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 52 insertions(+), 56 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index a5e11e2fc..3d3e1c92c 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1108,6 +1108,55 @@ static bool handle_cc_error(PRInt32 err, struct SessionHandle *data) static Curl_recv nss_recv; static Curl_send nss_send; +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; + + if(cafile && !nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE)) + return CURLE_SSL_CACERT_BADFILE; + + if(capath) { + struct_stat st; + if(stat(capath, &st) == -1) + return CURLE_SSL_CACERT_BADFILE; + + if(S_ISDIR(st.st_mode)) { + PRDirEntry *entry; + PRDir *dir = PR_OpenDir(capath); + if(!dir) + return CURLE_SSL_CACERT_BADFILE; + + while((entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN))) { + char *fullpath = aprintf("%s/%s", capath, entry->name); + if(!fullpath) { + PR_CloseDir(dir); + return CURLE_OUT_OF_MEMORY; + } + + if(!nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE)) + /* This is purposefully tolerant of errors so non-PEM files can + * be in the same directory */ + infof(data, "failed to load '%s' from CURLOPT_CAPATH\n", fullpath); + + free(fullpath); + } + + PR_CloseDir(dir); + } + else + infof(data, "warning: CURLOPT_CAPATH not a directory (%s)\n", capath); + } + + infof(data, " CAfile: %s\n CApath: %s\n", + cafile ? cafile : "none", + capath ? capath : "none"); + + return CURLE_OK; +} + CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) { PRInt32 err; @@ -1249,62 +1298,9 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) NULL) != SECSuccess) goto error; - if(!data->set.ssl.verifypeer) - /* skip the verifying of the peer */ - ; - else if(data->set.ssl.CAfile) { - int rc = nss_load_cert(&conn->ssl[sockindex], data->set.ssl.CAfile, - PR_TRUE); - if(!rc) { - curlerr = CURLE_SSL_CACERT_BADFILE; - goto error; - } - } - else if(data->set.ssl.CApath) { - struct_stat st; - PRDir *dir; - PRDirEntry *entry; - - if(stat(data->set.ssl.CApath, &st) == -1) { - curlerr = CURLE_SSL_CACERT_BADFILE; - goto error; - } - - if(S_ISDIR(st.st_mode)) { - int rc; - - dir = PR_OpenDir(data->set.ssl.CApath); - do { - entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN); - - if(entry) { - char *fullpath; - size_t pathlen = strlen(data->set.ssl.CApath) + - strlen(entry->name) + 2; /* add two, for slash and trailing zero */ - fullpath = malloc(pathlen); - if(!fullpath) { - PR_CloseDir(dir); - curlerr = CURLE_OUT_OF_MEMORY; - goto error; - } - - snprintf(fullpath, pathlen, "%s/%s", data->set.ssl.CApath, - entry->name); - rc = nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE); - /* FIXME: check this return value! */ - free(fullpath); - } - /* This is purposefully tolerant of errors so non-PEM files - * can be in the same directory */ - } while(entry != NULL); - PR_CloseDir(dir); - } - } - infof(data, - " CAfile: %s\n" - " CApath: %s\n", - data->set.ssl.CAfile ? data->set.ssl.CAfile : "none", - data->set.ssl.CApath ? data->set.ssl.CApath : "none"); + if(data->set.ssl.verifypeer && (CURLE_OK != + (curlerr = nss_load_ca_certificates(conn, sockindex)))) + goto error; if (data->set.ssl.CRLfile) { if(SECSuccess != nss_load_crl(data->set.ssl.CRLfile)) { -- cgit v1.2.1 From dc0a7161f868ae2f8094289f45278cc702eafbd9 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Thu, 27 Jan 2011 10:55:02 +0100 Subject: nss: avoid memory leaks and failure of NSS shutdown ... in case more than one CA is loaded. Bug: https://bugzilla.redhat.com/670802 --- lib/nss.c | 190 +++++++++++++++++++++++++++++--------------------------------- 1 file changed, 89 insertions(+), 101 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 3d3e1c92c..5b648cdac 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -42,6 +42,7 @@ #include "strequal.h" #include "select.h" #include "sslgen.h" +#include "llist.h" #define _MPRINTF_REPLACE /* use the internal *printf() functions */ #include @@ -90,8 +91,12 @@ typedef struct { PRInt32 version; /* protocol version valid for this cipher */ } cipher_s; -#define PK11_SETATTRS(x,id,v,l) (x)->type = (id); \ - (x)->pValue=(v); (x)->ulValueLen = (l) +#define PK11_SETATTRS(_attr, _idx, _type, _val, _len) do { \ + CK_ATTRIBUTE *ptr = (_attr) + ((_idx)++); \ + ptr->type = (_type); \ + ptr->pValue = (_val); \ + ptr->ulValueLen = (_len); \ +} while(0) #define CERT_NewTempCertificate __CERT_NewTempCertificate @@ -308,18 +313,73 @@ static char *fmt_nickname(struct SessionHandle *data, enum dupstring cert_kind, return aprintf("PEM Token #%d:%s", 1, n); } +#ifdef HAVE_PK11_CREATEGENERICOBJECT +/* Call PK11_CreateGenericObject() with the given obj_class and filename. If + * the call succeeds, append the object handle to the list of objects so that + * the object can be destroyed in Curl_nss_close(). */ +static CURLcode nss_create_object(struct ssl_connect_data *ssl, + CK_OBJECT_CLASS obj_class, + const char *filename, bool cacert) +{ + PK11SlotInfo *slot; + PK11GenericObject *obj; + CK_BBOOL cktrue = CK_TRUE; + CK_BBOOL ckfalse = CK_FALSE; + CK_ATTRIBUTE attrs[/* max count of attributes */ 4]; + int attr_cnt = 0; + + const int slot_id = (cacert) ? 0 : 1; + char *slot_name = aprintf("PEM Token #%d", slot_id); + if(!slot_name) + return CURLE_OUT_OF_MEMORY; + + slot = PK11_FindSlotByName(slot_name); + free(slot_name); + if(!slot) + return CURLE_SSL_CERTPROBLEM; + + PK11_SETATTRS(attrs, attr_cnt, CKA_CLASS, &obj_class, sizeof(obj_class)); + PK11_SETATTRS(attrs, attr_cnt, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL)); + PK11_SETATTRS(attrs, attr_cnt, CKA_LABEL, (unsigned char *)filename, + strlen(filename) + 1); + + if(CKO_CERTIFICATE == obj_class) { + CK_BBOOL *pval = (cacert) ? (&cktrue) : (&ckfalse); + PK11_SETATTRS(attrs, attr_cnt, CKA_TRUST, pval, sizeof(*pval)); + } + + obj = PK11_CreateGenericObject(slot, attrs, attr_cnt, PR_FALSE); + PK11_FreeSlot(slot); + if(!obj) + return CURLE_SSL_CERTPROBLEM; + + if(!Curl_llist_insert_next(ssl->obj_list, ssl->obj_list->tail, obj)) { + PK11_DestroyGenericObject(obj); + return CURLE_OUT_OF_MEMORY; + } + + return CURLE_OK; +} + +/* Destroy the NSS object whose handle is given by ptr. This function is + * a callback of Curl_llist_alloc() used by Curl_llist_destroy() to destroy + * NSS objects in Curl_nss_close() */ +static void nss_destroy_object(void *user, void *ptr) +{ + PK11GenericObject *obj = (PK11GenericObject *)ptr; + (void) user; + PK11_DestroyGenericObject(obj); +} +#endif + static int nss_load_cert(struct ssl_connect_data *ssl, const char *filename, PRBool cacert) { #ifdef HAVE_PK11_CREATEGENERICOBJECT - CK_SLOT_ID slotID; - PK11SlotInfo * slot = NULL; - CK_ATTRIBUTE *attrs; - CK_ATTRIBUTE theTemplate[20]; - CK_BBOOL cktrue = CK_TRUE; - CK_BBOOL ckfalse = CK_FALSE; - CK_OBJECT_CLASS objClass = CKO_CERTIFICATE; - char slotname[SLOTSIZE]; + /* All CA and trust objects go into slot 0. Other slots are used + * for storing certificates. + */ + const int slot_id = (cacert) ? 0 : 1; #endif CERTCertificate *cert; char *nickname = NULL; @@ -346,57 +406,15 @@ static int nss_load_cert(struct ssl_connect_data *ssl, } #ifdef HAVE_PK11_CREATEGENERICOBJECT - attrs = theTemplate; - - /* All CA and trust objects go into slot 0. Other slots are used - * for storing certificates. With each new user certificate we increment - * the slot count. We only support 1 user certificate right now. - */ - if(cacert) - slotID = 0; - else - slotID = 1; - - snprintf(slotname, SLOTSIZE, "PEM Token #%ld", slotID); - - nickname = aprintf("PEM Token #%ld:%s", slotID, n); + nickname = aprintf("PEM Token #%d:%s", slot_id, n); if(!nickname) return 0; - slot = PK11_FindSlotByName(slotname); - - if(!slot) { + if(CURLE_OK != nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert)) { free(nickname); return 0; } - PK11_SETATTRS(attrs, CKA_CLASS, &objClass, sizeof(objClass) ); - attrs++; - PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL) ); - attrs++; - PK11_SETATTRS(attrs, CKA_LABEL, (unsigned char *)filename, - strlen(filename)+1); - attrs++; - if(cacert) { - PK11_SETATTRS(attrs, CKA_TRUST, &cktrue, sizeof(CK_BBOOL) ); - } - else { - PK11_SETATTRS(attrs, CKA_TRUST, &ckfalse, sizeof(CK_BBOOL) ); - } - attrs++; - - /* This load the certificate in our PEM module into the appropriate - * slot. - */ - ssl->cacert[slotID] = PK11_CreateGenericObject(slot, theTemplate, 4, - PR_FALSE /* isPerm */); - - PK11_FreeSlot(slot); - - if(ssl->cacert[slotID] == NULL) { - free(nickname); - return 0; - } #else /* We don't have PK11_CreateGenericObject but a file-based cert was passed * in. We need to fail. @@ -516,54 +534,27 @@ static int nss_load_key(struct connectdata *conn, int sockindex, char *key_file) { #ifdef HAVE_PK11_CREATEGENERICOBJECT - PK11SlotInfo * slot = NULL; - CK_ATTRIBUTE *attrs; - CK_ATTRIBUTE theTemplate[20]; - CK_BBOOL cktrue = CK_TRUE; - CK_OBJECT_CLASS objClass = CKO_PRIVATE_KEY; - CK_SLOT_ID slotID; - char slotname[SLOTSIZE]; - struct ssl_connect_data *sslconn = &conn->ssl[sockindex]; - - attrs = theTemplate; - - /* FIXME: grok the various file types */ + PK11SlotInfo *slot; + SECStatus status; + struct ssl_connect_data *ssl = conn->ssl; - slotID = 1; /* hardcoded for now */ - - snprintf(slotname, sizeof(slotname), "PEM Token #%ld", slotID); - slot = PK11_FindSlotByName(slotname); - - if(!slot) - return 0; - - PK11_SETATTRS(attrs, CKA_CLASS, &objClass, sizeof(objClass) ); attrs++; - PK11_SETATTRS(attrs, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL) ); attrs++; - PK11_SETATTRS(attrs, CKA_LABEL, (unsigned char *)key_file, - strlen(key_file)+1); attrs++; - - /* When adding an encrypted key the PKCS#11 will be set as removed */ - sslconn->key = PK11_CreateGenericObject(slot, theTemplate, 3, - PR_FALSE /* isPerm */); - if(sslconn->key == NULL) { + if(CURLE_OK != nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE)) { PR_SetError(SEC_ERROR_BAD_KEY, 0); return 0; } + slot = PK11_FindSlotByName("PEM Token #1"); + if(!slot) + return 0; + /* This will force the token to be seen as re-inserted */ SECMOD_WaitForAnyTokenEvent(mod, 0, 0); PK11_IsPresent(slot); - /* parg is initialized in nss_Init_Tokens() */ - if(PK11_Authenticate(slot, PR_TRUE, - conn->data->set.str[STRING_KEY_PASSWD]) != SECSuccess) { - - PK11_FreeSlot(slot); - return 0; - } + status = PK11_Authenticate(slot, PR_TRUE, + conn->data->set.str[STRING_KEY_PASSWD]); PK11_FreeSlot(slot); - - return 1; + return (SECSuccess == status) ? 1 : 0; #else /* If we don't have PK11_CreateGenericObject then we can't load a file-based * key. @@ -1063,12 +1054,8 @@ void Curl_nss_close(struct connectdata *conn, int sockindex) connssl->client_nickname = NULL; } #ifdef HAVE_PK11_CREATEGENERICOBJECT - if(connssl->key) - (void)PK11_DestroyGenericObject(connssl->key); - if(connssl->cacert[1]) - (void)PK11_DestroyGenericObject(connssl->cacert[1]); - if(connssl->cacert[0]) - (void)PK11_DestroyGenericObject(connssl->cacert[0]); + /* destroy all NSS objects in order to avoid failure of NSS shutdown */ + Curl_llist_destroy(connssl->obj_list, NULL); #endif connssl->handle = NULL; } @@ -1179,9 +1166,10 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) connssl->data = data; #ifdef HAVE_PK11_CREATEGENERICOBJECT - connssl->cacert[0] = NULL; - connssl->cacert[1] = NULL; - connssl->key = NULL; + /* list of all NSS objects we need to destroy in Curl_nss_close() */ + connssl->obj_list = Curl_llist_alloc(nss_destroy_object); + if(!connssl->obj_list) + return CURLE_OUT_OF_MEMORY; #endif /* FIXME. NSS doesn't support multiple databases open at the same time. */ -- cgit v1.2.1 From c3a6116dc934d053d89d1eb3476fbc30d19d62ce Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 16 Feb 2011 19:33:22 +0100 Subject: nss_load_key: fix unused variable warning --- lib/nss.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 5b648cdac..e115ac912 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -537,6 +537,7 @@ static int nss_load_key(struct connectdata *conn, int sockindex, PK11SlotInfo *slot; SECStatus status; struct ssl_connect_data *ssl = conn->ssl; + (void)sockindex; /* unused */ if(CURLE_OK != nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE)) { PR_SetError(SEC_ERROR_BAD_KEY, 0); @@ -561,6 +562,7 @@ static int nss_load_key(struct connectdata *conn, int sockindex, */ (void)conn; /* unused */ (void)key_file; /* unused */ + (void)sockindex; /* unused */ return 0; #endif } -- cgit v1.2.1 From a40f58d2efac45dad7e12ea53870f42c825bcf0d Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Thu, 17 Feb 2011 17:37:24 +0100 Subject: nss: avoid memory leak on SSL connection failure --- lib/nss.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index e115ac912..d26ad5b78 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1058,6 +1058,7 @@ void Curl_nss_close(struct connectdata *conn, int sockindex) #ifdef HAVE_PK11_CREATEGENERICOBJECT /* destroy all NSS objects in order to avoid failure of NSS shutdown */ Curl_llist_destroy(connssl->obj_list, NULL); + connssl->obj_list = NULL; #endif connssl->handle = NULL; } @@ -1216,7 +1217,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) /* make the socket nonblocking */ sock_opt.option = PR_SockOpt_Nonblocking; sock_opt.value.non_blocking = PR_TRUE; - if(PR_SetSocketOption(model, &sock_opt) != SECSuccess) + if(PR_SetSocketOption(model, &sock_opt) != PR_SUCCESS) goto error; if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess) @@ -1407,6 +1408,12 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if(model) PR_Close(model); +#ifdef HAVE_PK11_CREATEGENERICOBJECT + /* cleanup on connection failure */ + Curl_llist_destroy(connssl->obj_list, NULL); + connssl->obj_list = NULL; +#endif + if (ssl3 && tlsv1 && isTLSIntoleranceError(err)) { /* schedule reconnect through Curl_retry_request() */ data->state.ssl_connect_retry = TRUE; -- cgit v1.2.1 From 7aa2d10e0db82a55eba6b5723307d915939cb2fb Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Tue, 22 Feb 2011 13:13:53 +0100 Subject: nss: do not ignore failure of SSL handshake Flaw introduced in fc77790 and present in curl-7.21.4. Bug: https://bugzilla.redhat.com/669702#c16 --- lib/nss.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index d26ad5b78..be26253c4 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1157,7 +1157,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) struct SessionHandle *data = conn->data; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - int curlerr; + CURLcode curlerr; const int *cipher_to_enable; PRSocketOptionData sock_opt; long time_left; @@ -1289,9 +1289,13 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) NULL) != SECSuccess) goto error; - if(data->set.ssl.verifypeer && (CURLE_OK != - (curlerr = nss_load_ca_certificates(conn, sockindex)))) - goto error; + if(data->set.ssl.verifypeer) { + const CURLcode rv = nss_load_ca_certificates(conn, sockindex); + if(CURLE_OK != rv) { + curlerr = rv; + goto error; + } + } if (data->set.ssl.CRLfile) { if(SECSuccess != nss_load_crl(data->set.ssl.CRLfile)) { -- cgit v1.2.1 From 806dbb022b8a595405a740131a30fa0cf4523645 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Tue, 15 Mar 2011 14:52:26 +0100 Subject: 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 --- lib/nss.c | 50 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 18 deletions(-) (limited to 'lib/nss.c') 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) { -- cgit v1.2.1 From d3408d05935e61a303b5761cc5aa029eb54408f8 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Mon, 4 Apr 2011 18:24:32 +0200 Subject: nss: fix a crash within SSL_AuthCertificate() The bug was introduced in 806dbb0 (a wrong value was passed in as the first argument to the default callback in our wrapper). --- lib/nss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index a3bd77cfb..7377e72fc 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -633,7 +633,7 @@ static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig, return SECSuccess; } - return SSL_AuthCertificate(arg, fd, checksig, isServer); + return SSL_AuthCertificate(CERT_GetDefaultCertDB(), fd, checksig, isServer); } static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) -- cgit v1.2.1 From 1a6e7da13d1bf14c09cb0509c114ba9bd3cac79f Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Fri, 1 Apr 2011 16:31:28 +0200 Subject: nss: allow to use multiple client certificates for a single host In case a client certificate is used, invalidate SSL session cache at the end of a session. This forces NSS to ask for a new client certificate when connecting second time to the same host. Bug: https://bugzilla.redhat.com/689031 --- lib/nss.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 7377e72fc..d93937755 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1046,8 +1046,6 @@ void Curl_nss_close(struct connectdata *conn, int sockindex) struct ssl_connect_data *connssl = &conn->ssl[sockindex]; if(connssl->handle) { - PR_Close(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]); @@ -1055,12 +1053,17 @@ void Curl_nss_close(struct connectdata *conn, int sockindex) if(connssl->client_nickname != NULL) { free(connssl->client_nickname); connssl->client_nickname = NULL; + + /* force NSS to ask again for a client cert when connecting + * next time to the same server */ + SSL_InvalidateSession(connssl->handle); } #ifdef HAVE_PK11_CREATEGENERICOBJECT /* destroy all NSS objects in order to avoid failure of NSS shutdown */ Curl_llist_destroy(connssl->obj_list, NULL); connssl->obj_list = NULL; #endif + PR_Close(connssl->handle); connssl->handle = NULL; } } -- cgit v1.2.1 From c828646f60b5bffb2bfcf924eba36da767bf08bf Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 20 Apr 2011 00:48:20 +0200 Subject: CURL_DOES_CONVERSIONS: cleanup Massively reduce #ifdefs all over (23 #ifdef lines less so far) Moved conversion-specific code to non-ascii.c --- lib/nss.c | 1 - 1 file changed, 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index d93937755..3677043f0 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -68,7 +68,6 @@ #include "curl_memory.h" #include "rawstr.h" -#include "easyif.h" /* for Curl_convert_from_utf8 prototype */ /* The last #include file should be: */ #include "memdebug.h" -- cgit v1.2.1 From b903186fa0189ff241d756d25d07fdfe9885ae49 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Wed, 20 Apr 2011 15:17:42 +0200 Subject: source cleanup: unify look, style and indent levels By the use of a the new lib/checksrc.pl script that checks that our basic source style rules are followed. --- lib/nss.c | 63 +++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 33 insertions(+), 30 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 3677043f0..3dc0ba61c 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -282,9 +282,9 @@ static int is_file(const char *filename) } /* Return on heap allocated filename/nickname of a certificate. The returned - * string should be later deallocated using free(). *is_nickname is set to TRUE - * if the given string is treated as nickname; FALSE if the given string is - * treated as file name. + * string should be later deallocated using free(). *is_nickname is set to + * TRUE if the given string is treated as nickname; FALSE if the given string + * is treated as file name. */ static char *fmt_nickname(struct SessionHandle *data, enum dupstring cert_kind, bool *is_nickname) @@ -662,7 +662,8 @@ 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); - } else { + } + 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); @@ -778,10 +779,10 @@ static SECStatus check_issuer_cert(PRFileDesc *sock, issuer = NULL; issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win); - if ((!cert_issuer) || (!issuer)) + if((!cert_issuer) || (!issuer)) res = SECFailure; - else if (SECITEM_CompareItem(&cert_issuer->derCert, - &issuer->derCert)!=SECEqual) + else if(SECITEM_CompareItem(&cert_issuer->derCert, + &issuer->derCert)!=SECEqual) res = SECFailure; CERT_DestroyCertificate(cert); @@ -806,8 +807,8 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, struct SessionHandle *data = connssl->data; const char *nickname = connssl->client_nickname; - if (mod && nickname && - 0 == strncmp(nickname, pem_nickname, /* length of "PEM Token" */ 9)) { + if(mod && nickname && + 0 == strncmp(nickname, pem_nickname, /* length of "PEM Token" */ 9)) { /* use the cert/key provided by PEM reader */ PK11SlotInfo *slot; @@ -815,20 +816,20 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, *pRetKey = NULL; *pRetCert = PK11_FindCertFromNickname(nickname, proto_win); - if (NULL == *pRetCert) { + if(NULL == *pRetCert) { failf(data, "NSS: client certificate not found: %s", nickname); return SECFailure; } slot = PK11_FindSlotByName(pem_slotname); - if (NULL == slot) { + if(NULL == slot) { failf(data, "NSS: PK11 slot not found: %s", pem_slotname); return SECFailure; } *pRetKey = PK11_FindPrivateKeyFromCert(slot, *pRetCert, NULL); PK11_FreeSlot(slot); - if (NULL == *pRetKey) { + if(NULL == *pRetKey) { failf(data, "NSS: private key not found for certificate: %s", nickname); return SECFailure; } @@ -839,11 +840,11 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, } /* use the default NSS hook */ - if (SECSuccess != NSS_GetClientAuthData((void *)nickname, sock, caNames, + if(SECSuccess != NSS_GetClientAuthData((void *)nickname, sock, caNames, pRetCert, pRetKey) || NULL == *pRetCert) { - if (NULL == nickname) + if(NULL == nickname) failf(data, "NSS: client certificate not found (nickname not " "specified)"); else @@ -854,10 +855,10 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, /* get certificate nickname if any */ nickname = (*pRetCert)->nickname; - if (NULL == nickname) + if(NULL == nickname) nickname = "[unknown]"; - if (NULL == *pRetKey) { + if(NULL == *pRetKey) { failf(data, "NSS: private key not found for certificate: %s", nickname); return SECFailure; } @@ -931,7 +932,8 @@ static CURLcode init_nss(struct SessionHandle *data) } else { char *certpath = - PR_smprintf("%s%s", NSS_VersionCheck("3.12.0") ? "sql:" : "", cert_dir); + PR_smprintf("%s%s", NSS_VersionCheck("3.12.0") ? "sql:" : "", + cert_dir); rv = NSS_Initialize(certpath, "", "", "", NSS_INIT_READONLY); PR_smprintf_free(certpath); } @@ -957,7 +959,7 @@ static CURLcode init_nss(struct SessionHandle *data) int Curl_nss_init(void) { /* curl_global_init() is not thread-safe so this test is ok */ - if (nss_initlock == NULL) { + if(nss_initlock == NULL) { PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 256); nss_initlock = PR_NewLock(); nss_crllock = PR_NewLock(); @@ -972,9 +974,10 @@ CURLcode Curl_nss_force_init(struct SessionHandle *data) { CURLcode rv; if(!nss_initlock) { - failf(data, "unable to initialize NSS, curl_global_init() should have been " - "called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL"); - return CURLE_OUT_OF_MEMORY; + failf(data, + "unable to initialize NSS, curl_global_init() should have been " + "called with CURL_GLOBAL_SSL or CURL_GLOBAL_ALL"); + return CURLE_FAILED_INIT; } PR_Lock(nss_initlock); @@ -990,7 +993,7 @@ void Curl_nss_cleanup(void) * as a safety feature. */ PR_Lock(nss_initlock); - if (initialized) { + if(initialized) { /* Free references to client certificates held in the SSL session cache. * Omitting this hampers destruction of the security module owning * the certificates. */ @@ -1167,7 +1170,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) long time_left; PRUint32 timeout; - if (connssl->state == ssl_connection_complete) + if(connssl->state == ssl_connection_complete) return CURLE_OK; connssl->data = data; @@ -1240,7 +1243,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) default: case CURL_SSLVERSION_DEFAULT: ssl3 = PR_TRUE; - if (data->state.ssl_connect_retry) + if(data->state.ssl_connect_retry) infof(data, "TLS disabled due to previous handshake failure\n"); else tlsv1 = PR_TRUE; @@ -1271,8 +1274,8 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) /* enable all ciphers from enable_ciphers_by_default */ cipher_to_enable = enable_ciphers_by_default; - while (SSL_NULL_WITH_NULL_NULL != *cipher_to_enable) { - if (SSL_CipherPrefSet(model, *cipher_to_enable, PR_TRUE) != SECSuccess) { + while(SSL_NULL_WITH_NULL_NULL != *cipher_to_enable) { + if(SSL_CipherPrefSet(model, *cipher_to_enable, PR_TRUE) != SECSuccess) { curlerr = CURLE_SSL_CIPHER; goto error; } @@ -1313,7 +1316,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) } } - if (data->set.ssl.CRLfile) { + if(data->set.ssl.CRLfile) { if(SECSuccess != nss_load_crl(data->set.ssl.CRLfile)) { curlerr = CURLE_SSL_CRL_BADFILE; goto error; @@ -1358,7 +1361,7 @@ CURLcode Curl_nss_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]) { + if(data->set.str[STRING_KEY_PASSWD]) { SSL_SetPKCS11PinArg(connssl->handle, data->set.str[STRING_KEY_PASSWD]); } @@ -1390,7 +1393,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) display_conn_info(conn, connssl->handle); - if (data->set.str[STRING_SSL_ISSUERCERT]) { + if(data->set.str[STRING_SSL_ISSUERCERT]) { SECStatus ret = SECFailure; bool is_nickname; char *nickname = fmt_nickname(data, STRING_SSL_ISSUERCERT, &is_nickname); @@ -1434,7 +1437,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) connssl->obj_list = NULL; #endif - if (ssl3 && tlsv1 && isTLSIntoleranceError(err)) { + if(ssl3 && tlsv1 && isTLSIntoleranceError(err)) { /* schedule reconnect through Curl_retry_request() */ data->state.ssl_connect_retry = TRUE; infof(data, "Error in TLS handshake, trying SSLv3...\n"); -- cgit v1.2.1 From f1586cb4775681810afd8e6626e7842d459f3b85 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Tue, 26 Jul 2011 17:23:27 +0200 Subject: stdio.h, stdlib.h, string.h, stdarg.h and ctype.h inclusion done in setup_once.h --- lib/nss.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 3dc0ba61c..7f5acbc84 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -27,9 +27,6 @@ #include "setup.h" -#include -#include -#include #ifdef HAVE_SYS_SOCKET_H #include #endif -- cgit v1.2.1 From d6f319fb64e63f200bf5d892765c69dc66b8db8c Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Mon, 15 Aug 2011 13:48:45 +0200 Subject: nss: start with no database if the selected database is broken Bug: https://bugzilla.redhat.com/728562 --- lib/nss.c | 63 +++++++++++++++++++++++++++++++++++++++------------------------ 1 file changed, 39 insertions(+), 24 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 7f5acbc84..b853b7a3a 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -895,10 +895,42 @@ isTLSIntoleranceError(PRInt32 err) } } -static CURLcode init_nss(struct SessionHandle *data) +static CURLcode nss_init_core(struct SessionHandle *data, const char *cert_dir) +{ + if(NSS_IsInitialized()) + return CURLE_OK; + + if(cert_dir) { + SECStatus rv; + const bool use_sql = NSS_VersionCheck("3.12.0"); + char *certpath = aprintf("%s%s", use_sql ? "sql:" : "", cert_dir); + if(!certpath) + return CURLE_OUT_OF_MEMORY; + + infof(data, "Initializing NSS with certpath: %s\n", certpath); + rv = NSS_Initialize(certpath, "", "", "", NSS_INIT_READONLY); + free(certpath); + + if(rv == SECSuccess) + return CURLE_OK; + + infof(data, "Unable to initialize NSS database\n"); + } + + infof(data, "Initializing NSS with certpath: none\n"); + if(NSS_NoDB_Init(NULL) == SECSuccess) + return CURLE_OK; + + infof(data, "Unable to initialize NSS\n"); + return CURLE_SSL_CACERT_BADFILE; +} + +static CURLcode nss_init(struct SessionHandle *data) { char *cert_dir; struct_stat st; + CURLcode rv; + if(initialized) return CURLE_OK; @@ -919,31 +951,14 @@ static CURLcode init_nss(struct SessionHandle *data) } } - if(!NSS_IsInitialized()) { - SECStatus rv; - initialized = 1; - infof(data, "Initializing NSS with certpath: %s\n", - cert_dir ? cert_dir : "none"); - if(!cert_dir) { - rv = NSS_NoDB_Init(NULL); - } - else { - char *certpath = - PR_smprintf("%s%s", NSS_VersionCheck("3.12.0") ? "sql:" : "", - cert_dir); - rv = NSS_Initialize(certpath, "", "", "", NSS_INIT_READONLY); - PR_smprintf_free(certpath); - } - if(rv != SECSuccess) { - infof(data, "Unable to initialize NSS database\n"); - initialized = 0; - return CURLE_SSL_CACERT_BADFILE; - } - } + rv = nss_init_core(data, cert_dir); + if(rv) + return rv; if(num_enabled_ciphers() == 0) NSS_SetDomesticPolicy(); + initialized = 1; return CURLE_OK; } @@ -978,7 +993,7 @@ CURLcode Curl_nss_force_init(struct SessionHandle *data) } PR_Lock(nss_initlock); - rv = init_nss(data); + rv = nss_init(data); PR_Unlock(nss_initlock); return rv; } @@ -1181,7 +1196,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) /* FIXME. NSS doesn't support multiple databases open at the same time. */ PR_Lock(nss_initlock); - curlerr = init_nss(conn->data); + curlerr = nss_init(conn->data); if(CURLE_OK != curlerr) { PR_Unlock(nss_initlock); goto error; -- cgit v1.2.1 From 6b75d2c2df7209919a70a29a4479625b62fb3c28 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Sat, 3 Sep 2011 16:06:10 +0200 Subject: fix a bunch of MSVC compiler warnings --- lib/nss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index b853b7a3a..fb52402a9 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -92,7 +92,7 @@ typedef struct { ptr->type = (_type); \ ptr->pValue = (_val); \ ptr->ulValueLen = (_len); \ -} while(0) +} WHILE_FALSE #define CERT_NewTempCertificate __CERT_NewTempCertificate -- cgit v1.2.1 From f6980bbf247fc1b035bfa852b2f084e43a8686db Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Fri, 26 Aug 2011 14:38:18 +0200 Subject: nss: select client certificates by DER ... instead of nicknames, which are not unique. --- lib/nss.c | 45 +++++++++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 16 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index fb52402a9..0be303cf0 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -354,6 +354,10 @@ static CURLcode nss_create_object(struct ssl_connect_data *ssl, return CURLE_OUT_OF_MEMORY; } + if(!cacert && CKO_CERTIFICATE == obj_class) + /* store reference to a client certificate */ + ssl->obj_clicert = obj; + return CURLE_OK; } @@ -398,6 +402,7 @@ static int nss_load_cert(struct ssl_connect_data *ssl, nickname = strdup(filename); if(!nickname) return 0; + goto done; } @@ -797,44 +802,51 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, struct CERTCertificateStr **pRetCert, struct SECKEYPrivateKeyStr **pRetKey) { - static const char pem_nickname[] = "PEM Token #1"; - const char *pem_slotname = pem_nickname; - struct ssl_connect_data *connssl = (struct ssl_connect_data *)arg; struct SessionHandle *data = connssl->data; const char *nickname = connssl->client_nickname; - if(mod && nickname && - 0 == strncmp(nickname, pem_nickname, /* length of "PEM Token" */ 9)) { - +#ifdef HAVE_PK11_CREATEGENERICOBJECT + if(connssl->obj_clicert) { /* use the cert/key provided by PEM reader */ - PK11SlotInfo *slot; + static const char pem_slotname[] = "PEM Token #1"; + SECItem cert_der = { 0, NULL, 0 }; void *proto_win = SSL_RevealPinArg(sock); - *pRetKey = NULL; - *pRetCert = PK11_FindCertFromNickname(nickname, proto_win); - if(NULL == *pRetCert) { - failf(data, "NSS: client certificate not found: %s", nickname); + PK11SlotInfo *slot = PK11_FindSlotByName(pem_slotname); + if(NULL == slot) { + failf(data, "NSS: PK11 slot not found: %s", pem_slotname); return SECFailure; } - slot = PK11_FindSlotByName(pem_slotname); - if(NULL == slot) { - failf(data, "NSS: PK11 slot not found: %s", pem_slotname); + if(PK11_ReadRawAttribute(PK11_TypeGeneric, connssl->obj_clicert, CKA_VALUE, + &cert_der) != SECSuccess) { + failf(data, "NSS: CKA_VALUE not found in PK11 generic object"); + PK11_FreeSlot(slot); + return SECFailure; + } + + *pRetCert = PK11_FindCertFromDERCertItem(slot, &cert_der, proto_win); + SECITEM_FreeItem(&cert_der, PR_FALSE); + if(NULL == *pRetCert) { + failf(data, "NSS: client certificate from file not found"); + PK11_FreeSlot(slot); return SECFailure; } *pRetKey = PK11_FindPrivateKeyFromCert(slot, *pRetCert, NULL); PK11_FreeSlot(slot); if(NULL == *pRetKey) { - failf(data, "NSS: private key not found for certificate: %s", nickname); + failf(data, "NSS: private key from file not found"); + CERT_DestroyCertificate(*pRetCert); return SECFailure; } - infof(data, "NSS: client certificate: %s\n", nickname); + infof(data, "NSS: client certificate from file\n"); display_cert_info(data, *pRetCert); return SECSuccess; } +#endif /* use the default NSS hook */ if(SECSuccess != NSS_GetClientAuthData((void *)nickname, sock, caNames, @@ -1076,6 +1088,7 @@ void Curl_nss_close(struct connectdata *conn, int sockindex) /* 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; #endif PR_Close(connssl->handle); connssl->handle = NULL; -- cgit v1.2.1 From 052a08ff59235357726a23fdc116eec3e7587dc2 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Fri, 26 Aug 2011 14:53:26 +0200 Subject: nss: refactor fmt_nickname() -> dup_nickname() Do not use artificial nicknames for certificates from files. --- lib/nss.c | 42 ++++++++++++++---------------------------- 1 file changed, 14 insertions(+), 28 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 0be303cf0..c6ab42f57 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -278,17 +278,16 @@ static int is_file(const char *filename) return 0; } -/* Return on heap allocated filename/nickname of a certificate. The returned - * string should be later deallocated using free(). *is_nickname is set to - * TRUE if the given string is treated as nickname; FALSE if the given string - * is treated as file name. +/* Check if the given string is filename or nickname of a certificate. If the + * given string is recognized as filename, return NULL. If the given string is + * recognized as nickname, return a duplicated string. The returned string + * should be later deallocated using free(). If the OOM failure occurs, we + * return NULL, too. */ -static char *fmt_nickname(struct SessionHandle *data, enum dupstring cert_kind, - bool *is_nickname) +static char* dup_nickname(struct SessionHandle *data, enum dupstring cert_kind) { const char *str = data->set.str[cert_kind]; const char *n; - *is_nickname = TRUE; if(!is_file(str)) /* no such file exists, use the string as nickname */ @@ -303,10 +302,7 @@ static char *fmt_nickname(struct SessionHandle *data, enum dupstring cert_kind, } /* we'll use the PEM reader to read the certificate from file */ - *is_nickname = FALSE; - - n++; /* skip last slash */ - return aprintf("PEM Token #%d:%s", 1, n); + return NULL; } #ifdef HAVE_PK11_CREATEGENERICOBJECT @@ -1352,17 +1348,11 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) } if(data->set.str[STRING_CERT]) { - bool is_nickname; - char *nickname = fmt_nickname(data, STRING_CERT, &is_nickname); - if(!nickname) - return CURLE_OUT_OF_MEMORY; - - if(!is_nickname && !cert_stuff(conn, sockindex, data->set.str[STRING_CERT], - data->set.str[STRING_KEY])) { + char *nickname = dup_nickname(data, STRING_CERT); + if(!nickname && !cert_stuff(conn, sockindex, data->set.str[STRING_CERT], + data->set.str[STRING_KEY])) /* failf() is already done in cert_stuff() */ - free(nickname); return CURLE_SSL_CERTPROBLEM; - } /* store the nickname for SelectClientCert() called during handshake */ connssl->client_nickname = nickname; @@ -1420,16 +1410,12 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if(data->set.str[STRING_SSL_ISSUERCERT]) { SECStatus ret = SECFailure; - bool is_nickname; - char *nickname = fmt_nickname(data, STRING_SSL_ISSUERCERT, &is_nickname); - if(!nickname) - return CURLE_OUT_OF_MEMORY; - - if(is_nickname) + char *nickname = dup_nickname(data, STRING_SSL_ISSUERCERT); + if(nickname) { /* we support only nicknames in case of STRING_SSL_ISSUERCERT for now */ ret = check_issuer_cert(connssl->handle, nickname); - - free(nickname); + free(nickname); + } if(SECFailure == ret) { infof(data,"SSL certificate issuer check failed\n"); -- cgit v1.2.1 From 06e6755e874557e5111e439cfb4ad0249673a90c Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Fri, 26 Aug 2011 15:43:48 +0200 Subject: nss: big cleanup in nss_load_cert() and cert_stuff() --- lib/nss.c | 151 ++++++++++++++++++++++++-------------------------------------- 1 file changed, 58 insertions(+), 93 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index c6ab42f57..25293d5a5 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -319,6 +319,9 @@ static CURLcode nss_create_object(struct ssl_connect_data *ssl, CK_BBOOL ckfalse = CK_FALSE; CK_ATTRIBUTE attrs[/* max count of attributes */ 4]; int attr_cnt = 0; + CURLcode err = (cacert) + ? CURLE_SSL_CACERT_BADFILE + : CURLE_SSL_CERTPROBLEM; const int slot_id = (cacert) ? 0 : 1; char *slot_name = aprintf("PEM Token #%d", slot_id); @@ -328,7 +331,7 @@ static CURLcode nss_create_object(struct ssl_connect_data *ssl, slot = PK11_FindSlotByName(slot_name); free(slot_name); if(!slot) - return CURLE_SSL_CERTPROBLEM; + return err; PK11_SETATTRS(attrs, attr_cnt, CKA_CLASS, &obj_class, sizeof(obj_class)); PK11_SETATTRS(attrs, attr_cnt, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL)); @@ -343,7 +346,7 @@ static CURLcode nss_create_object(struct ssl_connect_data *ssl, obj = PK11_CreateGenericObject(slot, attrs, attr_cnt, PR_FALSE); PK11_FreeSlot(slot); if(!obj) - return CURLE_SSL_CERTPROBLEM; + return err; if(!Curl_llist_insert_next(ssl->obj_list, ssl->obj_list->tail, obj)) { PK11_DestroyGenericObject(obj); @@ -368,77 +371,21 @@ static void nss_destroy_object(void *user, void *ptr) } #endif -static int nss_load_cert(struct ssl_connect_data *ssl, - const char *filename, PRBool cacert) +static CURLcode nss_load_cert(struct ssl_connect_data *ssl, + const char *filename, PRBool cacert) { -#ifdef HAVE_PK11_CREATEGENERICOBJECT - /* All CA and trust objects go into slot 0. Other slots are used - * for storing certificates. - */ - const int slot_id = (cacert) ? 0 : 1; -#endif - CERTCertificate *cert; - char *nickname = NULL; - char *n = NULL; - - /* If there is no slash in the filename it is assumed to be a regular - * NSS nickname. - */ - if(is_file(filename)) { - n = strrchr(filename, '/'); - if(n) - n++; - if(!mod) - return 1; - } - else { - /* A nickname from the NSS internal database */ - if(cacert) - return 0; /* You can't specify an NSS CA nickname this way */ - nickname = strdup(filename); - if(!nickname) - return 0; - - goto done; - } + CURLcode err = (cacert) + ? CURLE_SSL_CACERT_BADFILE + : CURLE_SSL_CERTPROBLEM; #ifdef HAVE_PK11_CREATEGENERICOBJECT - nickname = aprintf("PEM Token #%d:%s", slot_id, n); - if(!nickname) - return 0; - - if(CURLE_OK != nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert)) { - free(nickname); - return 0; - } - -#else - /* We don't have PK11_CreateGenericObject but a file-based cert was passed - * in. We need to fail. - */ - return 0; + /* libnsspem.so leaks memory if the requested file does not exist. For more + * details, go to . */ + if(is_file(filename)) + return nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert); #endif - done: - /* Double-check that the certificate or nickname requested exists in - * either the token or the NSS certificate database. - */ - if(!cacert) { - cert = PK11_FindCertFromNickname((char *)nickname, NULL); - - /* An invalid nickname was passed in */ - if(cert == NULL) { - free(nickname); - PR_SetError(SEC_ERROR_UNKNOWN_CERT, 0); - return 0; - } - - CERT_DestroyCertificate(cert); - } - - free(nickname); - - return 1; + return err; } /* add given CRL to cache if it is not already there */ @@ -527,23 +474,23 @@ fail: return SECFailure; } -static int nss_load_key(struct connectdata *conn, int sockindex, - char *key_file) +static CURLcode nss_load_key(struct connectdata *conn, int sockindex, + char *key_file) { #ifdef HAVE_PK11_CREATEGENERICOBJECT PK11SlotInfo *slot; SECStatus status; struct ssl_connect_data *ssl = conn->ssl; - (void)sockindex; /* unused */ - if(CURLE_OK != nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE)) { + CURLcode rv = nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE); + if(CURLE_OK != rv) { PR_SetError(SEC_ERROR_BAD_KEY, 0); - return 0; + return rv; } slot = PK11_FindSlotByName("PEM Token #1"); if(!slot) - return 0; + return CURLE_SSL_CERTPROBLEM; /* This will force the token to be seen as re-inserted */ SECMOD_WaitForAnyTokenEvent(mod, 0, 0); @@ -552,16 +499,18 @@ static int nss_load_key(struct connectdata *conn, int sockindex, status = PK11_Authenticate(slot, PR_TRUE, conn->data->set.str[STRING_KEY_PASSWD]); PK11_FreeSlot(slot); - return (SECSuccess == status) ? 1 : 0; + return (SECSuccess == status) + ? CURLE_OK + : CURLE_SSL_CERTPROBLEM; #else /* If we don't have PK11_CreateGenericObject then we can't load a file-based * key. */ (void)conn; /* unused */ (void)key_file; /* unused */ - (void)sockindex; /* unused */ - return 0; + return CURLE_SSL_CERTPROBLEM; #endif + (void)sockindex; /* unused */ } static int display_error(struct connectdata *conn, PRInt32 err, @@ -580,34 +529,37 @@ static int display_error(struct connectdata *conn, PRInt32 err, return 0; /* The caller will print a generic error */ } -static int cert_stuff(struct connectdata *conn, - int sockindex, char *cert_file, char *key_file) +static CURLcode cert_stuff(struct connectdata *conn, int sockindex, + char *cert_file, char *key_file) { struct SessionHandle *data = conn->data; - int rv = 0; + CURLcode rv; if(cert_file) { rv = nss_load_cert(&conn->ssl[sockindex], cert_file, PR_FALSE); - if(!rv) { + if(CURLE_OK != rv) { if(!display_error(conn, PR_GetError(), cert_file)) failf(data, "Unable to load client cert %d.", PR_GetError()); - return 0; + + return rv; } } + if(key_file || (is_file(cert_file))) { if(key_file) rv = nss_load_key(conn, sockindex, key_file); else /* In case the cert file also has the key */ rv = nss_load_key(conn, sockindex, cert_file); - if(!rv) { + if(CURLE_OK != rv) { if(!display_error(conn, PR_GetError(), key_file)) failf(data, "Unable to load client key %d.", PR_GetError()); - return 0; + return rv; } } - return 1; + + return CURLE_OK; } static char * nss_get_password(PK11SlotInfo * slot, PRBool retry, void *arg) @@ -774,7 +726,6 @@ static SECStatus check_issuer_cert(PRFileDesc *sock, cert_issuer = CERT_FindCertIssuer(cert,PR_Now(),certUsageObjectSigner); proto_win = SSL_RevealPinArg(sock); - issuer = NULL; issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win); if((!cert_issuer) || (!issuer)) @@ -1132,8 +1083,11 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn, const char *cafile = data->set.ssl.CAfile; const char *capath = data->set.ssl.CApath; - if(cafile && !nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE)) - return CURLE_SSL_CACERT_BADFILE; + if(cafile) { + CURLcode rv = nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE); + if(CURLE_OK != rv) + return rv; + } if(capath) { struct_stat st; @@ -1153,7 +1107,7 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn, return CURLE_OUT_OF_MEMORY; } - if(!nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE)) + if(CURLE_OK != nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE)) /* This is purposefully tolerant of errors so non-PEM files can * be in the same directory */ infof(data, "failed to load '%s' from CURLOPT_CAPATH\n", fullpath); @@ -1349,10 +1303,21 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if(data->set.str[STRING_CERT]) { char *nickname = dup_nickname(data, STRING_CERT); - if(!nickname && !cert_stuff(conn, sockindex, data->set.str[STRING_CERT], - data->set.str[STRING_KEY])) - /* failf() is already done in cert_stuff() */ - return CURLE_SSL_CERTPROBLEM; + if(nickname) { + /* we are not going to use libnsspem.so to read the client cert */ +#ifdef HAVE_PK11_CREATEGENERICOBJECT + connssl->obj_clicert = NULL; +#endif + } + else { + CURLcode rv = cert_stuff(conn, sockindex, data->set.str[STRING_CERT], + data->set.str[STRING_KEY]); + if(CURLE_OK != rv) { + /* failf() is already done in cert_stuff() */ + curlerr = rv; + goto error; + } + } /* store the nickname for SelectClientCert() called during handshake */ connssl->client_nickname = nickname; -- cgit v1.2.1 From 491c5a497cc4cab0a488a0c94eec7d518d57d304 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Tue, 6 Sep 2011 18:17:38 +0200 Subject: nss: avoid a SIGSEGV with immature version of NSS Bug: https://bugzilla.redhat.com/733685 --- lib/nss.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 25293d5a5..f63d9718b 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -382,7 +382,29 @@ static CURLcode nss_load_cert(struct ssl_connect_data *ssl, /* libnsspem.so leaks memory if the requested file does not exist. For more * details, go to . */ if(is_file(filename)) - return nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert); + err = nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert); + + if(CURLE_OK == err && !cacert) { + /* we have successfully loaded a client certificate */ + CERTCertificate *cert; + char *nickname = NULL; + char *n = strrchr(filename, '/'); + if(n) + n++; + + /* The following undocumented magic helps to avoid a SIGSEGV on call + * of PK11_ReadRawAttribute() from SelectClientCert() when using an + * immature version of libnsspem.so. For more details, go to + * . */ + nickname = aprintf("PEM Token #1:%s", n); + if(nickname) { + cert = PK11_FindCertFromNickname(nickname, NULL); + if(cert) + CERT_DestroyCertificate(cert); + + free(nickname); + } + } #endif return err; -- cgit v1.2.1 From ebf31389927dd1f514c0a7092a6ba52ad003ad95 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Wed, 8 Feb 2012 13:36:36 +0100 Subject: nss: add support for the CURLSSLOPT_ALLOW_BEAST option ... and fix some typos from the 62d15f1 commit. --- lib/nss.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index f63d9718b..8f6da50ea 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1158,6 +1158,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) PRBool ssl3 = PR_FALSE; PRBool tlsv1 = PR_FALSE; PRBool ssl_no_cache; + PRBool ssl_cbc_random_iv; struct SessionHandle *data = conn->data; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; @@ -1266,6 +1267,18 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if(SSL_OptionSet(model, SSL_V2_COMPATIBLE_HELLO, ssl2) != SECSuccess) goto error; + 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 */ + if(SSL_OptionSet(model, SSL_CBC_RANDOM_IV, ssl_cbc_random_iv) != SECSuccess) + infof(data, "warning: failed to set SSL_CBC_RANDOM_IV = %d\n", + ssl_cbc_random_iv); +#else + if(ssl_cbc_random_iv) + infof(data, "warning: support for SSL_CBC_RANDOM_IV not compiled in\n"); +#endif + /* reset the flag to avoid an infinite loop */ data->state.ssl_connect_retry = FALSE; -- cgit v1.2.1 From 42aa796150a580a0adff714c157d3b38b7672c7f Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Fri, 6 Apr 2012 16:05:25 +0200 Subject: nss: unconditionally require PK11_CreateGenericObject() This bumps the minimal supported version of NSS to 3.12.x. --- lib/nss.c | 30 ++---------------------------- 1 file changed, 2 insertions(+), 28 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 8f6da50ea..61089173c 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -170,9 +170,7 @@ static const int enable_ciphers_by_default[] = { SSL_NULL_WITH_NULL_NULL }; -#ifdef HAVE_PK11_CREATEGENERICOBJECT static const char* pem_library = "libnsspem.so"; -#endif SECMODModule* mod = NULL; static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model, @@ -305,7 +303,6 @@ static char* dup_nickname(struct SessionHandle *data, enum dupstring cert_kind) return NULL; } -#ifdef HAVE_PK11_CREATEGENERICOBJECT /* Call PK11_CreateGenericObject() with the given obj_class and filename. If * the call succeeds, append the object handle to the list of objects so that * the object can be destroyed in Curl_nss_close(). */ @@ -369,7 +366,6 @@ static void nss_destroy_object(void *user, void *ptr) (void) user; PK11_DestroyGenericObject(obj); } -#endif static CURLcode nss_load_cert(struct ssl_connect_data *ssl, const char *filename, PRBool cacert) @@ -378,7 +374,6 @@ static CURLcode nss_load_cert(struct ssl_connect_data *ssl, ? CURLE_SSL_CACERT_BADFILE : CURLE_SSL_CERTPROBLEM; -#ifdef HAVE_PK11_CREATEGENERICOBJECT /* libnsspem.so leaks memory if the requested file does not exist. For more * details, go to . */ if(is_file(filename)) @@ -405,7 +400,6 @@ static CURLcode nss_load_cert(struct ssl_connect_data *ssl, free(nickname); } } -#endif return err; } @@ -499,10 +493,10 @@ fail: static CURLcode nss_load_key(struct connectdata *conn, int sockindex, char *key_file) { -#ifdef HAVE_PK11_CREATEGENERICOBJECT PK11SlotInfo *slot; SECStatus status; struct ssl_connect_data *ssl = conn->ssl; + (void)sockindex; /* unused */ CURLcode rv = nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE); if(CURLE_OK != rv) { @@ -524,15 +518,6 @@ static CURLcode nss_load_key(struct connectdata *conn, int sockindex, return (SECSuccess == status) ? CURLE_OK : CURLE_SSL_CERTPROBLEM; -#else - /* If we don't have PK11_CreateGenericObject then we can't load a file-based - * key. - */ - (void)conn; /* unused */ - (void)key_file; /* unused */ - return CURLE_SSL_CERTPROBLEM; -#endif - (void)sockindex; /* unused */ } static int display_error(struct connectdata *conn, PRInt32 err, @@ -775,7 +760,6 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, struct SessionHandle *data = connssl->data; const char *nickname = connssl->client_nickname; -#ifdef HAVE_PK11_CREATEGENERICOBJECT if(connssl->obj_clicert) { /* use the cert/key provided by PEM reader */ static const char pem_slotname[] = "PEM Token #1"; @@ -815,7 +799,6 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, display_cert_info(data, *pRetCert); return SECSuccess; } -#endif /* use the default NSS hook */ if(SECSuccess != NSS_GetClientAuthData((void *)nickname, sock, caNames, @@ -1053,12 +1036,11 @@ void Curl_nss_close(struct connectdata *conn, int sockindex) * next time to the same server */ SSL_InvalidateSession(connssl->handle); } -#ifdef HAVE_PK11_CREATEGENERICOBJECT /* 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; -#endif + PR_Close(connssl->handle); connssl->handle = NULL; } @@ -1173,12 +1155,10 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) connssl->data = data; -#ifdef HAVE_PK11_CREATEGENERICOBJECT /* list of all NSS objects we need to destroy in Curl_nss_close() */ connssl->obj_list = Curl_llist_alloc(nss_destroy_object); if(!connssl->obj_list) return CURLE_OUT_OF_MEMORY; -#endif /* FIXME. NSS doesn't support multiple databases open at the same time. */ PR_Lock(nss_initlock); @@ -1190,7 +1170,6 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) curlerr = CURLE_SSL_CONNECT_ERROR; -#ifdef HAVE_PK11_CREATEGENERICOBJECT if(!mod) { char *configstring = aprintf("library=%s name=PEM", pem_library); if(!configstring) { @@ -1209,7 +1188,6 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) "OpenSSL PEM certificates will not work.\n", pem_library); } } -#endif PK11_SetPasswordFunc(nss_get_password); PR_Unlock(nss_initlock); @@ -1340,9 +1318,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) char *nickname = dup_nickname(data, STRING_CERT); if(nickname) { /* we are not going to use libnsspem.so to read the client cert */ -#ifdef HAVE_PK11_CREATEGENERICOBJECT connssl->obj_clicert = NULL; -#endif } else { CURLcode rv = cert_stuff(conn, sockindex, data->set.str[STRING_CERT], @@ -1442,11 +1418,9 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if(model) PR_Close(model); -#ifdef HAVE_PK11_CREATEGENERICOBJECT /* cleanup on connection failure */ Curl_llist_destroy(connssl->obj_list, NULL); connssl->obj_list = NULL; -#endif if(ssl3 && tlsv1 && isTLSIntoleranceError(err)) { /* schedule reconnect through Curl_retry_request() */ -- cgit v1.2.1 From 20cb12db8df6f956b885a5215bcffd425f2d34dd Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Tue, 10 Apr 2012 15:42:34 +0200 Subject: nss: use NSS_InitContext() to initialize NSS if available NSS_InitContext() was introduced in NSS 3.12.5 and helps to prevent collisions on NSS initialization/shutdown with other libraries. Bug: https://bugzilla.redhat.com/738456 --- lib/nss.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 61089173c..16127ee7f 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -78,6 +78,9 @@ PRFileDesc *PR_ImportTCPSocket(PRInt32 osfd); PRLock * nss_initlock = NULL; PRLock * nss_crllock = NULL; +#ifdef HAVE_NSS_INITCONTEXT +NSSInitContext * nss_context = NULL; +#endif volatile int initialized = 0; @@ -861,29 +864,56 @@ isTLSIntoleranceError(PRInt32 err) static CURLcode nss_init_core(struct SessionHandle *data, const char *cert_dir) { +#ifdef HAVE_NSS_INITCONTEXT + if(nss_context != NULL) + return CURLE_OK; + + NSSInitParameters initparams; + memset((void *) &initparams, '\0', sizeof(initparams)); + initparams.length = sizeof(initparams); +#else /* HAVE_NSS_INITCONTEXT */ + SECStatus rv; + if(NSS_IsInitialized()) return CURLE_OK; +#endif if(cert_dir) { - SECStatus rv; const bool use_sql = NSS_VersionCheck("3.12.0"); char *certpath = aprintf("%s%s", use_sql ? "sql:" : "", cert_dir); if(!certpath) return CURLE_OUT_OF_MEMORY; infof(data, "Initializing NSS with certpath: %s\n", certpath); +#ifdef HAVE_NSS_INITCONTEXT + nss_context = NSS_InitContext(certpath, "", "", "", &initparams, + NSS_INIT_READONLY | NSS_INIT_PK11RELOAD); + free(certpath); + + if(nss_context != NULL) + return CURLE_OK; +#else /* HAVE_NSS_INITCONTEXT */ rv = NSS_Initialize(certpath, "", "", "", NSS_INIT_READONLY); free(certpath); if(rv == SECSuccess) return CURLE_OK; +#endif infof(data, "Unable to initialize NSS database\n"); } infof(data, "Initializing NSS with certpath: none\n"); +#ifdef HAVE_NSS_INITCONTEXT + nss_context = NSS_InitContext("", "", "", "", &initparams, NSS_INIT_READONLY + | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN + | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD); + if(nss_context != NULL) + return CURLE_OK; +#else /* HAVE_NSS_INITCONTEXT */ if(NSS_NoDB_Init(NULL) == SECSuccess) return CURLE_OK; +#endif infof(data, "Unable to initialize NSS\n"); return CURLE_SSL_CACERT_BADFILE; @@ -979,7 +1009,12 @@ void Curl_nss_cleanup(void) SECMOD_DestroyModule(mod); mod = NULL; } +#ifdef HAVE_NSS_INITCONTEXT + NSS_ShutdownContext(nss_context); + nss_context = NULL; +#else /* HAVE_NSS_INITCONTEXT */ NSS_Shutdown(); +#endif } PR_Unlock(nss_initlock); -- cgit v1.2.1 From a60edcc6d445e7e0517589cf5fda2682bd89e34e Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Wed, 11 Apr 2012 13:44:20 +0200 Subject: nss: provide human-readable names for NSS errors --- lib/nss.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 16127ee7f..6002391f8 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -62,6 +62,7 @@ #include #include #include +#include #include "curl_memory.h" #include "rawstr.h" @@ -176,6 +177,15 @@ static const int enable_ciphers_by_default[] = { static const char* pem_library = "libnsspem.so"; SECMODModule* mod = NULL; +static const char* nss_error_to_name(PRErrorCode code) +{ + const char *name = PR_ErrorToName(code); + if(name) + return name; + + return "unknown error"; +} + static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model, char *cipher_list) { @@ -548,8 +558,11 @@ static CURLcode cert_stuff(struct connectdata *conn, int sockindex, if(cert_file) { rv = nss_load_cert(&conn->ssl[sockindex], cert_file, PR_FALSE); if(CURLE_OK != rv) { - if(!display_error(conn, PR_GetError(), cert_file)) - failf(data, "Unable to load client cert %d.", PR_GetError()); + const PRErrorCode err = PR_GetError(); + if(!display_error(conn, err, cert_file)) { + const char *err_name = nss_error_to_name(err); + failf(data, "unable to load client cert: %d (%s)", err, err_name); + } return rv; } @@ -562,8 +575,11 @@ static CURLcode cert_stuff(struct connectdata *conn, int sockindex, /* In case the cert file also has the key */ rv = nss_load_key(conn, sockindex, cert_file); if(CURLE_OK != rv) { - if(!display_error(conn, PR_GetError(), key_file)) - failf(data, "Unable to load client key %d.", PR_GetError()); + const PRErrorCode err = PR_GetError(); + if(!display_error(conn, err, key_file)) { + const char *err_name = nss_error_to_name(err); + failf(data, "unable to load client key: %d (%s)", err, err_name); + } return rv; } @@ -1448,7 +1464,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) if(handle_cc_error(err, data)) curlerr = CURLE_SSL_CERTPROBLEM; else - infof(data, "NSS error %d\n", err); + infof(data, "NSS error %d (%s)\n", err, nss_error_to_name(err)); if(model) PR_Close(model); @@ -1484,7 +1500,8 @@ static ssize_t nss_send(struct connectdata *conn, /* connection data */ else if(handle_cc_error(err, conn->data)) *curlcode = CURLE_SSL_CERTPROBLEM; else { - failf(conn->data, "SSL write: error %d", err); + const char *err_name = nss_error_to_name(err); + failf(conn->data, "SSL write: error %d (%s)", err, err_name); *curlcode = CURLE_SEND_ERROR; } return -1; @@ -1510,7 +1527,8 @@ static ssize_t nss_recv(struct connectdata * conn, /* connection data */ else if(handle_cc_error(err, conn->data)) *curlcode = CURLE_SSL_CERTPROBLEM; else { - failf(conn->data, "SSL read: errno %d", err); + const char *err_name = nss_error_to_name(err); + failf(conn->data, "SSL read: errno %d (%s)", err, err_name); *curlcode = CURLE_RECV_ERROR; } return -1; -- cgit v1.2.1 From c156b916a403c0732f2b033bef20517fc66922ea Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Fri, 13 Apr 2012 18:26:42 +0200 Subject: nss.c: fix compiler warning --- lib/nss.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 6002391f8..e3298947f 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -508,10 +508,11 @@ static CURLcode nss_load_key(struct connectdata *conn, int sockindex, { PK11SlotInfo *slot; SECStatus status; + CURLcode rv; struct ssl_connect_data *ssl = conn->ssl; (void)sockindex; /* unused */ - CURLcode rv = nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE); + rv = nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE); if(CURLE_OK != rv) { PR_SetError(SEC_ERROR_BAD_KEY, 0); return rv; @@ -922,8 +923,8 @@ static CURLcode nss_init_core(struct SessionHandle *data, const char *cert_dir) infof(data, "Initializing NSS with certpath: none\n"); #ifdef HAVE_NSS_INITCONTEXT nss_context = NSS_InitContext("", "", "", "", &initparams, NSS_INIT_READONLY - | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN - | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD); + | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN + | NSS_INIT_NOROOTINIT | NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD); if(nss_context != NULL) return CURLE_OK; #else /* HAVE_NSS_INITCONTEXT */ -- cgit v1.2.1 From a498daa0e0793ef0529b61ba1df31aeb9057d33c Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Mon, 16 Apr 2012 22:37:12 +0200 Subject: nss.c: fix compiler warning --- lib/nss.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index e3298947f..6b9389343 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -882,10 +882,11 @@ isTLSIntoleranceError(PRInt32 err) static CURLcode nss_init_core(struct SessionHandle *data, const char *cert_dir) { #ifdef HAVE_NSS_INITCONTEXT + NSSInitParameters initparams; + if(nss_context != NULL) return CURLE_OK; - NSSInitParameters initparams; memset((void *) &initparams, '\0', sizeof(initparams)); initparams.length = sizeof(initparams); #else /* HAVE_NSS_INITCONTEXT */ -- cgit v1.2.1 From 74be99357669da26900ae23b5005ddeabb91b453 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Mon, 21 May 2012 16:31:21 +0200 Subject: nss: avoid using explicit casts of code pointers --- lib/nss.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 6b9389343..885a120bb 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -670,11 +670,10 @@ static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) /** * Inform the application that the handshake is complete. */ -static SECStatus HandshakeCallback(PRFileDesc *sock, void *arg) +static void HandshakeCallback(PRFileDesc *sock, void *arg) { (void)sock; (void)arg; - return SECSuccess; } static void display_cert_info(struct SessionHandle *data, @@ -1341,12 +1340,10 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) goto error; data->set.ssl.certverifyresult=0; /* not checked yet */ - if(SSL_BadCertHook(model, (SSLBadCertHandler) BadCertHandler, conn) - != SECSuccess) { + if(SSL_BadCertHook(model, BadCertHandler, conn) != SECSuccess) goto error; - } - if(SSL_HandshakeCallback(model, (SSLHandshakeCallback) HandshakeCallback, - NULL) != SECSuccess) + + if(SSL_HandshakeCallback(model, HandshakeCallback, NULL) != SECSuccess) goto error; if(data->set.ssl.verifypeer) { -- cgit v1.2.1 From 72f4b534c4f57a71c458257c4ea317d557cf59f5 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Mon, 21 May 2012 16:19:12 +0200 Subject: nss: use human-readable error messages provided by NSS Bug: http://lists.baseurl.org/pipermail/yum-devel/2012-January/009002.html --- lib/nss.c | 128 ++++++++++++++++++++++++++++---------------------------------- 1 file changed, 57 insertions(+), 71 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 885a120bb..d60b18479 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -186,6 +186,11 @@ static const char* nss_error_to_name(PRErrorCode code) return "unknown error"; } +static void nss_print_error_message(struct SessionHandle *data, PRUint32 err) +{ + failf(data, "%s", PR_ErrorToString(err, PR_LANGUAGE_I_DEFAULT)); +} + static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model, char *cipher_list) { @@ -612,61 +617,6 @@ static SECStatus nss_auth_cert_hook(void *arg, PRFileDesc *fd, PRBool checksig, return SSL_AuthCertificate(CERT_GetDefaultCertDB(), fd, checksig, isServer); } -static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) -{ - SECStatus result = SECFailure; - struct connectdata *conn = (struct connectdata *)arg; - PRErrorCode err = PR_GetError(); - CERTCertificate *cert = NULL; - char *subject, *subject_cn, *issuer; - - conn->data->set.ssl.certverifyresult=err; - cert = SSL_PeerCertificate(sock); - subject = CERT_NameToAscii(&cert->subject); - subject_cn = CERT_GetCommonName(&cert->subject); - issuer = CERT_NameToAscii(&cert->issuer); - CERT_DestroyCertificate(cert); - - switch(err) { - case SEC_ERROR_CA_CERT_INVALID: - infof(conn->data, "Issuer certificate is invalid: '%s'\n", issuer); - break; - case SEC_ERROR_UNTRUSTED_ISSUER: - infof(conn->data, "Certificate is signed by an untrusted issuer: '%s'\n", - issuer); - break; - case SSL_ERROR_BAD_CERT_DOMAIN: - 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); - } - 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: - infof(conn->data, "Remote Certificate has expired.\n"); - break; - case SEC_ERROR_UNKNOWN_ISSUER: - infof(conn->data, "Peer's certificate issuer is not recognized: '%s'\n", - issuer); - break; - default: - infof(conn->data, "Bad certificate received. Subject = '%s', " - "Issuer = '%s'\n", subject, issuer); - break; - } - if(result == SECSuccess) - infof(conn->data, "SSL certificate verify ok.\n"); - PR_Free(subject); - PR_Free(subject_cn); - PR_Free(issuer); - - return result; -} - /** * Inform the application that the handshake is complete. */ @@ -728,6 +678,31 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc *sock) return; } +static SECStatus BadCertHandler(void *arg, PRFileDesc *sock) +{ + struct connectdata *conn = (struct connectdata *)arg; + struct SessionHandle *data = conn->data; + PRErrorCode err = PR_GetError(); + CERTCertificate *cert; + + /* remember the cert verification result */ + data->set.ssl.certverifyresult = err; + + if(err == SSL_ERROR_BAD_CERT_DOMAIN && !data->set.ssl.verifyhost) + /* we are asked not to verify the host name */ + return SECSuccess; + + /* print only info about the cert, the error is printed off the callback */ + cert = SSL_PeerCertificate(sock); + if(cert) { + infof(data, "Server certificate:\n"); + display_cert_info(data, cert); + CERT_DestroyCertificate(cert); + } + + return SECFailure; +} + /** * * Check that the Peer certificate's issuer certificate matches the one found @@ -1108,20 +1083,17 @@ int Curl_nss_close_all(struct SessionHandle *data) return 0; } -/* handle client certificate related errors if any; return false otherwise */ -static bool handle_cc_error(PRInt32 err, struct SessionHandle *data) +/* return true if the given error code is related to a client certificate */ +static bool is_cc_error(PRInt32 err) { switch(err) { case SSL_ERROR_BAD_CERT_ALERT: - failf(data, "SSL error: SSL_ERROR_BAD_CERT_ALERT"); return true; case SSL_ERROR_REVOKED_CERT_ALERT: - failf(data, "SSL error: SSL_ERROR_REVOKED_CERT_ALERT"); return true; case SSL_ERROR_EXPIRED_CERT_ALERT: - failf(data, "SSL error: SSL_ERROR_EXPIRED_CERT_ALERT"); return true; default: @@ -1460,10 +1432,14 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) data->state.ssl_connect_retry = FALSE; err = PR_GetError(); - if(handle_cc_error(err, data)) + if(is_cc_error(err)) curlerr = CURLE_SSL_CERTPROBLEM; - else - infof(data, "NSS error %d (%s)\n", err, nss_error_to_name(err)); + + /* print the error number and error string */ + infof(data, "NSS error %d (%s)\n", err, nss_error_to_name(err)); + + /* print a human-readable message describing the error if available */ + nss_print_error_message(data, err); if(model) PR_Close(model); @@ -1496,12 +1472,17 @@ static ssize_t nss_send(struct connectdata *conn, /* connection data */ PRInt32 err = PR_GetError(); if(err == PR_WOULD_BLOCK_ERROR) *curlcode = CURLE_AGAIN; - else if(handle_cc_error(err, conn->data)) - *curlcode = CURLE_SSL_CERTPROBLEM; else { + /* print the error number and error string */ const char *err_name = nss_error_to_name(err); - failf(conn->data, "SSL write: error %d (%s)", err, err_name); - *curlcode = CURLE_SEND_ERROR; + infof(conn->data, "SSL write: error %d (%s)\n", err, err_name); + + /* print a human-readable message describing the error if available */ + nss_print_error_message(conn->data, err); + + *curlcode = (is_cc_error(err)) + ? CURLE_SSL_CERTPROBLEM + : CURLE_SEND_ERROR; } return -1; } @@ -1523,12 +1504,17 @@ static ssize_t nss_recv(struct connectdata * conn, /* connection data */ if(err == PR_WOULD_BLOCK_ERROR) *curlcode = CURLE_AGAIN; - else if(handle_cc_error(err, conn->data)) - *curlcode = CURLE_SSL_CERTPROBLEM; else { + /* print the error number and error string */ const char *err_name = nss_error_to_name(err); - failf(conn->data, "SSL read: errno %d (%s)", err, err_name); - *curlcode = CURLE_RECV_ERROR; + infof(conn->data, "SSL read: errno %d (%s)\n", err, err_name); + + /* print a human-readable message describing the error if available */ + nss_print_error_message(conn->data, err); + + *curlcode = (is_cc_error(err)) + ? CURLE_SSL_CERTPROBLEM + : CURLE_RECV_ERROR; } return -1; } -- cgit v1.2.1 From 849179ba2739ab9a0ad079384b125d9c1745db5f Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 26 Jun 2012 14:52:46 +0200 Subject: SSL cleanup: use crypto functions through the sslgen layer curl_ntlm_msgs.c would previously use an #ifdef maze and direct SSL-library calls instead of using the SSL layer we have for this purpose. --- lib/nss.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index d60b18479..cb742c1b0 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1533,4 +1533,24 @@ int Curl_nss_seed(struct SessionHandle *data) return 0; } +void Curl_nss_random(struct SessionHandle *data, + unsigned char *entropy, + size_t length) +{ + Curl_nss_seed(data); /* Initiate the seed if not already done */ + PK11_GenerateRandom(entropy, length); +} + +void Curl_nss_md5sum(unsigned char *tmp, /* input */ + size_t tmplen, + unsigned char *md5sum, /* output */ + size_t md5len) +{ + PK11Context *MD5pw = PK11_CreateDigestContext(SEC_OID_MD5); + unsigned int MD5out; + PK11_DigestOp(MD5pw, tmp, tmplen); + PK11_DigestFinal(MD5pw, md5sum, &MD5out, md5len); + PK11_DestroyContext(MD5pw, PR_TRUE); +} + #endif /* USE_NSS */ -- cgit v1.2.1 From c0f2bfb2c763fdbaa1fd012033fe14b0e3c467b1 Mon Sep 17 00:00:00 2001 From: Marc Hoersken Date: Thu, 28 Jun 2012 15:48:32 +0200 Subject: nss.c: Fixed size_t conversion warnings --- lib/nss.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index cb742c1b0..a7957736d 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1538,7 +1538,7 @@ void Curl_nss_random(struct SessionHandle *data, size_t length) { Curl_nss_seed(data); /* Initiate the seed if not already done */ - PK11_GenerateRandom(entropy, length); + PK11_GenerateRandom(entropy, curlx_uztosi(length)); } void Curl_nss_md5sum(unsigned char *tmp, /* input */ @@ -1548,8 +1548,8 @@ void Curl_nss_md5sum(unsigned char *tmp, /* input */ { PK11Context *MD5pw = PK11_CreateDigestContext(SEC_OID_MD5); unsigned int MD5out; - PK11_DigestOp(MD5pw, tmp, tmplen); - PK11_DigestFinal(MD5pw, md5sum, &MD5out, md5len); + PK11_DigestOp(MD5pw, tmp, curlx_uztoui(tmplen)); + PK11_DigestFinal(MD5pw, md5sum, &MD5out, curlx_uztoui(md5len)); PK11_DestroyContext(MD5pw, PR_TRUE); } -- cgit v1.2.1 From ac6111aeb09dd258154afdecb289898674435813 Mon Sep 17 00:00:00 2001 From: Yang Tse Date: Thu, 28 Jun 2012 16:58:07 +0200 Subject: nss.c: #include warnless.h for curlx_uztosi and curlx_uztoui prototypes --- lib/nss.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index a7957736d..b11796cef 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -27,6 +27,8 @@ #include "setup.h" +#ifdef USE_NSS + #ifdef HAVE_SYS_SOCKET_H #include #endif @@ -44,8 +46,6 @@ #define _MPRINTF_REPLACE /* use the internal *printf() functions */ #include -#ifdef USE_NSS - #include "nssg.h" #include #include @@ -66,6 +66,7 @@ #include "curl_memory.h" #include "rawstr.h" +#include "warnless.h" /* The last #include file should be: */ #include "memdebug.h" -- cgit v1.2.1 From 52b6eda4f2a006e33358c6964ef6a00b09ae59ab Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Thu, 9 Aug 2012 09:40:00 +0200 Subject: nss: do not print misleading NSS error codes --- lib/nss.c | 42 ++++++++++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 12 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index b11796cef..a8e08f419 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1084,17 +1084,31 @@ int Curl_nss_close_all(struct SessionHandle *data) return 0; } -/* return true if the given error code is related to a client certificate */ -static bool is_cc_error(PRInt32 err) +/* return true if NSS can provide error code (and possibly msg) for the error */ +static bool is_nss_error(CURLcode err) { switch(err) { - case SSL_ERROR_BAD_CERT_ALERT: + case CURLE_PEER_FAILED_VERIFICATION: + case CURLE_SSL_CACERT: + case CURLE_SSL_CACERT_BADFILE: + case CURLE_SSL_CERTPROBLEM: + case CURLE_SSL_CONNECT_ERROR: + case CURLE_SSL_CRL_BADFILE: + case CURLE_SSL_ISSUER_ERROR: return true; - case SSL_ERROR_REVOKED_CERT_ALERT: - return true; + default: + return false; + } +} +/* return true if the given error code is related to a client certificate */ +static bool is_cc_error(PRInt32 err) +{ + switch(err) { + case SSL_ERROR_BAD_CERT_ALERT: case SSL_ERROR_EXPIRED_CERT_ALERT: + case SSL_ERROR_REVOKED_CERT_ALERT: return true; default: @@ -1388,6 +1402,7 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) time_left = Curl_timeleft(data, NULL, TRUE); if(time_left < 0L) { failf(data, "timed out before SSL handshake"); + curlerr = CURLE_OPERATION_TIMEDOUT; goto error; } timeout = PR_MillisecondsToInterval((PRUint32) time_left); @@ -1432,15 +1447,18 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) /* reset the flag to avoid an infinite loop */ data->state.ssl_connect_retry = FALSE; - err = PR_GetError(); - if(is_cc_error(err)) - curlerr = CURLE_SSL_CERTPROBLEM; + if(is_nss_error(curlerr)) { + /* read NSPR error code */ + err = PR_GetError(); + if(is_cc_error(err)) + curlerr = CURLE_SSL_CERTPROBLEM; - /* print the error number and error string */ - infof(data, "NSS error %d (%s)\n", err, nss_error_to_name(err)); + /* print the error number and error string */ + infof(data, "NSS error %d (%s)\n", err, nss_error_to_name(err)); - /* print a human-readable message describing the error if available */ - nss_print_error_message(data, err); + /* print a human-readable message describing the error if available */ + nss_print_error_message(data, err); + } if(model) PR_Close(model); -- cgit v1.2.1 From f208bf5a2d622ae525690dfba2ab58abd8d72264 Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Thu, 9 Aug 2012 14:08:11 +0200 Subject: docs: update the links to cipher-suites supported by NSS ... and make the list of cipher-suites in nss.c readable by humans. Bug: http://curl.haxx.se/mail/archive-2012-08/0016.html --- lib/nss.c | 105 ++++++++++++++++++++++++++++++-------------------------------- 1 file changed, 51 insertions(+), 54 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index a8e08f419..26f35eb0f 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -89,7 +89,6 @@ volatile int initialized = 0; typedef struct { const char *name; int num; - PRInt32 version; /* protocol version valid for this cipher */ } cipher_s; #define PK11_SETATTRS(_attr, _idx, _type, _val, _len) do { \ @@ -101,65 +100,63 @@ typedef struct { #define CERT_NewTempCertificate __CERT_NewTempCertificate -enum sslversion { SSL2 = 1, SSL3 = 2, TLS = 4 }; - #define NUM_OF_CIPHERS sizeof(cipherlist)/sizeof(cipherlist[0]) static const cipher_s cipherlist[] = { /* SSL2 cipher suites */ - {"rc4", SSL_EN_RC4_128_WITH_MD5, SSL2}, - {"rc4-md5", SSL_EN_RC4_128_WITH_MD5, SSL2}, - {"rc4export", SSL_EN_RC4_128_EXPORT40_WITH_MD5, SSL2}, - {"rc2", SSL_EN_RC2_128_CBC_WITH_MD5, SSL2}, - {"rc2export", SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5, SSL2}, - {"des", SSL_EN_DES_64_CBC_WITH_MD5, SSL2}, - {"desede3", SSL_EN_DES_192_EDE3_CBC_WITH_MD5, SSL2}, + {"rc4", SSL_EN_RC4_128_WITH_MD5}, + {"rc4-md5", SSL_EN_RC4_128_WITH_MD5}, + {"rc4export", SSL_EN_RC4_128_EXPORT40_WITH_MD5}, + {"rc2", SSL_EN_RC2_128_CBC_WITH_MD5}, + {"rc2export", SSL_EN_RC2_128_CBC_EXPORT40_WITH_MD5}, + {"des", SSL_EN_DES_64_CBC_WITH_MD5}, + {"desede3", SSL_EN_DES_192_EDE3_CBC_WITH_MD5}, /* SSL3/TLS cipher suites */ - {"rsa_rc4_128_md5", SSL_RSA_WITH_RC4_128_MD5, SSL3 | TLS}, - {"rsa_rc4_128_sha", SSL_RSA_WITH_RC4_128_SHA, SSL3 | TLS}, - {"rsa_3des_sha", SSL_RSA_WITH_3DES_EDE_CBC_SHA, SSL3 | TLS}, - {"rsa_des_sha", SSL_RSA_WITH_DES_CBC_SHA, SSL3 | TLS}, - {"rsa_rc4_40_md5", SSL_RSA_EXPORT_WITH_RC4_40_MD5, SSL3 | TLS}, - {"rsa_rc2_40_md5", SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5, SSL3 | TLS}, - {"rsa_null_md5", SSL_RSA_WITH_NULL_MD5, SSL3 | TLS}, - {"rsa_null_sha", SSL_RSA_WITH_NULL_SHA, SSL3 | TLS}, - {"fips_3des_sha", SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA, SSL3 | TLS}, - {"fips_des_sha", SSL_RSA_FIPS_WITH_DES_CBC_SHA, SSL3 | TLS}, - {"fortezza", SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA, SSL3 | TLS}, - {"fortezza_rc4_128_sha", SSL_FORTEZZA_DMS_WITH_RC4_128_SHA, SSL3 | TLS}, - {"fortezza_null", SSL_FORTEZZA_DMS_WITH_NULL_SHA, SSL3 | TLS}, + {"rsa_rc4_128_md5", SSL_RSA_WITH_RC4_128_MD5}, + {"rsa_rc4_128_sha", SSL_RSA_WITH_RC4_128_SHA}, + {"rsa_3des_sha", SSL_RSA_WITH_3DES_EDE_CBC_SHA}, + {"rsa_des_sha", SSL_RSA_WITH_DES_CBC_SHA}, + {"rsa_rc4_40_md5", SSL_RSA_EXPORT_WITH_RC4_40_MD5}, + {"rsa_rc2_40_md5", SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5}, + {"rsa_null_md5", SSL_RSA_WITH_NULL_MD5}, + {"rsa_null_sha", SSL_RSA_WITH_NULL_SHA}, + {"fips_3des_sha", SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA}, + {"fips_des_sha", SSL_RSA_FIPS_WITH_DES_CBC_SHA}, + {"fortezza", SSL_FORTEZZA_DMS_WITH_FORTEZZA_CBC_SHA}, + {"fortezza_rc4_128_sha", SSL_FORTEZZA_DMS_WITH_RC4_128_SHA}, + {"fortezza_null", SSL_FORTEZZA_DMS_WITH_NULL_SHA}, /* TLS 1.0: Exportable 56-bit Cipher Suites. */ - {"rsa_des_56_sha", TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA, SSL3 | TLS}, - {"rsa_rc4_56_sha", TLS_RSA_EXPORT1024_WITH_RC4_56_SHA, SSL3 | TLS}, + {"rsa_des_56_sha", TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA}, + {"rsa_rc4_56_sha", TLS_RSA_EXPORT1024_WITH_RC4_56_SHA}, /* AES ciphers. */ - {"rsa_aes_128_sha", TLS_RSA_WITH_AES_128_CBC_SHA, SSL3 | TLS}, - {"rsa_aes_256_sha", TLS_RSA_WITH_AES_256_CBC_SHA, SSL3 | TLS}, + {"rsa_aes_128_sha", TLS_RSA_WITH_AES_128_CBC_SHA}, + {"rsa_aes_256_sha", TLS_RSA_WITH_AES_256_CBC_SHA}, #ifdef NSS_ENABLE_ECC /* ECC ciphers. */ - {"ecdh_ecdsa_null_sha", TLS_ECDH_ECDSA_WITH_NULL_SHA, TLS}, - {"ecdh_ecdsa_rc4_128_sha", TLS_ECDH_ECDSA_WITH_RC4_128_SHA, TLS}, - {"ecdh_ecdsa_3des_sha", TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS}, - {"ecdh_ecdsa_aes_128_sha", TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, TLS}, - {"ecdh_ecdsa_aes_256_sha", TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, TLS}, - {"ecdhe_ecdsa_null_sha", TLS_ECDHE_ECDSA_WITH_NULL_SHA, TLS}, - {"ecdhe_ecdsa_rc4_128_sha", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS}, - {"ecdhe_ecdsa_3des_sha", TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS}, - {"ecdhe_ecdsa_aes_128_sha", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS}, - {"ecdhe_ecdsa_aes_256_sha", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS}, - {"ecdh_rsa_null_sha", TLS_ECDH_RSA_WITH_NULL_SHA, TLS}, - {"ecdh_rsa_128_sha", TLS_ECDH_RSA_WITH_RC4_128_SHA, TLS}, - {"ecdh_rsa_3des_sha", TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, TLS}, - {"ecdh_rsa_aes_128_sha", TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, TLS}, - {"ecdh_rsa_aes_256_sha", TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, TLS}, - {"echde_rsa_null", TLS_ECDHE_RSA_WITH_NULL_SHA, TLS}, - {"ecdhe_rsa_rc4_128_sha", TLS_ECDHE_RSA_WITH_RC4_128_SHA, TLS}, - {"ecdhe_rsa_3des_sha", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS}, - {"ecdhe_rsa_aes_128_sha", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS}, - {"ecdhe_rsa_aes_256_sha", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, TLS}, - {"ecdh_anon_null_sha", TLS_ECDH_anon_WITH_NULL_SHA, TLS}, - {"ecdh_anon_rc4_128sha", TLS_ECDH_anon_WITH_RC4_128_SHA, TLS}, - {"ecdh_anon_3des_sha", TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA, TLS}, - {"ecdh_anon_aes_128_sha", TLS_ECDH_anon_WITH_AES_128_CBC_SHA, TLS}, - {"ecdh_anon_aes_256_sha", TLS_ECDH_anon_WITH_AES_256_CBC_SHA, TLS}, + {"ecdh_ecdsa_null_sha", TLS_ECDH_ECDSA_WITH_NULL_SHA}, + {"ecdh_ecdsa_rc4_128_sha", TLS_ECDH_ECDSA_WITH_RC4_128_SHA}, + {"ecdh_ecdsa_3des_sha", TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA}, + {"ecdh_ecdsa_aes_128_sha", TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA}, + {"ecdh_ecdsa_aes_256_sha", TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA}, + {"ecdhe_ecdsa_null_sha", TLS_ECDHE_ECDSA_WITH_NULL_SHA}, + {"ecdhe_ecdsa_rc4_128_sha", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA}, + {"ecdhe_ecdsa_3des_sha", TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA}, + {"ecdhe_ecdsa_aes_128_sha", TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA}, + {"ecdhe_ecdsa_aes_256_sha", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA}, + {"ecdh_rsa_null_sha", TLS_ECDH_RSA_WITH_NULL_SHA}, + {"ecdh_rsa_128_sha", TLS_ECDH_RSA_WITH_RC4_128_SHA}, + {"ecdh_rsa_3des_sha", TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA}, + {"ecdh_rsa_aes_128_sha", TLS_ECDH_RSA_WITH_AES_128_CBC_SHA}, + {"ecdh_rsa_aes_256_sha", TLS_ECDH_RSA_WITH_AES_256_CBC_SHA}, + {"echde_rsa_null", TLS_ECDHE_RSA_WITH_NULL_SHA}, + {"ecdhe_rsa_rc4_128_sha", TLS_ECDHE_RSA_WITH_RC4_128_SHA}, + {"ecdhe_rsa_3des_sha", TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA}, + {"ecdhe_rsa_aes_128_sha", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA}, + {"ecdhe_rsa_aes_256_sha", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA}, + {"ecdh_anon_null_sha", TLS_ECDH_anon_WITH_NULL_SHA}, + {"ecdh_anon_rc4_128sha", TLS_ECDH_anon_WITH_RC4_128_SHA}, + {"ecdh_anon_3des_sha", TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA}, + {"ecdh_anon_aes_128_sha", TLS_ECDH_anon_WITH_AES_128_CBC_SHA}, + {"ecdh_anon_aes_256_sha", TLS_ECDH_anon_WITH_AES_256_CBC_SHA}, #endif }; @@ -248,7 +245,7 @@ static SECStatus set_ciphers(struct SessionHandle *data, PRFileDesc * model, for(i=0; i Date: Fri, 10 Aug 2012 08:54:29 +0200 Subject: white space fix: shorten long line ... to please checksrc.pl --- lib/nss.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 26f35eb0f..f39de3bad 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1081,7 +1081,8 @@ int Curl_nss_close_all(struct SessionHandle *data) return 0; } -/* true if NSS can provide error code (and possibly a message) for the error */ +/* return true if NSS can provide error code (and possibly msg) for the + error */ static bool is_nss_error(CURLcode err) { switch(err) { -- cgit v1.2.1 From e6ba0487013085afc5bc1ca7d7c8a15a13367ba6 Mon Sep 17 00:00:00 2001 From: Marc Hoersken Date: Tue, 11 Sep 2012 09:49:23 +0200 Subject: nss.c: Fixed warning: 'err' may be used uninitialized in this function --- lib/nss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index f39de3bad..56290f4b7 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1171,7 +1171,7 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn, CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) { - PRInt32 err; + PRErrorCode err = 0; PRFileDesc *model = NULL; PRBool ssl2 = PR_FALSE; PRBool ssl3 = PR_FALSE; -- cgit v1.2.1 From da82f59b697310229ccdf66104d5d65a44dfab98 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sat, 27 Oct 2012 12:31:39 +0200 Subject: CURLOPT_SSL_VERIFYHOST: stop supporting the 1 value After a research team wrote a document[1] that found several live source codes out there in the wild that misused the CURLOPT_SSL_VERIFYHOST option thinking it was a boolean, this change now bans 1 as a value and will make libcurl return error for it. 1 was never a sensible value to use in production but was introduced back in the days to help debugging. It was always documented clearly this way. 1 was never supported by all SSL backends in libcurl, so this cleanup makes the treatment of it unified. The report's list of mistakes for this option were all PHP code and while there's a binding layer between libcurl and PHP, the PHP team has decided that they have an as thin layer as possible on top of libcurl so they will not alter or specifically filter a 'TRUE' value for this particular option. I sympathize with that position. [1] = http://daniel.haxx.se/blog/2012/10/25/libcurl-claimed-to-be-dangerous/ --- lib/nss.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 56290f4b7..22b53bfd8 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -1316,8 +1316,6 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex) 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 */ -- cgit v1.2.1 From 68d2830ee9df50961e481e81c1baaa290c33f03e Mon Sep 17 00:00:00 2001 From: Kamil Dudka Date: Mon, 3 Dec 2012 13:17:50 +0100 Subject: nss: prevent NSS from crashing on client auth hook failure Although it is not explicitly stated in the documentation, NSS uses *pRetCert and *pRetKey even if the client authentication hook returns a failure. Namely, if we destroy *pRetCert without clearing *pRetCert afterwards, NSS destroys the certificate once again, which causes a double free. Reported by: Bob Relyea --- lib/nss.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'lib/nss.c') diff --git a/lib/nss.c b/lib/nss.c index 22b53bfd8..794eccbd4 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -757,6 +757,8 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, static const char pem_slotname[] = "PEM Token #1"; SECItem cert_der = { 0, NULL, 0 }; void *proto_win = SSL_RevealPinArg(sock); + struct CERTCertificateStr *cert; + struct SECKEYPrivateKeyStr *key; PK11SlotInfo *slot = PK11_FindSlotByName(pem_slotname); if(NULL == slot) { @@ -771,24 +773,27 @@ static SECStatus SelectClientCert(void *arg, PRFileDesc *sock, return SECFailure; } - *pRetCert = PK11_FindCertFromDERCertItem(slot, &cert_der, proto_win); + cert = PK11_FindCertFromDERCertItem(slot, &cert_der, proto_win); SECITEM_FreeItem(&cert_der, PR_FALSE); - if(NULL == *pRetCert) { + if(NULL == cert) { failf(data, "NSS: client certificate from file not found"); PK11_FreeSlot(slot); return SECFailure; } - *pRetKey = PK11_FindPrivateKeyFromCert(slot, *pRetCert, NULL); + key = PK11_FindPrivateKeyFromCert(slot, cert, NULL); PK11_FreeSlot(slot); - if(NULL == *pRetKey) { + if(NULL == key) { failf(data, "NSS: private key from file not found"); - CERT_DestroyCertificate(*pRetCert); + CERT_DestroyCertificate(cert); return SECFailure; } infof(data, "NSS: client certificate from file\n"); - display_cert_info(data, *pRetCert); + display_cert_info(data, cert); + + *pRetCert = cert; + *pRetKey = key; return SECSuccess; } -- cgit v1.2.1