[curl/f15] sync the NSS code with upstream f551aa5 (several bug fixes)
Kamil Dudka
kdudka at fedoraproject.org
Wed Jun 8 00:02:34 UTC 2011
commit f830f75410275c30af27c9724324849f61079e9c
Author: Kamil Dudka <kdudka at redhat.com>
Date: Wed Jun 8 01:57:11 2011 +0200
sync the NSS code with upstream f551aa5 (several bug fixes)
0002-curl-7.21.3-nss.patch | 692 ++++++++++++++++++++++++++++++++++++++++++++
curl.spec | 5 +
2 files changed, 697 insertions(+), 0 deletions(-)
---
diff --git a/0002-curl-7.21.3-nss.patch b/0002-curl-7.21.3-nss.patch
new file mode 100644
index 0000000..bde6ebe
--- /dev/null
+++ b/0002-curl-7.21.3-nss.patch
@@ -0,0 +1,692 @@
+From 458b55fe274ee211e9b6a95bec62d9b1a26f27af Mon Sep 17 00:00:00 2001
+From: Kamil Dudka <kdudka at redhat.com>
+Date: Wed, 8 Jun 2011 01:13:02 +0200
+Subject: [PATCH] backport lib/nss.c from upstream f551aa5
+
+---
+ lib/nss.c | 437 ++++++++++++++++++++++++++++++--------------------------
+ lib/urldata.h | 3 +-
+ 2 files changed, 235 insertions(+), 205 deletions(-)
+
+diff --git a/lib/nss.c b/lib/nss.c
+index 6d3f12c..d146fcc 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 <curl/mprintf.h>
+@@ -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
+
+@@ -277,36 +282,104 @@ 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);
+ }
+
++#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;
+@@ -333,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.
+@@ -503,60 +534,35 @@ 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 */
+-
+- slotID = 1; /* hardcoded for now */
+-
+- snprintf(slotname, sizeof(slotname), "PEM Token #%ld", slotID);
+- slot = PK11_FindSlotByName(slotname);
+-
+- if(!slot)
+- return 0;
++ PK11SlotInfo *slot;
++ SECStatus status;
++ struct ssl_connect_data *ssl = conn->ssl;
++ (void)sockindex; /* unused */
+
+- 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.
+ */
+ (void)conn; /* unused */
+ (void)key_file; /* unused */
++ (void)sockindex; /* unused */
+ return 0;
+ #endif
+ }
+@@ -616,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(CERT_GetDefaultCertDB(), 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);
+@@ -637,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;
+@@ -650,37 +663,32 @@ 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 {
++ }
++ 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;
+ }
+
+ /**
+@@ -1039,8 +1047,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]);
+@@ -1048,15 +1054,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
+- 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);
++ connssl->obj_list = NULL;
+ #endif
++ PR_Close(connssl->handle);
+ connssl->handle = NULL;
+ }
+ }
+@@ -1095,6 +1103,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;
+@@ -1102,10 +1159,11 @@ 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];
+- int curlerr;
++ CURLcode curlerr;
+ const int *cipher_to_enable;
+ PRSocketOptionData sock_opt;
+ long time_left;
+@@ -1117,9 +1175,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. */
+@@ -1164,7 +1223,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)
+@@ -1174,6 +1233,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:
+@@ -1224,9 +1288,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) {
+@@ -1236,53 +1307,13 @@ 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;
++ if(data->set.ssl.verifypeer) {
++ const CURLcode rv = nss_load_ca_certificates(conn, sockindex);
++ if(CURLE_OK != rv) {
++ curlerr = rv;
+ 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[PATH_MAX];
+-
+- snprintf(fullpath, sizeof(fullpath), "%s/%s", data->set.ssl.CApath,
+- entry->name);
+- 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
+- * 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.CRLfile) {
+ if(SECSuccess != nss_load_crl(data->set.ssl.CRLfile)) {
+@@ -1295,25 +1326,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;
+@@ -1366,19 +1392,18 @@ 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);
+-
++ 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;
+
+- 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");
+@@ -1405,7 +1430,13 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
+ if(model)
+ PR_Close(model);
+
+- if (ssl3 && tlsv1 && isTLSIntoleranceError(err)) {
++#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;
+ infof(data, "Error in TLS handshake, trying SSLv3...\n");
+diff --git a/lib/urldata.h b/lib/urldata.h
+index f4f6786..ab6b5be 100644
+--- a/lib/urldata.h
++++ b/lib/urldata.h
+@@ -261,8 +261,7 @@ struct ssl_connect_data {
+ char *client_nickname;
+ struct SessionHandle *data;
+ #ifdef HAVE_PK11_CREATEGENERICOBJECT
+- PK11GenericObject *key;
+- PK11GenericObject *cacert[2];
++ struct curl_llist *obj_list;
+ #endif
+ #endif /* USE_NSS */
+ #ifdef USE_QSOSSL
+--
+1.7.4.4
+
diff --git a/curl.spec b/curl.spec
index a1b3d3d..f200cca 100644
--- a/curl.spec
+++ b/curl.spec
@@ -11,6 +11,9 @@ Source3: hide_selinux.c
# avoid an invalid timeout event on a reused handle (#679709)
Patch1: 0001-curl-7.21.3-f551aa5.patch
+# sync the NSS code with upstream f551aa5 (several bug fixes)
+Patch2: 0002-curl-7.21.3-nss.patch
+
# Avoid buffer overflow report from glibc with FORTIFY_SOURCE
Patch5: 0005-curl-7.21.3-tftpd-buffer-overflow.patch
@@ -114,6 +117,7 @@ done
# upstream patches (already applied)
%patch1 -p1
+%patch2 -p1
%patch5 -p1
# Fedora patches
@@ -230,6 +234,7 @@ rm -rf $RPM_BUILD_ROOT
%changelog
* Wed Jun 08 2011 Kamil Dudka <kdudka at redhat.com> 7.21.3-7
- avoid an invalid timeout event on a reused handle (#679709)
+- sync the NSS code with upstream f551aa5 (several bug fixes)
* Sat Apr 16 2011 Peter Robinson <pbrobinson at gmail.com> 7.21.3-6
- no valgrind on ARMv5 arches
More information about the scm-commits
mailing list