[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