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 #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,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; }