Blob Blame History Raw
 CHANGES   |    4 ++
 lib/nss.c |  151 +++++++++++++++++++++++++++++++++---------------------------
 2 files changed, 87 insertions(+), 68 deletions(-)

diff --git a/CHANGES b/CHANGES
index 7433364..7928690 100644
--- a/CHANGES
+++ b/CHANGES
@@ -6,6 +6,10 @@
 
                                   Changelog
 
+Kamil Dudka (11 May 2010)
+- CRL support in libcurl-NSS has been completely broken. Now it works. Original
+  bug report: https://bugzilla.redhat.com/581926
+
 Kamil Dudka (24 Apr 2010)
 - Fixed test536 in order to not fail with threaded DNS resolver and tweaked
   comments in certain examples using curl_multi_fdset().
diff --git a/lib/nss.c b/lib/nss.c
index addb94b..5e94d31 100644
--- a/lib/nss.c
+++ b/lib/nss.c
@@ -63,6 +63,7 @@
 #include <secport.h>
 #include <certdb.h>
 #include <base64.h>
+#include <cert.h>
 
 #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,78 +413,90 @@ static int nss_load_cert(struct ssl_connect_data *ssl,
   return 1;
 }
 
-static int nss_load_crl(const char* crlfilename, PRBool ascii)
+/* add given CRL to cache if it is not already there */
+static SECStatus nss_cache_crl(SECItem *crlDER)
 {
-  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;
+  CERTCertDBHandle *db = CERT_GetDefaultCertDB();
+  CERTSignedCrl *crl = SEC_FindCrlByDERCert(db, crlDER, 0);
+  if(crl) {
+    /* CRL already cached */
+    SEC_DestroyCrl(crl);
+    SECITEM_FreeItem(crlDER, PR_FALSE);
+    return SECSuccess;
   }
-  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;
 
-    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;
+  /* 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);
+    SECITEM_FreeItem(crlDER, PR_FALSE);
+    return SECFailure;
   }
-  else {
-    if (!SECITEM_AllocItem(NULL,&crlDER,info.size))
-      return 0;
-    nb = PR_Read(infile,crlDER.data,info.size);
-    if (nb!=info.size)
-      return 0;
+
+  /* we need to clear session cache, so that the CRL could take effect */
+  SSL_ClearSessionCache();
+  PR_Unlock(nss_crllock);
+  return SECSuccess;
+}
+
+static SECStatus nss_load_crl(const char* crlfilename)
+{
+  PRFileDesc *infile;
+  PRFileInfo  info;
+  SECItem filedata = { 0, NULL, 0 };
+  SECItem crlDER = { 0, NULL, 0 };
+  char *body;
+
+  infile = PR_Open(crlfilename, PR_RDONLY, 0);
+  if(!infile)
+    return SECFailure;
+
+  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;
 
-  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;
+  PR_Close(infile);
+  return 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,
@@ -889,6 +903,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 +933,7 @@ void Curl_nss_cleanup(void)
   PR_Unlock(nss_initlock);
 
   PR_DestroyLock(nss_initlock);
+  PR_DestroyLock(nss_crllock);
   nss_initlock = NULL;
 
   initialized = 0;
@@ -1244,8 +1260,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;
     }