summaryrefslogtreecommitdiff
path: root/security/nss/lib/certdb/crl.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/certdb/crl.c')
-rw-r--r--security/nss/lib/certdb/crl.c3003
1 files changed, 0 insertions, 3003 deletions
diff --git a/security/nss/lib/certdb/crl.c b/security/nss/lib/certdb/crl.c
deleted file mode 100644
index 7cd308c0a..000000000
--- a/security/nss/lib/certdb/crl.c
+++ /dev/null
@@ -1,3003 +0,0 @@
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is the Netscape security libraries.
- *
- * The Initial Developer of the Original Code is
- * Netscape Communications Corporation.
- * Portions created by the Initial Developer are Copyright (C) 1994-2000
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * and other provisions required by the GPL or the LGPL. If you do not delete
- * the provisions above, a recipient may use your version of this file under
- * the terms of any one of the MPL, the GPL or the LGPL.
- *
- * ***** END LICENSE BLOCK ***** */
-
-/*
- * Moved from secpkcs7.c
- *
- * $Id$
- */
-
-#include "cert.h"
-#include "certi.h"
-#include "secder.h"
-#include "secasn1.h"
-#include "secoid.h"
-#include "certdb.h"
-#include "certxutl.h"
-#include "prtime.h"
-#include "secerr.h"
-#include "pk11func.h"
-#include "dev.h"
-#include "dev3hack.h"
-#include "nssbase.h"
-#if defined(DPC_RWLOCK) || defined(GLOBAL_RWLOCK)
-#include "nssrwlk.h"
-#endif
-#include "pk11priv.h"
-
-const SEC_ASN1Template SEC_CERTExtensionTemplate[] = {
- { SEC_ASN1_SEQUENCE,
- 0, NULL, sizeof(CERTCertExtension) },
- { SEC_ASN1_OBJECT_ID,
- offsetof(CERTCertExtension,id) },
- { SEC_ASN1_OPTIONAL | SEC_ASN1_BOOLEAN, /* XXX DER_DEFAULT */
- offsetof(CERTCertExtension,critical), },
- { SEC_ASN1_OCTET_STRING,
- offsetof(CERTCertExtension,value) },
- { 0, }
-};
-
-static const SEC_ASN1Template SEC_CERTExtensionsTemplate[] = {
- { SEC_ASN1_SEQUENCE_OF, 0, SEC_CERTExtensionTemplate}
-};
-
-/*
- * XXX Also, these templates, especially the Krl/FORTEZZA ones, need to
- * be tested; Lisa did the obvious translation but they still should be
- * verified.
- */
-
-const SEC_ASN1Template CERT_IssuerAndSNTemplate[] = {
- { SEC_ASN1_SEQUENCE,
- 0, NULL, sizeof(CERTIssuerAndSN) },
- { SEC_ASN1_SAVE,
- offsetof(CERTIssuerAndSN,derIssuer) },
- { SEC_ASN1_INLINE,
- offsetof(CERTIssuerAndSN,issuer),
- CERT_NameTemplate },
- { SEC_ASN1_INTEGER,
- offsetof(CERTIssuerAndSN,serialNumber) },
- { 0 }
-};
-
-static const SEC_ASN1Template cert_KrlEntryTemplate[] = {
- { SEC_ASN1_SEQUENCE,
- 0, NULL, sizeof(CERTCrlEntry) },
- { SEC_ASN1_OCTET_STRING,
- offsetof(CERTCrlEntry,serialNumber) },
- { SEC_ASN1_UTC_TIME,
- offsetof(CERTCrlEntry,revocationDate) },
- { 0 }
-};
-
-static const SEC_ASN1Template cert_KrlTemplate[] = {
- { SEC_ASN1_SEQUENCE,
- 0, NULL, sizeof(CERTCrl) },
- { SEC_ASN1_INLINE,
- offsetof(CERTCrl,signatureAlg),
- SECOID_AlgorithmIDTemplate },
- { SEC_ASN1_SAVE,
- offsetof(CERTCrl,derName) },
- { SEC_ASN1_INLINE,
- offsetof(CERTCrl,name),
- CERT_NameTemplate },
- { SEC_ASN1_UTC_TIME,
- offsetof(CERTCrl,lastUpdate) },
- { SEC_ASN1_UTC_TIME,
- offsetof(CERTCrl,nextUpdate) },
- { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF,
- offsetof(CERTCrl,entries),
- cert_KrlEntryTemplate },
- { 0 }
-};
-
-static const SEC_ASN1Template cert_SignedKrlTemplate[] = {
- { SEC_ASN1_SEQUENCE,
- 0, NULL, sizeof(CERTSignedCrl) },
- { SEC_ASN1_SAVE,
- offsetof(CERTSignedCrl,signatureWrap.data) },
- { SEC_ASN1_INLINE,
- offsetof(CERTSignedCrl,crl),
- cert_KrlTemplate },
- { SEC_ASN1_INLINE,
- offsetof(CERTSignedCrl,signatureWrap.signatureAlgorithm),
- SECOID_AlgorithmIDTemplate },
- { SEC_ASN1_BIT_STRING,
- offsetof(CERTSignedCrl,signatureWrap.signature) },
- { 0 }
-};
-
-static const SEC_ASN1Template cert_CrlKeyTemplate[] = {
- { SEC_ASN1_SEQUENCE,
- 0, NULL, sizeof(CERTCrlKey) },
- { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof(CERTCrlKey,dummy) },
- { SEC_ASN1_SKIP },
- { SEC_ASN1_ANY, offsetof(CERTCrlKey,derName) },
- { SEC_ASN1_SKIP_REST },
- { 0 }
-};
-
-static const SEC_ASN1Template cert_CrlEntryTemplate[] = {
- { SEC_ASN1_SEQUENCE,
- 0, NULL, sizeof(CERTCrlEntry) },
- { SEC_ASN1_INTEGER,
- offsetof(CERTCrlEntry,serialNumber) },
- { SEC_ASN1_INLINE,
- offsetof(CERTCrlEntry,revocationDate), CERT_TimeChoiceTemplate },
- { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF,
- offsetof(CERTCrlEntry, extensions),
- SEC_CERTExtensionTemplate},
- { 0 }
-};
-
-const SEC_ASN1Template CERT_CrlTemplate[] = {
- { SEC_ASN1_SEQUENCE,
- 0, NULL, sizeof(CERTCrl) },
- { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof (CERTCrl, version) },
- { SEC_ASN1_INLINE,
- offsetof(CERTCrl,signatureAlg),
- SECOID_AlgorithmIDTemplate },
- { SEC_ASN1_SAVE,
- offsetof(CERTCrl,derName) },
- { SEC_ASN1_INLINE,
- offsetof(CERTCrl,name),
- CERT_NameTemplate },
- { SEC_ASN1_INLINE,
- offsetof(CERTCrl,lastUpdate), CERT_TimeChoiceTemplate },
- { SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL,
- offsetof(CERTCrl,nextUpdate), CERT_TimeChoiceTemplate },
- { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF,
- offsetof(CERTCrl,entries),
- cert_CrlEntryTemplate },
- { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
- SEC_ASN1_EXPLICIT | 0,
- offsetof(CERTCrl,extensions),
- SEC_CERTExtensionsTemplate},
- { 0 }
-};
-
-const SEC_ASN1Template CERT_CrlTemplateNoEntries[] = {
- { SEC_ASN1_SEQUENCE,
- 0, NULL, sizeof(CERTCrl) },
- { SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL, offsetof (CERTCrl, version) },
- { SEC_ASN1_INLINE,
- offsetof(CERTCrl,signatureAlg),
- SECOID_AlgorithmIDTemplate },
- { SEC_ASN1_SAVE,
- offsetof(CERTCrl,derName) },
- { SEC_ASN1_INLINE,
- offsetof(CERTCrl,name),
- CERT_NameTemplate },
- { SEC_ASN1_INLINE,
- offsetof(CERTCrl,lastUpdate), CERT_TimeChoiceTemplate },
- { SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL,
- offsetof(CERTCrl,nextUpdate), CERT_TimeChoiceTemplate },
- { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF |
- SEC_ASN1_SKIP }, /* skip entries */
- { SEC_ASN1_OPTIONAL | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
- SEC_ASN1_EXPLICIT | 0,
- offsetof(CERTCrl,extensions),
- SEC_CERTExtensionsTemplate },
- { 0 }
-};
-
-const SEC_ASN1Template CERT_CrlTemplateEntriesOnly[] = {
- { SEC_ASN1_SEQUENCE,
- 0, NULL, sizeof(CERTCrl) },
- { SEC_ASN1_SKIP | SEC_ASN1_INTEGER | SEC_ASN1_OPTIONAL },
- { SEC_ASN1_SKIP },
- { SEC_ASN1_SKIP },
- { SEC_ASN1_SKIP | SEC_ASN1_INLINE,
- offsetof(CERTCrl,lastUpdate), CERT_TimeChoiceTemplate },
- { SEC_ASN1_SKIP | SEC_ASN1_INLINE | SEC_ASN1_OPTIONAL,
- offsetof(CERTCrl,nextUpdate), CERT_TimeChoiceTemplate },
- { SEC_ASN1_OPTIONAL | SEC_ASN1_SEQUENCE_OF,
- offsetof(CERTCrl,entries),
- cert_CrlEntryTemplate }, /* decode entries */
- { SEC_ASN1_SKIP_REST },
- { 0 }
-};
-
-const SEC_ASN1Template CERT_SignedCrlTemplate[] = {
- { SEC_ASN1_SEQUENCE,
- 0, NULL, sizeof(CERTSignedCrl) },
- { SEC_ASN1_SAVE,
- offsetof(CERTSignedCrl,signatureWrap.data) },
- { SEC_ASN1_INLINE,
- offsetof(CERTSignedCrl,crl),
- CERT_CrlTemplate },
- { SEC_ASN1_INLINE,
- offsetof(CERTSignedCrl,signatureWrap.signatureAlgorithm),
- SECOID_AlgorithmIDTemplate },
- { SEC_ASN1_BIT_STRING,
- offsetof(CERTSignedCrl,signatureWrap.signature) },
- { 0 }
-};
-
-static const SEC_ASN1Template cert_SignedCrlTemplateNoEntries[] = {
- { SEC_ASN1_SEQUENCE,
- 0, NULL, sizeof(CERTSignedCrl) },
- { SEC_ASN1_SAVE,
- offsetof(CERTSignedCrl,signatureWrap.data) },
- { SEC_ASN1_INLINE,
- offsetof(CERTSignedCrl,crl),
- CERT_CrlTemplateNoEntries },
- { SEC_ASN1_INLINE,
- offsetof(CERTSignedCrl,signatureWrap.signatureAlgorithm),
- SECOID_AlgorithmIDTemplate },
- { SEC_ASN1_BIT_STRING,
- offsetof(CERTSignedCrl,signatureWrap.signature) },
- { 0 }
-};
-
-const SEC_ASN1Template CERT_SetOfSignedCrlTemplate[] = {
- { SEC_ASN1_SET_OF, 0, CERT_SignedCrlTemplate },
-};
-
-/* get CRL version */
-int cert_get_crl_version(CERTCrl * crl)
-{
- /* CRL version is defaulted to v1 */
- int version = SEC_CRL_VERSION_1;
- if (crl && crl->version.data != 0) {
- version = (int)DER_GetUInteger (&crl->version);
- }
- return version;
-}
-
-
-/* check the entries in the CRL */
-SECStatus cert_check_crl_entries (CERTCrl *crl)
-{
- CERTCrlEntry **entries;
- CERTCrlEntry *entry;
- PRBool hasCriticalExten = PR_FALSE;
- SECStatus rv = SECSuccess;
-
- if (!crl) {
- return SECFailure;
- }
-
- if (crl->entries == NULL) {
- /* CRLs with no entries are valid */
- return (SECSuccess);
- }
-
- /* Look in the crl entry extensions. If there is a critical extension,
- then the crl version must be v2; otherwise, it should be v1.
- */
- entries = crl->entries;
- while (*entries) {
- entry = *entries;
- if (entry->extensions) {
- /* If there is a critical extension in the entries, then the
- CRL must be of version 2. If we already saw a critical extension,
- there is no need to check the version again.
- */
- if (hasCriticalExten == PR_FALSE) {
- hasCriticalExten = cert_HasCriticalExtension (entry->extensions);
- if (hasCriticalExten) {
- if (cert_get_crl_version(crl) != SEC_CRL_VERSION_2) {
- /* only CRL v2 critical extensions are supported */
- PORT_SetError(SEC_ERROR_CRL_V1_CRITICAL_EXTENSION);
- rv = SECFailure;
- break;
- }
- }
- }
-
- /* For each entry, make sure that it does not contain an unknown
- critical extension. If it does, we must reject the CRL since
- we don't know how to process the extension.
- */
- if (cert_HasUnknownCriticalExten (entry->extensions) == PR_TRUE) {
- PORT_SetError (SEC_ERROR_CRL_UNKNOWN_CRITICAL_EXTENSION);
- rv = SECFailure;
- break;
- }
- }
- ++entries;
- }
- return(rv);
-}
-
-/* Check the version of the CRL. If there is a critical extension in the crl
- or crl entry, then the version must be v2. Otherwise, it should be v1. If
- the crl contains critical extension(s), then we must recognized the
- extension's OID.
- */
-SECStatus cert_check_crl_version (CERTCrl *crl)
-{
- PRBool hasCriticalExten = PR_FALSE;
- int version = cert_get_crl_version(crl);
-
- if (version > SEC_CRL_VERSION_2) {
- PORT_SetError (SEC_ERROR_CRL_INVALID_VERSION);
- return (SECFailure);
- }
-
- /* Check the crl extensions for a critial extension. If one is found,
- and the version is not v2, then we are done.
- */
- if (crl->extensions) {
- hasCriticalExten = cert_HasCriticalExtension (crl->extensions);
- if (hasCriticalExten) {
- if (version != SEC_CRL_VERSION_2) {
- /* only CRL v2 critical extensions are supported */
- PORT_SetError(SEC_ERROR_CRL_V1_CRITICAL_EXTENSION);
- return (SECFailure);
- }
- /* make sure that there is no unknown critical extension */
- if (cert_HasUnknownCriticalExten (crl->extensions) == PR_TRUE) {
- PORT_SetError (SEC_ERROR_CRL_UNKNOWN_CRITICAL_EXTENSION);
- return (SECFailure);
- }
- }
- }
-
- return (SECSuccess);
-}
-
-/*
- * Generate a database key, based on the issuer name from a
- * DER crl.
- */
-SECStatus
-CERT_KeyFromDERCrl(PRArenaPool *arena, SECItem *derCrl, SECItem *key)
-{
- SECStatus rv;
- CERTSignedData sd;
- CERTCrlKey crlkey;
-
- PORT_Memset (&sd, 0, sizeof (sd));
- rv = SEC_ASN1DecodeItem (arena, &sd, CERT_SignedDataTemplate, derCrl);
- if (rv != SECSuccess) {
- return rv;
- }
-
- PORT_Memset (&crlkey, 0, sizeof (crlkey));
- rv = SEC_ASN1DecodeItem(arena, &crlkey, cert_CrlKeyTemplate, &sd.data);
- if (rv != SECSuccess) {
- return rv;
- }
-
- key->len = crlkey.derName.len;
- key->data = crlkey.derName.data;
-
- return(SECSuccess);
-}
-
-#define GetOpaqueCRLFields(x) ((OpaqueCRLFields*)x->opaque)
-
-SECStatus CERT_CompleteCRLDecodeEntries(CERTSignedCrl* crl)
-{
- SECStatus rv = SECSuccess;
- SECItem* crldata = NULL;
- OpaqueCRLFields* extended = NULL;
-
- if ( (!crl) ||
- (!(extended = (OpaqueCRLFields*) crl->opaque)) ||
- (PR_TRUE == extended->decodingError) ) {
- rv = SECFailure;
- } else {
- if (PR_FALSE == extended->partial) {
- /* the CRL has already been fully decoded */
- return SECSuccess;
- }
- if (PR_TRUE == extended->badEntries) {
- /* the entries decoding already failed */
- return SECFailure;
- }
- crldata = &crl->signatureWrap.data;
- if (!crldata) {
- rv = SECFailure;
- }
- }
-
- if (SECSuccess == rv) {
- rv = SEC_QuickDERDecodeItem(crl->arena,
- &crl->crl,
- CERT_CrlTemplateEntriesOnly,
- crldata);
- if (SECSuccess == rv) {
- extended->partial = PR_FALSE; /* successful decode, avoid
- decoding again */
- } else {
- extended->decodingError = PR_TRUE;
- extended->badEntries = PR_TRUE;
- /* cache the decoding failure. If it fails the first time,
- it will fail again, which will grow the arena and leak
- memory, so we want to avoid it */
- }
- rv = cert_check_crl_entries(&crl->crl);
- if (rv != SECSuccess) {
- extended->badExtensions = PR_TRUE;
- }
- }
- return rv;
-}
-
-/*
- * take a DER CRL or KRL and decode it into a CRL structure
- * allow reusing the input DER without making a copy
- */
-CERTSignedCrl *
-CERT_DecodeDERCrlWithFlags(PRArenaPool *narena, SECItem *derSignedCrl,
- int type, PRInt32 options)
-{
- PRArenaPool *arena;
- CERTSignedCrl *crl;
- SECStatus rv;
- OpaqueCRLFields* extended = NULL;
- const SEC_ASN1Template* crlTemplate = CERT_SignedCrlTemplate;
-
- if (!derSignedCrl ||
- ( (options & CRL_DECODE_ADOPT_HEAP_DER) && /* adopting DER requires
- not copying it */
- (!(options & CRL_DECODE_DONT_COPY_DER))
- )
- ) {
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- return NULL;
- }
-
- /* make a new arena if needed */
- if (narena == NULL) {
- arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
- if ( !arena ) {
- return NULL;
- }
- } else {
- arena = narena;
- }
-
- /* allocate the CRL structure */
- crl = (CERTSignedCrl *)PORT_ArenaZAlloc(arena, sizeof(CERTSignedCrl));
- if ( !crl ) {
- PORT_SetError(SEC_ERROR_NO_MEMORY);
- goto loser;
- }
-
- crl->arena = arena;
-
- /* allocate opaque fields */
- crl->opaque = (void*)PORT_ArenaZAlloc(arena, sizeof(OpaqueCRLFields));
- if ( !crl->opaque ) {
- goto loser;
- }
- extended = (OpaqueCRLFields*) crl->opaque;
- if (options & CRL_DECODE_ADOPT_HEAP_DER) {
- extended->heapDER = PR_TRUE;
- }
- if (options & CRL_DECODE_DONT_COPY_DER) {
- crl->derCrl = derSignedCrl; /* DER is not copied . The application
- must keep derSignedCrl until it
- destroys the CRL */
- } else {
- crl->derCrl = (SECItem *)PORT_ArenaZAlloc(arena,sizeof(SECItem));
- if (crl->derCrl == NULL) {
- goto loser;
- }
- rv = SECITEM_CopyItem(arena, crl->derCrl, derSignedCrl);
- if (rv != SECSuccess) {
- goto loser;
- }
- }
-
- /* Save the arena in the inner crl for CRL extensions support */
- crl->crl.arena = arena;
- if (options & CRL_DECODE_SKIP_ENTRIES) {
- crlTemplate = cert_SignedCrlTemplateNoEntries;
- extended->partial = PR_TRUE;
- }
-
- /* decode the CRL info */
- switch (type) {
- case SEC_CRL_TYPE:
- rv = SEC_QuickDERDecodeItem(arena, crl, crlTemplate, crl->derCrl);
- if (rv != SECSuccess) {
- extended->badDER = PR_TRUE;
- break;
- }
- /* check for critical extensions */
- rv = cert_check_crl_version (&crl->crl);
- if (rv != SECSuccess) {
- extended->badExtensions = PR_TRUE;
- break;
- }
-
- if (PR_TRUE == extended->partial) {
- /* partial decoding, don't verify entries */
- break;
- }
-
- rv = cert_check_crl_entries(&crl->crl);
- if (rv != SECSuccess) {
- extended->badExtensions = PR_TRUE;
- }
-
- break;
-
- case SEC_KRL_TYPE:
- rv = SEC_QuickDERDecodeItem
- (arena, crl, cert_SignedKrlTemplate, derSignedCrl);
- break;
- default:
- rv = SECFailure;
- break;
- }
-
- if (rv != SECSuccess) {
- goto loser;
- }
-
- crl->referenceCount = 1;
-
- return(crl);
-
-loser:
- if (options & CRL_DECODE_KEEP_BAD_CRL) {
- extended->decodingError = PR_TRUE;
- crl->referenceCount = 1;
- return(crl);
- }
-
- if ((narena == NULL) && arena ) {
- PORT_FreeArena(arena, PR_FALSE);
- }
-
- return(0);
-}
-
-/*
- * take a DER CRL or KRL and decode it into a CRL structure
- */
-CERTSignedCrl *
-CERT_DecodeDERCrl(PRArenaPool *narena, SECItem *derSignedCrl, int type)
-{
- return CERT_DecodeDERCrlWithFlags(narena, derSignedCrl, type,
- CRL_DECODE_DEFAULT_OPTIONS);
-}
-
-/*
- * Lookup a CRL in the databases. We mirror the same fast caching data base
- * caching stuff used by certificates....?
- * return values :
- *
- * SECSuccess means we got a valid DER CRL (passed in "decoded"), or no CRL at
- * all
- *
- * SECFailure means we got a fatal error - most likely, we found a CRL,
- * and it failed decoding, or there was an out of memory error. Do NOT ignore
- * it and specifically do NOT treat it the same as having no CRL, as this
- * can compromise security !!! Ideally, you should treat this case as if you
- * received a "catch-all" CRL where all certs you were looking up are
- * considered to be revoked
- */
-static SECStatus
-SEC_FindCrlByKeyOnSlot(PK11SlotInfo *slot, SECItem *crlKey, int type,
- CERTSignedCrl** decoded, PRInt32 decodeoptions)
-{
- SECStatus rv = SECSuccess;
- CERTSignedCrl *crl = NULL;
- SECItem *derCrl = NULL;
- CK_OBJECT_HANDLE crlHandle = 0;
- char *url = NULL;
- int nsserror;
-
- PORT_Assert(decoded);
- if (!decoded) {
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- return SECFailure;
- }
-
- /* XXX it would be really useful to be able to fetch the CRL directly into
- an arena. This would avoid a copy later on in the decode step */
- PORT_SetError(0);
- derCrl = PK11_FindCrlByName(&slot, &crlHandle, crlKey, type, &url);
- if (derCrl == NULL) {
- /* if we had a problem other than the CRL just didn't exist, return
- * a failure to the upper level */
- nsserror = PORT_GetError();
- if ((nsserror != 0) && (nsserror != SEC_ERROR_CRL_NOT_FOUND)) {
- rv = SECFailure;
- }
- goto loser;
- }
- PORT_Assert(crlHandle != CK_INVALID_HANDLE);
- /* PK11_FindCrlByName obtained a slot reference. */
-
- if (!(decodeoptions & CRL_DECODE_DONT_COPY_DER) ) {
- /* force adoption of the DER from the heap - this will cause it to be
- automatically freed when SEC_DestroyCrl is invoked */
- decodeoptions |= CRL_DECODE_ADOPT_HEAP_DER;
- }
- crl = CERT_DecodeDERCrlWithFlags(NULL, derCrl, type, decodeoptions);
- if (crl) {
- crl->slot = slot;
- slot = NULL; /* adopt it */
- crl->pkcs11ID = crlHandle;
- if (url) {
- crl->url = PORT_ArenaStrdup(crl->arena,url);
- }
- } else {
- rv = SECFailure;
- }
-
- if (url) {
- PORT_Free(url);
- }
-
- if (slot) {
- PK11_FreeSlot(slot);
- }
-
-loser:
- if (derCrl) {
- /* destroy the DER if it was copied to the CRL */
- if (crl && (!(decodeoptions & CRL_DECODE_DONT_COPY_DER)) ) {
- SECITEM_FreeItem(derCrl, PR_TRUE);
- }
- }
-
- *decoded = crl;
-
- return rv;
-}
-
-SECStatus SEC_DestroyCrl(CERTSignedCrl *crl);
-
-CERTSignedCrl *
-crl_storeCRL (PK11SlotInfo *slot,char *url,
- CERTSignedCrl *newCrl, SECItem *derCrl, int type)
-{
- CERTSignedCrl *oldCrl = NULL, *crl = NULL;
- PRBool deleteOldCrl = PR_FALSE;
- CK_OBJECT_HANDLE crlHandle = CK_INVALID_HANDLE;
-
- PORT_Assert(newCrl);
- PORT_Assert(derCrl);
-
- /* we can't use the cache here because we must look in the same
- token */
- SEC_FindCrlByKeyOnSlot(slot, &newCrl->crl.derName, type,
- &oldCrl, CRL_DECODE_SKIP_ENTRIES);
-
- /* if there is an old crl on the token, make sure the one we are
- installing is newer. If not, exit out, otherwise delete the
- old crl.
- */
- if (oldCrl != NULL) {
- /* if it's already there, quietly continue */
- if (SECITEM_CompareItem(newCrl->derCrl, oldCrl->derCrl)
- == SECEqual) {
- crl = newCrl;
- crl->slot = PK11_ReferenceSlot(slot);
- crl->pkcs11ID = oldCrl->pkcs11ID;
- goto done;
- }
- if (!SEC_CrlIsNewer(&newCrl->crl,&oldCrl->crl)) {
-
- if (type == SEC_CRL_TYPE) {
- PORT_SetError(SEC_ERROR_OLD_CRL);
- } else {
- PORT_SetError(SEC_ERROR_OLD_KRL);
- }
-
- goto done;
- }
-
- if ((SECITEM_CompareItem(&newCrl->crl.derName,
- &oldCrl->crl.derName) != SECEqual) &&
- (type == SEC_KRL_TYPE) ) {
-
- PORT_SetError(SEC_ERROR_CKL_CONFLICT);
- goto done;
- }
-
- /* if we have a url in the database, use that one */
- if (oldCrl->url) {
- url = oldCrl->url;
- }
-
- /* really destroy this crl */
- /* first drum it out of the permanment Data base */
- deleteOldCrl = PR_TRUE;
- }
-
- /* invalidate CRL cache for this issuer */
- CERT_CRLCacheRefreshIssuer(NULL, &newCrl->crl.derName);
- /* Write the new entry into the data base */
- crlHandle = PK11_PutCrl(slot, derCrl, &newCrl->crl.derName, url, type);
- if (crlHandle != CK_INVALID_HANDLE) {
- crl = newCrl;
- crl->slot = PK11_ReferenceSlot(slot);
- crl->pkcs11ID = crlHandle;
- if (url) {
- crl->url = PORT_ArenaStrdup(crl->arena,url);
- }
- }
-
-done:
- if (oldCrl) {
- if (deleteOldCrl && crlHandle != CK_INVALID_HANDLE) {
- SEC_DeletePermCRL(oldCrl);
- }
- SEC_DestroyCrl(oldCrl);
- }
-
- return crl;
-}
-
-/*
- *
- * create a new CRL from DER material.
- *
- * The signature on this CRL must be checked before you
- * load it. ???
- */
-CERTSignedCrl *
-SEC_NewCrl(CERTCertDBHandle *handle, char *url, SECItem *derCrl, int type)
-{
- CERTSignedCrl* retCrl = NULL;
- PK11SlotInfo* slot = PK11_GetInternalKeySlot();
- retCrl = PK11_ImportCRL(slot, derCrl, url, type, NULL,
- CRL_IMPORT_BYPASS_CHECKS, NULL, CRL_DECODE_DEFAULT_OPTIONS);
- PK11_FreeSlot(slot);
-
- return retCrl;
-}
-
-CERTSignedCrl *
-SEC_FindCrlByDERCert(CERTCertDBHandle *handle, SECItem *derCrl, int type)
-{
- PRArenaPool *arena;
- SECItem crlKey;
- SECStatus rv;
- CERTSignedCrl *crl = NULL;
-
- /* create a scratch arena */
- arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
- if ( arena == NULL ) {
- return(NULL);
- }
-
- /* extract the database key from the cert */
- rv = CERT_KeyFromDERCrl(arena, derCrl, &crlKey);
- if ( rv != SECSuccess ) {
- goto loser;
- }
-
- /* find the crl */
- crl = SEC_FindCrlByName(handle, &crlKey, type);
-
-loser:
- PORT_FreeArena(arena, PR_FALSE);
- return(crl);
-}
-
-CERTSignedCrl* SEC_DupCrl(CERTSignedCrl* acrl)
-{
- if (acrl)
- {
- PR_AtomicIncrement(&acrl->referenceCount);
- return acrl;
- }
- return NULL;
-}
-
-SECStatus
-SEC_DestroyCrl(CERTSignedCrl *crl)
-{
- if (crl) {
- if (PR_AtomicDecrement(&crl->referenceCount) < 1) {
- if (crl->slot) {
- PK11_FreeSlot(crl->slot);
- }
- if (GetOpaqueCRLFields(crl) &&
- PR_TRUE == GetOpaqueCRLFields(crl)->heapDER) {
- SECITEM_FreeItem(crl->derCrl, PR_TRUE);
- }
- if (crl->arena) {
- PORT_FreeArena(crl->arena, PR_FALSE);
- }
- }
- return SECSuccess;
- } else {
- return SECFailure;
- }
-}
-
-SECStatus
-SEC_LookupCrls(CERTCertDBHandle *handle, CERTCrlHeadNode **nodes, int type)
-{
- CERTCrlHeadNode *head;
- PRArenaPool *arena = NULL;
- SECStatus rv;
-
- *nodes = NULL;
-
- arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
- if ( arena == NULL ) {
- return SECFailure;
- }
-
- /* build a head structure */
- head = (CERTCrlHeadNode *)PORT_ArenaAlloc(arena, sizeof(CERTCrlHeadNode));
- head->arena = arena;
- head->first = NULL;
- head->last = NULL;
- head->dbhandle = handle;
-
- /* Look up the proper crl types */
- *nodes = head;
-
- rv = PK11_LookupCrls(head, type, NULL);
-
- if (rv != SECSuccess) {
- if ( arena ) {
- PORT_FreeArena(arena, PR_FALSE);
- *nodes = NULL;
- }
- }
-
- return rv;
-}
-
-/* These functions simply return the address of the above-declared templates.
-** This is necessary for Windows DLLs. Sigh.
-*/
-SEC_ASN1_CHOOSER_IMPLEMENT(CERT_IssuerAndSNTemplate)
-SEC_ASN1_CHOOSER_IMPLEMENT(CERT_CrlTemplate)
-SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SignedCrlTemplate)
-SEC_ASN1_CHOOSER_IMPLEMENT(CERT_SetOfSignedCrlTemplate)
-
-/* CRL cache code starts here */
-
-/* constructor */
-static SECStatus CachedCrl_Create(CachedCrl** returned, CERTSignedCrl* crl,
- CRLOrigin origin);
-/* destructor */
-static SECStatus CachedCrl_Destroy(CachedCrl* crl);
-
-/* create hash table of CRL entries */
-static SECStatus CachedCrl_Populate(CachedCrl* crlobject);
-
-/* empty the cache content */
-static SECStatus CachedCrl_Depopulate(CachedCrl* crl);
-
-/* are these CRLs the same, as far as the cache is concerned ?
- Or are they the same token object, but with different DER ? */
-
-static SECStatus CachedCrl_Compare(CachedCrl* a, CachedCrl* b, PRBool* isDupe,
- PRBool* isUpdated);
-
-/* create a DPCache object */
-static SECStatus DPCache_Create(CRLDPCache** returned, CERTCertificate* issuer,
- SECItem* subject, SECItem* dp);
-
-/* destructor for CRL DPCache object */
-static SECStatus DPCache_Destroy(CRLDPCache* cache);
-
-/* add a new CRL object to the dynamic array of CRLs of the DPCache, and
- returns the cached CRL object . Needs write access to DPCache. */
-static SECStatus DPCache_AddCRL(CRLDPCache* cache, CachedCrl* crl, PRBool* added);
-
-/* fetch the CRL for this DP from the PKCS#11 tokens */
-static SECStatus DPCache_FetchFromTokens(CRLDPCache* cache, PRTime vfdate, void* wincx);
-
-/* check if a particular SN is in the CRL cache and return its entry */
-static SECStatus DPCache_Lookup(CRLDPCache* cache, SECItem* sn, CERTCrlEntry** returned);
-
-/* update the content of the CRL cache, including fetching of CRLs, and
- reprocessing with specified issuer and date */
-static SECStatus DPCache_GetUpToDate(CRLDPCache* cache, CERTCertificate* issuer,
- PRBool readlocked, PRTime vfdate, void* wincx);
-
-/* returns true if there are CRLs from PKCS#11 slots */
-static PRBool DPCache_HasTokenCRLs(CRLDPCache* cache);
-
-/* remove CRL at offset specified */
-static SECStatus DPCache_RemoveCRL(CRLDPCache* cache, PRUint32 offset);
-
-/* Pick best CRL to use . needs write access */
-static SECStatus DPCache_SelectCRL(CRLDPCache* cache);
-
-/* create an issuer cache object (per CA subject ) */
-static SECStatus IssuerCache_Create(CRLIssuerCache** returned,
- CERTCertificate* issuer,
- SECItem* subject, SECItem* dp);
-
-/* destructor for CRL IssuerCache object */
-SECStatus IssuerCache_Destroy(CRLIssuerCache* cache);
-
-/* add a DPCache to the issuer cache */
-static SECStatus IssuerCache_AddDP(CRLIssuerCache* cache, CERTCertificate* issuer,
- SECItem* subject, SECItem* dp, CRLDPCache** newdpc);
-
-/* get a particular DPCache object from an IssuerCache */
-static CRLDPCache* IssuerCache_GetDPCache(CRLIssuerCache* cache, SECItem* dp);
-
-/*
-** Pre-allocator hash allocator ops.
-*/
-
-/* allocate memory for hash table */
-static void * PR_CALLBACK
-PreAllocTable(void *pool, PRSize size)
-{
- PreAllocator* alloc = (PreAllocator*)pool;
- PORT_Assert(alloc);
- if (!alloc)
- {
- /* no allocator, or buffer full */
- return NULL;
- }
- if (size > (alloc->len - alloc->used))
- {
- /* initial buffer full, let's use the arena */
- alloc->extra += size;
- return PORT_ArenaAlloc(alloc->arena, size);
- }
- /* use the initial buffer */
- alloc->used += size;
- return (char*) alloc->data + alloc->used - size;
-}
-
-/* free hash table memory.
- Individual PreAllocator elements cannot be freed, so this is a no-op. */
-static void PR_CALLBACK
-PreFreeTable(void *pool, void *item)
-{
-}
-
-/* allocate memory for hash table */
-static PLHashEntry * PR_CALLBACK
-PreAllocEntry(void *pool, const void *key)
-{
- return PreAllocTable(pool, sizeof(PLHashEntry));
-}
-
-/* free hash table entry.
- Individual PreAllocator elements cannot be freed, so this is a no-op. */
-static void PR_CALLBACK
-PreFreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
-{
-}
-
-/* methods required for PL hash table functions */
-static PLHashAllocOps preAllocOps =
-{
- PreAllocTable, PreFreeTable,
- PreAllocEntry, PreFreeEntry
-};
-
-/* destructor for PreAllocator object */
-void PreAllocator_Destroy(PreAllocator* PreAllocator)
-{
- if (!PreAllocator)
- {
- return;
- }
- if (PreAllocator->arena)
- {
- PORT_FreeArena(PreAllocator->arena, PR_TRUE);
- }
- if (PreAllocator->data)
- {
- PORT_Free(PreAllocator->data);
- }
- PORT_Free(PreAllocator);
-}
-
-/* constructor for PreAllocator object */
-PreAllocator* PreAllocator_Create(PRSize size)
-{
- PreAllocator prebuffer;
- PreAllocator* prepointer = NULL;
- memset(&prebuffer, 0, sizeof(PreAllocator));
- prebuffer.len = size;
- prebuffer.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
- PORT_Assert(prebuffer.arena);
- if (!prebuffer.arena)
- {
- PreAllocator_Destroy(&prebuffer);
- return NULL;
- }
- if (prebuffer.len)
- {
- prebuffer.data = PORT_Alloc(prebuffer.len);
- if (!prebuffer.data)
- {
- PreAllocator_Destroy(&prebuffer);
- return NULL;
- }
- }
- else
- {
- prebuffer.data = NULL;
- }
- prepointer = (PreAllocator*)PORT_Alloc(sizeof(PreAllocator));
- if (!prepointer)
- {
- PreAllocator_Destroy(&prebuffer);
- return NULL;
- }
- *prepointer = prebuffer;
- return prepointer;
-}
-
-/* global CRL cache object */
-static CRLCache crlcache = { NULL, NULL };
-
-/* initial state is off */
-static PRBool crlcache_initialized = PR_FALSE;
-
-PRTime CRLCache_Empty_TokenFetch_Interval = 60 * 1000000; /* how often
- to query the tokens for CRL objects, in order to discover new objects, if
- the cache does not contain any token CRLs . In microseconds */
-
-PRTime CRLCache_TokenRefetch_Interval = 600 * 1000000 ; /* how often
- to query the tokens for CRL objects, in order to discover new objects, if
- the cache already contains token CRLs In microseconds */
-
-PRTime CRLCache_ExistenceCheck_Interval = 60 * 1000000; /* how often to check
- if a token CRL object still exists. In microseconds */
-
-/* this function is called at NSS initialization time */
-SECStatus InitCRLCache(void)
-{
- if (PR_FALSE == crlcache_initialized)
- {
- PORT_Assert(NULL == crlcache.lock);
- PORT_Assert(NULL == crlcache.issuers);
- if (crlcache.lock || crlcache.issuers)
- {
- /* CRL cache already partially initialized */
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
-#ifdef GLOBAL_RWLOCK
- crlcache.lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL);
-#else
- crlcache.lock = PR_NewLock();
-#endif
- if (!crlcache.lock)
- {
- return SECFailure;
- }
- crlcache.issuers = PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare,
- PL_CompareValues, NULL, NULL);
- if (!crlcache.issuers)
- {
-#ifdef GLOBAL_RWLOCK
- NSSRWLock_Destroy(crlcache.lock);
-#else
- PR_DestroyLock(crlcache.lock);
-#endif
- crlcache.lock = NULL;
- return SECFailure;
- }
- crlcache_initialized = PR_TRUE;
- return SECSuccess;
- }
- else
- {
- PORT_Assert(crlcache.lock);
- PORT_Assert(crlcache.issuers);
- if ( (NULL == crlcache.lock) || (NULL == crlcache.issuers) )
- {
- /* CRL cache not fully initialized */
- return SECFailure;
- }
- else
- {
- /* CRL cache already initialized */
- return SECSuccess;
- }
- }
-}
-
-/* destructor for CRL DPCache object */
-static SECStatus DPCache_Destroy(CRLDPCache* cache)
-{
- PRUint32 i = 0;
- PORT_Assert(cache);
- if (!cache)
- {
- PORT_Assert(0);
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- if (cache->lock)
- {
-#ifdef DPC_RWLOCK
- NSSRWLock_Destroy(cache->lock);
-#else
- PR_DestroyLock(cache->lock);
-#endif
- }
- else
- {
- PORT_Assert(0);
- return SECFailure;
- }
- /* destroy all our CRL objects */
- for (i=0;i<cache->ncrls;i++)
- {
- if (!cache->crls || !cache->crls[i] ||
- SECSuccess != CachedCrl_Destroy(cache->crls[i]))
- {
- return SECFailure;
- }
- }
- /* free the array of CRLs */
- if (cache->crls)
- {
- PORT_Free(cache->crls);
- }
- /* destroy the cert */
- if (cache->issuer)
- {
- CERT_DestroyCertificate(cache->issuer);
- }
- /* free the subject */
- if (cache->subject)
- {
- SECITEM_FreeItem(cache->subject, PR_TRUE);
- }
- /* free the distribution points */
- if (cache->distributionPoint)
- {
- SECITEM_FreeItem(cache->distributionPoint, PR_TRUE);
- }
- PORT_Free(cache);
- return SECSuccess;
-}
-
-/* destructor for CRL IssuerCache object */
-SECStatus IssuerCache_Destroy(CRLIssuerCache* cache)
-{
- PORT_Assert(cache);
- if (!cache)
- {
- PORT_Assert(0);
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
-#ifdef XCRL
- if (cache->lock)
- {
- NSSRWLock_Destroy(cache->lock);
- }
- else
- {
- PORT_Assert(0);
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- if (cache->issuer)
- {
- CERT_DestroyCertificate(cache->issuer);
- }
-#endif
- /* free the subject */
- if (cache->subject)
- {
- SECITEM_FreeItem(cache->subject, PR_TRUE);
- }
- if (SECSuccess != DPCache_Destroy(cache->dpp))
- {
- PORT_Assert(0);
- return SECFailure;
- }
- PORT_Free(cache);
- return SECSuccess;
-}
-
-/* callback function used in hash table destructor */
-static PRIntn PR_CALLBACK FreeIssuer(PLHashEntry *he, PRIntn i, void *arg)
-{
- CRLIssuerCache* issuer = NULL;
- SECStatus* rv = (SECStatus*) arg;
-
- PORT_Assert(he);
- if (!he)
- {
- return HT_ENUMERATE_NEXT;
- }
- issuer = (CRLIssuerCache*) he->value;
- PORT_Assert(issuer);
- if (issuer)
- {
- if (SECSuccess != IssuerCache_Destroy(issuer))
- {
- PORT_Assert(rv);
- if (rv)
- {
- *rv = SECFailure;
- }
- return HT_ENUMERATE_NEXT;
- }
- }
- return HT_ENUMERATE_NEXT;
-}
-
-/* needs to be called at NSS shutdown time
- This will destroy the global CRL cache, including
- - the hash table of issuer cache objects
- - the issuer cache objects
- - DPCache objects in issuer cache objects */
-SECStatus ShutdownCRLCache(void)
-{
- SECStatus rv = SECSuccess;
- if (PR_FALSE == crlcache_initialized &&
- !crlcache.lock && !crlcache.issuers)
- {
- /* CRL cache has already been shut down */
- return SECSuccess;
- }
- if (PR_TRUE == crlcache_initialized &&
- (!crlcache.lock || !crlcache.issuers))
- {
- /* CRL cache has partially been shut down */
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- /* empty the cache */
- /* free the issuers */
- PL_HashTableEnumerateEntries(crlcache.issuers, &FreeIssuer, &rv);
- /* free the hash table of issuers */
- PL_HashTableDestroy(crlcache.issuers);
- crlcache.issuers = NULL;
- /* free the global lock */
-#ifdef GLOBAL_RWLOCK
- NSSRWLock_Destroy(crlcache.lock);
-#else
- PR_DestroyLock(crlcache.lock);
-#endif
- crlcache.lock = NULL;
- crlcache_initialized = PR_FALSE;
- return rv;
-}
-
-/* add a new CRL object to the dynamic array of CRLs of the DPCache, and
- returns the cached CRL object . Needs write access to DPCache. */
-static SECStatus DPCache_AddCRL(CRLDPCache* cache, CachedCrl* newcrl,
- PRBool* added)
-{
- CachedCrl** newcrls = NULL;
- PRUint32 i = 0;
- PORT_Assert(cache);
- PORT_Assert(newcrl);
- PORT_Assert(added);
- if (!cache || !newcrl || !added)
- {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
-
- *added = PR_FALSE;
- /* before adding a new CRL, check if it is a duplicate */
- for (i=0;i<cache->ncrls;i++)
- {
- CachedCrl* existing = NULL;
- SECStatus rv = SECSuccess;
- PRBool dupe = PR_FALSE, updated = PR_FALSE;
- if (!cache->crls)
- {
- PORT_Assert(0);
- return SECFailure;
- }
- existing = cache->crls[i];
- if (!existing)
- {
- PORT_Assert(0);
- return SECFailure;
- }
- rv = CachedCrl_Compare(existing, newcrl, &dupe, &updated);
- if (SECSuccess != rv)
- {
- PORT_Assert(0);
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- if (PR_TRUE == dupe)
- {
- /* dupe */
- PORT_SetError(SEC_ERROR_CRL_ALREADY_EXISTS);
- return SECSuccess;
- }
- if (PR_TRUE == updated)
- {
- /* this token CRL is in the same slot and has the same object ID,
- but different content. We need to remove the old object */
- if (SECSuccess != DPCache_RemoveCRL(cache, i))
- {
- PORT_Assert(0);
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return PR_FALSE;
- }
- }
- }
-
- newcrls = (CachedCrl**)PORT_Realloc(cache->crls,
- (cache->ncrls+1)*sizeof(CachedCrl*));
- if (!newcrls)
- {
- return SECFailure;
- }
- cache->crls = newcrls;
- cache->ncrls++;
- cache->crls[cache->ncrls-1] = newcrl;
- *added = PR_TRUE;
- return SECSuccess;
-}
-
-/* remove CRL at offset specified */
-static SECStatus DPCache_RemoveCRL(CRLDPCache* cache, PRUint32 offset)
-{
- CachedCrl* acrl = NULL;
- PORT_Assert(cache);
- if (!cache || (!cache->crls) || (!(offset<cache->ncrls)) )
- {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- acrl = cache->crls[offset];
- PORT_Assert(acrl);
- if (!acrl)
- {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- cache->crls[offset] = cache->crls[cache->ncrls-1];
- cache->crls[cache->ncrls-1] = NULL;
- cache->ncrls--;
- if (cache->selected == acrl) {
- cache->selected = NULL;
- }
- if (SECSuccess != CachedCrl_Destroy(acrl))
- {
- PORT_Assert(0);
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- return SECSuccess;
-}
-
-/* check whether a CRL object stored in a PKCS#11 token still exists in
- that token . This has to be efficient (the entire CRL value cannot be
- transferred accross the token boundaries), so this is accomplished by
- simply fetching the subject attribute and making sure it hasn't changed .
- Note that technically, the CRL object could have been replaced with a new
- PKCS#11 object of the same ID and subject (which actually happens in
- softoken), but this function has no way of knowing that the object
- value changed, since CKA_VALUE isn't checked. */
-static PRBool TokenCRLStillExists(CERTSignedCrl* crl)
-{
- NSSItem newsubject;
- SECItem subject;
- CK_ULONG crl_class;
- PRStatus status;
- PK11SlotInfo* slot = NULL;
- nssCryptokiObject instance;
- NSSArena* arena;
- PRBool xstatus = PR_TRUE;
- SECItem* oldSubject = NULL;
-
- PORT_Assert(crl);
- if (!crl)
- {
- return PR_FALSE;
- }
- slot = crl->slot;
- PORT_Assert(crl->slot);
- if (!slot)
- {
- return PR_FALSE;
- }
- oldSubject = &crl->crl.derName;
- PORT_Assert(oldSubject);
- if (!oldSubject)
- {
- return PR_FALSE;
- }
-
- /* query subject and type attributes in order to determine if the
- object has been deleted */
-
- /* first, make an nssCryptokiObject */
- instance.handle = crl->pkcs11ID;
- PORT_Assert(instance.handle);
- if (!instance.handle)
- {
- return PR_FALSE;
- }
- instance.token = PK11Slot_GetNSSToken(slot);
- PORT_Assert(instance.token);
- if (!instance.token)
- {
- return PR_FALSE;
- }
- instance.isTokenObject = PR_TRUE;
- instance.label = NULL;
-
- arena = NSSArena_Create();
- PORT_Assert(arena);
- if (!arena)
- {
- return PR_FALSE;
- }
-
- status = nssCryptokiCRL_GetAttributes(&instance,
- NULL, /* XXX sessionOpt */
- arena,
- NULL,
- &newsubject, /* subject */
- &crl_class, /* class */
- NULL,
- NULL);
- if (PR_SUCCESS == status)
- {
- subject.data = newsubject.data;
- subject.len = newsubject.size;
- if (SECITEM_CompareItem(oldSubject, &subject) != SECEqual)
- {
- xstatus = PR_FALSE;
- }
- if (CKO_NETSCAPE_CRL != crl_class)
- {
- xstatus = PR_FALSE;
- }
- }
- else
- {
- xstatus = PR_FALSE;
- }
- NSSArena_Destroy(arena);
- return xstatus;
-}
-
-/* verify the signature of a CRL against its issuer at a given date */
-static SECStatus CERT_VerifyCRL(
- CERTSignedCrl* crlobject,
- CERTCertificate* issuer,
- PRTime vfdate,
- void* wincx)
-{
- return CERT_VerifySignedData(&crlobject->signatureWrap,
- issuer, vfdate, wincx);
-}
-
-/* verify a CRL and update cache state */
-static SECStatus CachedCrl_Verify(CRLDPCache* cache, CachedCrl* crlobject,
- PRTime vfdate, void* wincx)
-{
- /* Check if it is an invalid CRL
- if we got a bad CRL, we want to cache it in order to avoid
- subsequent fetches of this same identical bad CRL. We set
- the cache to the invalid state to ensure that all certs
- on this DP are considered revoked from now on. The cache
- object will remain in this state until the bad CRL object
- is removed from the token it was fetched from. If the cause
- of the failure is that we didn't have the issuer cert to
- verify the signature, this state can be cleared when
- the issuer certificate becomes available if that causes the
- signature to verify */
-
- if (!cache || !crlobject)
- {
- PORT_Assert(0);
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- if (PR_TRUE == GetOpaqueCRLFields(crlobject->crl)->decodingError)
- {
- crlobject->sigChecked = PR_TRUE; /* we can never verify a CRL
- with bogus DER. Mark it checked so we won't try again */
- PORT_SetError(SEC_ERROR_BAD_DER);
- return SECSuccess;
- }
- else
- {
- SECStatus signstatus = SECFailure;
- if (cache->issuer)
- {
- signstatus = CERT_VerifyCRL(crlobject->crl, cache->issuer, vfdate,
- wincx);
- }
- if (SECSuccess != signstatus)
- {
- if (!cache->issuer)
- {
- /* we tried to verify without an issuer cert . This is
- because this CRL came through a call to SEC_FindCrlByName.
- So, we don't cache this verification failure. We'll try
- to verify the CRL again when a certificate from that issuer
- becomes available */
- } else
- {
- crlobject->sigChecked = PR_TRUE;
- }
- PORT_SetError(SEC_ERROR_CRL_BAD_SIGNATURE);
- return SECSuccess;
- } else
- {
- crlobject->sigChecked = PR_TRUE;
- crlobject->sigValid = PR_TRUE;
- }
- }
-
- return SECSuccess;
-}
-
-/* fetch the CRLs for this DP from the PKCS#11 tokens */
-static SECStatus DPCache_FetchFromTokens(CRLDPCache* cache, PRTime vfdate,
- void* wincx)
-{
- SECStatus rv = SECSuccess;
- CERTCrlHeadNode head;
- if (!cache)
- {
- PORT_Assert(0);
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- /* first, initialize list */
- memset(&head, 0, sizeof(head));
- head.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
- rv = pk11_RetrieveCrls(&head, cache->subject, wincx);
-
- /* if this function fails, something very wrong happened, such as an out
- of memory error during CRL decoding. We don't want to proceed and must
- mark the cache object invalid */
- if (SECFailure == rv)
- {
- /* fetch failed, add error bit */
- cache->invalid |= CRL_CACHE_LAST_FETCH_FAILED;
- } else
- {
- /* fetch was successful, clear this error bit */
- cache->invalid &= (~CRL_CACHE_LAST_FETCH_FAILED);
- }
-
- /* add any CRLs found to our array */
- if (SECSuccess == rv)
- {
- CERTCrlNode* crlNode = NULL;
-
- for (crlNode = head.first; crlNode ; crlNode = crlNode->next)
- {
- CachedCrl* returned = NULL;
- CERTSignedCrl* crlobject = crlNode->crl;
- if (!crlobject)
- {
- PORT_Assert(0);
- continue;
- }
- rv = CachedCrl_Create(&returned, crlobject, CRL_OriginToken);
- if (SECSuccess == rv)
- {
- PRBool added = PR_FALSE;
- rv = DPCache_AddCRL(cache, returned, &added);
- if (PR_TRUE != added)
- {
- rv = CachedCrl_Destroy(returned);
- returned = NULL;
- }
- else
- {
- rv = CachedCrl_Verify(cache, returned, vfdate, wincx);
- }
- }
- else
- {
- /* not enough memory to add the CRL to the cache. mark it
- invalid so we will try again . */
- cache->invalid |= CRL_CACHE_LAST_FETCH_FAILED;
- }
- if (SECFailure == rv)
- {
- break;
- }
- }
- }
-
- if (head.arena)
- {
- CERTCrlNode* crlNode = NULL;
- /* clean up the CRL list in case we got a partial one
- during a failed fetch */
- for (crlNode = head.first; crlNode ; crlNode = crlNode->next)
- {
- if (crlNode->crl)
- {
- SEC_DestroyCrl(crlNode->crl); /* free the CRL. Either it got
- added to the cache and the refcount got bumped, or not, and
- thus we need to free its RAM */
- }
- }
- PORT_FreeArena(head.arena, PR_FALSE); /* destroy CRL list */
- }
-
- return rv;
-}
-
-/* check if a particular SN is in the CRL cache and return its entry */
-static SECStatus DPCache_Lookup(CRLDPCache* cache, SECItem* sn,
- CERTCrlEntry** returned)
-{
- CERTCrlEntry* acrlEntry = NULL;
- if (!cache || !sn || !returned)
- {
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- /* no cache or SN to look up, or no way to return entry */
- return SECFailure;
- }
- if (0 != cache->invalid)
- {
- /* the cache contains a bad CRL, or there was a CRL fetching error.
- consider all certs revoked as a security measure */
- PORT_SetError(SEC_ERROR_CRL_INVALID);
- return SECFailure;
- }
- if (!cache->selected)
- {
- /* no CRL means no entry to return, but this is OK */
- *returned = NULL;
- return SECSuccess;
- }
- PORT_Assert(cache->selected->entries);
- if (!cache->selected->entries)
- {
- return SECFailure;
- }
- /* XXX should probably use CachedCrl accessor function here */
- acrlEntry = PL_HashTableLookup(cache->selected->entries, (void*)sn);
- if (acrlEntry)
- {
- *returned = acrlEntry;
- }
- return SECSuccess;
-}
-
-#if defined(DPC_RWLOCK)
-
-#define DPCache_LockWrite() \
-{ \
- if (readlocked) \
- { \
- NSSRWLock_UnlockRead(cache->lock); \
- } \
- NSSRWLock_LockWrite(cache->lock); \
-}
-
-#define DPCache_UnlockWrite() \
-{ \
- if (readlocked) \
- { \
- NSSRWLock_LockRead(cache->lock); \
- } \
- NSSRWLock_UnlockWrite(cache->lock); \
-}
-
-#else
-
-/* with a global lock, we are always locked for read before we need write
- access, so do nothing */
-
-#define DPCache_LockWrite() \
-{ \
-}
-
-#define DPCache_UnlockWrite() \
-{ \
-}
-
-#endif
-
-/* update the content of the CRL cache, including fetching of CRLs, and
- reprocessing with specified issuer and date . We are always holding
- either the read or write lock on DPCache upon entry. */
-static SECStatus DPCache_GetUpToDate(CRLDPCache* cache, CERTCertificate*
- issuer, PRBool readlocked, PRTime vfdate,
- void* wincx)
-{
- /* Update the CRLDPCache now. We don't cache token CRL lookup misses
- yet, as we have no way of getting notified of new PKCS#11 object
- creation that happens in a token */
- SECStatus rv = SECSuccess;
- PRUint32 i = 0;
- PRBool forcedrefresh = PR_FALSE;
- PRBool dirty = PR_FALSE; /* whether something was changed in the
- cache state during this update cycle */
- PRBool hastokenCRLs = PR_FALSE;
- PRTime now = 0;
- PRTime lastfetch = 0;
-
- if (!cache)
- {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
-
- /* first, make sure we have obtained all the CRLs we need.
- We do an expensive token fetch in the following cases :
- 1) cache is empty because no fetch was ever performed yet
- 2) cache is explicitly set to refresh state
- 3) cache is in invalid state because last fetch failed
- 4) cache contains no token CRLs, and it's been more than one minute
- since the last fetch
- 5) cache contains token CRLs, and it's been more than 10 minutes since
- the last fetch
- */
- forcedrefresh = cache->refresh;
- lastfetch = cache->lastfetch;
- if (PR_TRUE != forcedrefresh &&
- (!(cache->invalid & CRL_CACHE_LAST_FETCH_FAILED)))
- {
- now = PR_Now();
- hastokenCRLs = DPCache_HasTokenCRLs(cache);
- }
- if ( (0 == lastfetch) ||
-
- (PR_TRUE == forcedrefresh) ||
-
- (cache->invalid & CRL_CACHE_LAST_FETCH_FAILED) ||
-
- ( (PR_FALSE == hastokenCRLs) &&
- ( (now - cache->lastfetch > CRLCache_Empty_TokenFetch_Interval) ||
- (now < cache->lastfetch)) ) ||
-
- ( (PR_TRUE == hastokenCRLs) &&
- ((now - cache->lastfetch > CRLCache_TokenRefetch_Interval) ||
- (now < cache->lastfetch)) ) )
- {
- /* the cache needs to be refreshed, and/or we had zero CRL for this
- DP. Try to get one from PKCS#11 tokens */
- DPCache_LockWrite();
- /* check if another thread updated before us, and skip update if so */
- if (lastfetch == cache->lastfetch)
- {
- /* we are the first */
- rv = DPCache_FetchFromTokens(cache, vfdate, wincx);
- if (PR_TRUE == cache->refresh)
- {
- cache->refresh = PR_FALSE; /* clear refresh state */
- }
- dirty = PR_TRUE;
- cache->lastfetch = PR_Now();
- }
- DPCache_UnlockWrite();
- }
-
- /* now, make sure we have no extraneous CRLs (deleted token objects)
- we'll do this inexpensive existence check either
- 1) if there was a token object fetch
- 2) every minute */
- if (( PR_TRUE != dirty) && (!now) )
- {
- now = PR_Now();
- }
- if ( (PR_TRUE == dirty) ||
- ( (now - cache->lastcheck > CRLCache_ExistenceCheck_Interval) ||
- (now < cache->lastcheck)) )
- {
- PRBool mustunlock = PR_FALSE;
- PRTime lastcheck = cache->lastcheck;
- /* check if all CRLs still exist */
- for (i = 0; (i < cache->ncrls) ; i++)
- {
- CachedCrl* savcrl = cache->crls[i];
- if ( (!savcrl) || (savcrl && CRL_OriginToken != savcrl->origin))
- {
- /* we only want to check token CRLs */
- continue;
- }
- if ((PR_TRUE != TokenCRLStillExists(savcrl->crl)))
- {
-
- /* this CRL is gone */
- if (PR_TRUE != mustunlock)
- {
- DPCache_LockWrite();
- mustunlock = PR_TRUE;
- }
- /* first, we need to check if another thread did an update
- before we did */
- if (lastcheck == cache->lastcheck)
- {
- /* the CRL is gone. And we are the one to do the update */
- DPCache_RemoveCRL(cache, i);
- dirty = PR_TRUE;
- }
- /* stay locked here intentionally so we do all the other
- updates in this thread for the remaining CRLs */
- }
- }
- if (PR_TRUE == mustunlock)
- {
- cache->lastcheck = PR_Now();
- DPCache_UnlockWrite();
- mustunlock = PR_FALSE;
- }
- }
-
- /* add issuer certificate if it was previously unavailable */
- if (issuer && (NULL == cache->issuer) &&
- (SECSuccess == CERT_CheckCertUsage(issuer, KU_CRL_SIGN)))
- {
- /* if we didn't have a valid issuer cert yet, but we do now. add it */
- DPCache_LockWrite();
- if (!cache->issuer)
- {
- dirty = PR_TRUE;
- cache->issuer = CERT_DupCertificate(issuer);
- }
- DPCache_UnlockWrite();
- }
-
- /* verify CRLs that couldn't be checked when inserted into the cache
- because the issuer cert or a verification date was unavailable.
- These are CRLs that were inserted into the cache through
- SEC_FindCrlByName, or through manual insertion, rather than through a
- certificate verification (CERT_CheckCRL) */
-
- if (cache->issuer && vfdate )
- {
- PRBool mustunlock = PR_FALSE;
- /* re-process all unverified CRLs */
- for (i = 0; i < cache->ncrls ; i++)
- {
- CachedCrl* savcrl = cache->crls[i];
- if (!savcrl)
- {
- continue;
- }
- if (PR_TRUE != savcrl->sigChecked)
- {
- if (PR_TRUE != mustunlock)
- {
- DPCache_LockWrite();
- mustunlock = PR_TRUE;
- }
- /* first, we need to check if another thread updated
- it before we did, and abort if it has been modified since
- we acquired the lock. Make sure first that the CRL is still
- in the array at the same position */
- if ( (i<cache->ncrls) && (savcrl == cache->crls[i]) &&
- (PR_TRUE != savcrl->sigChecked) )
- {
- /* the CRL is still there, unverified. Do it */
- CachedCrl_Verify(cache, savcrl, vfdate, wincx);
- dirty = PR_TRUE;
- }
- /* stay locked here intentionally so we do all the other
- updates in this thread for the remaining CRLs */
- }
- if (PR_TRUE == mustunlock)
- {
- DPCache_UnlockWrite();
- mustunlock = PR_FALSE;
- }
- }
- }
-
- if (dirty || cache->mustchoose)
- {
- /* changes to the content of the CRL cache necessitate examining all
- CRLs for selection of the most appropriate one to cache */
- DPCache_LockWrite();
- DPCache_SelectCRL(cache);
- cache->mustchoose = PR_FALSE;
- DPCache_UnlockWrite();
- }
-
- return rv;
-}
-
-/* callback for qsort to sort by thisUpdate */
-static int SortCRLsByThisUpdate(const void* arg1, const void* arg2)
-{
- PRTime timea, timeb;
- SECStatus rv = SECSuccess;
- CachedCrl* a, *b;
-
- a = *(CachedCrl**) arg1;
- b = *(CachedCrl**) arg2;
-
- if (!a || !b)
- {
- PORT_Assert(0);
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- rv = SECFailure;
- }
-
- if (SECSuccess == rv)
- {
- rv = DER_DecodeTimeChoice(&timea, &a->crl->crl.lastUpdate);
- }
- if (SECSuccess == rv)
- {
- rv = DER_DecodeTimeChoice(&timeb, &b->crl->crl.lastUpdate);
- }
- if (SECSuccess == rv)
- {
- if (timea > timeb)
- {
- return 1; /* a is better than b */
- }
- if (timea < timeb )
- {
- return -1; /* a is not as good as b */
- }
- }
-
- /* if they are equal, or if all else fails, use pointer differences */
- PORT_Assert(a != b); /* they should never be equal */
- return a>b?1:-1;
-}
-
-/* callback for qsort to sort a set of disparate CRLs, some of which are
- invalid DER or failed signature check.
-
- Validated CRLs are differentiated by thisUpdate .
- Validated CRLs are preferred over non-validated CRLs .
- Proper DER CRLs are preferred over non-DER data .
-*/
-static int SortImperfectCRLs(const void* arg1, const void* arg2)
-{
- CachedCrl* a, *b;
-
- a = *(CachedCrl**) arg1;
- b = *(CachedCrl**) arg2;
-
- if (!a || !b)
- {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- PORT_Assert(0);
- }
- else
- {
- PRBool aDecoded = PR_FALSE, bDecoded = PR_FALSE;
- if ( (PR_TRUE == a->sigValid) && (PR_TRUE == b->sigValid) )
- {
- /* both CRLs have been validated, choose the latest one */
- return SortCRLsByThisUpdate(arg1, arg2);
- }
- if (PR_TRUE == a->sigValid)
- {
- return 1; /* a is greater than b */
- }
- if (PR_TRUE == b->sigValid)
- {
- return -1; /* a is not as good as b */
- }
- aDecoded = GetOpaqueCRLFields(a->crl)->decodingError;
- bDecoded = GetOpaqueCRLFields(b->crl)->decodingError;
- /* neither CRL had its signature check pass */
- if ( (PR_FALSE == aDecoded) && (PR_FALSE == bDecoded) )
- {
- /* both CRLs are proper DER, choose the latest one */
- return SortCRLsByThisUpdate(arg1, arg2);
- }
- if (PR_FALSE == aDecoded)
- {
- return 1; /* a is better than b */
- }
- if (PR_FALSE == bDecoded)
- {
- return -1; /* a is not as good as b */
- }
- /* both are invalid DER. sigh. */
- }
- /* if they are equal, or if all else fails, use pointer differences */
- PORT_Assert(a != b); /* they should never be equal */
- return a>b?1:-1;
-}
-
-
-/* Pick best CRL to use . needs write access */
-static SECStatus DPCache_SelectCRL(CRLDPCache* cache)
-{
- PRUint32 i;
- PRBool valid = PR_TRUE;
- CachedCrl* selected = NULL;
-
- PORT_Assert(cache);
- if (!cache)
- {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- /* if any invalid CRL is present, then the CRL cache is
- considered invalid, for security reasons */
- for (i = 0 ; i<cache->ncrls; i++)
- {
- if (!cache->crls[i] || !cache->crls[i]->sigChecked ||
- !cache->crls[i]->sigValid)
- {
- valid = PR_FALSE;
- break;
- }
- }
- if (PR_TRUE == valid)
- {
- /* all CRLs are valid, clear this error */
- cache->invalid &= (~CRL_CACHE_INVALID_CRLS);
- } else
- {
- /* some CRLs are invalid, set this error */
- cache->invalid |= CRL_CACHE_INVALID_CRLS;
- }
-
- if (cache->invalid)
- {
- /* cache is in an invalid state, so destroy it */
- if (cache->selected)
- {
- if (SECSuccess != CachedCrl_Depopulate(cache->selected))
- {
- PORT_Assert(0);
- return SECFailure;
- }
- cache->selected = NULL;
- }
- /* also sort the CRLs imperfectly */
- qsort(cache->crls, cache->ncrls, sizeof(CachedCrl*),
- SortImperfectCRLs);
- return SECSuccess;
- }
- /* all CRLs are good, sort them by thisUpdate */
- qsort(cache->crls, cache->ncrls, sizeof(CachedCrl*),
- SortCRLsByThisUpdate);
-
- if (cache->ncrls)
- {
- /* pick the newest CRL */
- selected = cache->crls[cache->ncrls-1];
-
- /* and populate the cache */
- if (SECSuccess != CachedCrl_Populate(selected))
- {
- return SECFailure;
- }
- }
-
- /* free the old CRL cache, if it's for a different CRL */
- if (cache->selected && cache->selected != selected)
- {
- if (SECSuccess != CachedCrl_Depopulate(cache->selected))
- {
- return SECFailure;
- }
- }
-
- cache->selected = selected;
-
- return SECSuccess;
-}
-
-/* initialize a DPCache object */
-static SECStatus DPCache_Create(CRLDPCache** returned, CERTCertificate* issuer,
- SECItem* subject, SECItem* dp)
-{
- CRLDPCache* cache = NULL;
- PORT_Assert(returned);
- /* issuer and dp are allowed to be NULL */
- if (!returned || !subject)
- {
- PORT_Assert(0);
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- *returned = NULL;
- cache = PORT_ZAlloc(sizeof(CRLDPCache));
- PORT_Assert(cache);
- if (!cache)
- {
- return SECFailure;
- }
-#ifdef DPC_RWLOCK
- cache->lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL);
-#else
- cache->lock = PR_NewLock();
-#endif
- if (!cache->lock)
- {
- return SECFailure;
- }
- if (issuer)
- {
- cache->issuer = CERT_DupCertificate(issuer);
- }
- cache->distributionPoint = SECITEM_DupItem(dp);
- cache->subject = SECITEM_DupItem(subject);
- cache->lastfetch = 0;
- cache->lastcheck = 0;
- *returned = cache;
- return SECSuccess;
-}
-
-/* create an issuer cache object (per CA subject ) */
-static SECStatus IssuerCache_Create(CRLIssuerCache** returned,
- CERTCertificate* issuer,
- SECItem* subject, SECItem* dp)
-{
- SECStatus rv = SECSuccess;
- CRLIssuerCache* cache = NULL;
- PORT_Assert(returned);
- PORT_Assert(subject);
- /* issuer and dp are allowed to be NULL */
- if (!returned || !subject)
- {
- PORT_Assert(0);
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- *returned = NULL;
- cache = (CRLIssuerCache*) PORT_ZAlloc(sizeof(CRLIssuerCache));
- if (!cache)
- {
- return SECFailure;
- }
- cache->subject = SECITEM_DupItem(subject);
-#ifdef XCRL
- cache->lock = NSSRWLock_New(NSS_RWLOCK_RANK_NONE, NULL);
- if (!cache->lock)
- {
- rv = SECFailure;
- }
- if (SECSuccess == rv && issuer)
- {
- cache->issuer = CERT_DupCertificate(issuer);
- if (!cache->issuer)
- {
- rv = SECFailure;
- }
- }
-#endif
- if (SECSuccess != rv)
- {
- PORT_Assert(SECSuccess == IssuerCache_Destroy(cache));
- return SECFailure;
- }
- *returned = cache;
- return SECSuccess;
-}
-
-/* add a DPCache to the issuer cache */
-static SECStatus IssuerCache_AddDP(CRLIssuerCache* cache,
- CERTCertificate* issuer,
- SECItem* subject, SECItem* dp,
- CRLDPCache** newdpc)
-{
- /* now create the required DP cache object */
- if (!cache || !subject || !newdpc)
- {
- PORT_Assert(0);
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- if (!dp)
- {
- /* default distribution point */
- SECStatus rv = DPCache_Create(&cache->dpp, issuer, subject, NULL);
- if (SECSuccess == rv)
- {
- *newdpc = cache->dpp;
- return SECSuccess;
- }
- }
- else
- {
- /* we should never hit this until we support multiple DPs */
- PORT_Assert(dp);
- /* XCRL allocate a new distribution point cache object, initialize it,
- and add it to the hash table of DPs */
- }
- return SECFailure;
-}
-
-/* add an IssuerCache to the global hash table of issuers */
-static SECStatus CRLCache_AddIssuer(CRLIssuerCache* issuer)
-{
- PORT_Assert(issuer);
- PORT_Assert(crlcache.issuers);
- if (!issuer || !crlcache.issuers)
- {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- if (NULL == PL_HashTableAdd(crlcache.issuers, (void*) issuer->subject,
- (void*) issuer))
- {
- return SECFailure;
- }
- return SECSuccess;
-}
-
-/* retrieve the issuer cache object for a given issuer subject */
-static SECStatus CRLCache_GetIssuerCache(CRLCache* cache, SECItem* subject,
- CRLIssuerCache** returned)
-{
- /* we need to look up the issuer in the hash table */
- SECStatus rv = SECSuccess;
- PORT_Assert(cache);
- PORT_Assert(subject);
- PORT_Assert(returned);
- PORT_Assert(crlcache.issuers);
- if (!cache || !subject || !returned || !crlcache.issuers)
- {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- rv = SECFailure;
- }
-
- if (SECSuccess == rv)
- {
- *returned = (CRLIssuerCache*) PL_HashTableLookup(crlcache.issuers,
- (void*) subject);
- }
-
- return rv;
-}
-
-/* retrieve the full CRL object that best matches the content of a DPCache */
-static CERTSignedCrl* GetBestCRL(CRLDPCache* cache, PRBool entries)
-{
- CachedCrl* acrl = NULL;
-
- PORT_Assert(cache);
- if (!cache)
- {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return NULL;
- }
-
- if (0 == cache->ncrls)
- {
- /* empty cache*/
- return NULL;
- }
-
- /* if we have a valid full CRL selected, return it */
- if (cache->selected)
- {
- return SEC_DupCrl(cache->selected->crl);
- }
-
- /* otherwise, use latest valid DER CRL */
- acrl = cache->crls[cache->ncrls-1];
-
- if (acrl && (PR_FALSE == GetOpaqueCRLFields(acrl->crl)->decodingError) )
- {
- SECStatus rv = SECSuccess;
- if (PR_TRUE == entries)
- {
- rv = CERT_CompleteCRLDecodeEntries(acrl->crl);
- }
- if (SECSuccess == rv)
- {
- return SEC_DupCrl(acrl->crl);
- }
- }
-
- return NULL;
-}
-
-/* get a particular DPCache object from an IssuerCache */
-static CRLDPCache* IssuerCache_GetDPCache(CRLIssuerCache* cache, SECItem* dp)
-{
- CRLDPCache* dpp = NULL;
- PORT_Assert(cache);
- /* XCRL for now we only support the "default" DP, ie. the
- full CRL. So we can return the global one without locking. In
- the future we will have a lock */
- PORT_Assert(NULL == dp);
- if (!cache || dp)
- {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return NULL;
- }
-#ifdef XCRL
- NSSRWLock_LockRead(cache->lock);
-#endif
- dpp = cache->dpp;
-#ifdef XCRL
- NSSRWLock_UnlockRead(cache->lock);
-#endif
- return dpp;
-}
-
-/* get a DPCache object for the given issuer subject and dp
- Automatically creates the cache object if it doesn't exist yet.
- */
-static SECStatus AcquireDPCache(CERTCertificate* issuer, SECItem* subject,
- SECItem* dp, int64 t, void* wincx,
- CRLDPCache** dpcache, PRBool* writeLocked)
-{
- SECStatus rv = SECSuccess;
- CRLIssuerCache* issuercache = NULL;
-#ifdef GLOBAL_RWLOCK
- PRBool globalwrite = PR_FALSE;
-#endif
- PORT_Assert(crlcache.lock);
- if (!crlcache.lock)
- {
- /* CRL cache is not initialized */
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
-#ifdef GLOBAL_RWLOCK
- NSSRWLock_LockRead(crlcache.lock);
-#else
- PR_Lock(crlcache.lock);
-#endif
- rv = CRLCache_GetIssuerCache(&crlcache, subject, &issuercache);
- if (SECSuccess != rv)
- {
-#ifdef GLOBAL_RWLOCK
- NSSRWLock_UnlockRead(crlcache.lock);
-#else
- PR_Unlock(crlcache.lock);
-#endif
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- if (!issuercache)
- {
- /* there is no cache for this issuer yet. This means this is the
- first time we look up a cert from that issuer, and we need to
- create the cache. */
-
- rv = IssuerCache_Create(&issuercache, issuer, subject, dp);
- if (SECSuccess == rv && !issuercache)
- {
- PORT_Assert(issuercache);
- rv = SECFailure;
- }
-
- if (SECSuccess == rv)
- {
- /* This is the first time we look up a cert of this issuer.
- Create the DPCache for this DP . */
- rv = IssuerCache_AddDP(issuercache, issuer, subject, dp, dpcache);
- }
-
- if (SECSuccess == rv)
- {
- /* lock the DPCache for write to ensure the update happens in this
- thread */
- *writeLocked = PR_TRUE;
-#ifdef DPC_RWLOCK
- NSSRWLock_LockWrite((*dpcache)->lock);
-#else
- PR_Lock((*dpcache)->lock);
-#endif
- }
-
- if (SECSuccess == rv)
- {
- /* now add the new issuer cache to the global hash table of
- issuers */
-#ifdef GLOBAL_RWLOCK
- CRLIssuerCache* existing = NULL;
- NSSRWLock_UnlockRead(crlcache.lock);
- /* when using a r/w lock for the global cache, check if the issuer
- already exists before adding to the hash table */
- NSSRWLock_LockWrite(crlcache.lock);
- globalwrite = PR_TRUE;
- rv = CRLCache_GetIssuerCache(&crlcache, subject, &existing);
- if (!existing)
- {
-#endif
- rv = CRLCache_AddIssuer(issuercache);
- if (SECSuccess != rv)
- {
- /* failure */
- rv = SECFailure;
- }
-#ifdef GLOBAL_RWLOCK
- }
- else
- {
- /* somebody else updated before we did */
- IssuerCache_Destroy(issuercache); /* destroy the new object */
- issuercache = existing; /* use the existing one */
- *dpcache = IssuerCache_GetDPCache(issuercache, dp);
- }
-#endif
- }
-
- /* now unlock the global cache. We only want to lock the issuer hash
- table addition. Holding it longer would hurt scalability */
-#ifdef GLOBAL_RWLOCK
- if (PR_TRUE == globalwrite)
- {
- NSSRWLock_UnlockWrite(crlcache.lock);
- globalwrite = PR_FALSE;
- }
- else
- {
- NSSRWLock_UnlockRead(crlcache.lock);
- }
-#else
- PR_Unlock(crlcache.lock);
-#endif
-
- /* if there was a failure adding an issuer cache object, destroy it */
- if (SECSuccess != rv && issuercache)
- {
- if (PR_TRUE == *writeLocked)
- {
-#ifdef DPC_RWLOCK
- NSSRWLock_UnlockWrite((*dpcache)->lock);
-#else
- PR_Unlock((*dpcache)->lock);
-#endif
- }
- IssuerCache_Destroy(issuercache);
- issuercache = NULL;
- }
-
- if (SECSuccess != rv)
- {
- return SECFailure;
- }
- } else
- {
-#ifdef GLOBAL_RWLOCK
- NSSRWLock_UnlockRead(crlcache.lock);
-#else
- PR_Unlock(crlcache.lock);
-#endif
- *dpcache = IssuerCache_GetDPCache(issuercache, dp);
- }
- /* we now have a DPCache that we can use for lookups */
- /* lock it for read, unless we already locked for write */
- if (PR_FALSE == *writeLocked)
- {
-#ifdef DPC_RWLOCK
- NSSRWLock_LockRead((*dpcache)->lock);
-#else
- PR_Lock((*dpcache)->lock);
-#endif
- }
-
- if (SECSuccess == rv)
- {
- /* currently there is always one and only one DPCache per issuer */
- PORT_Assert(*dpcache);
- if (*dpcache)
- {
- /* make sure the DP cache is up to date before using it */
- rv = DPCache_GetUpToDate(*dpcache, issuer, PR_FALSE == *writeLocked,
- t, wincx);
- }
- else
- {
- rv = SECFailure;
- }
- }
- return rv;
-}
-
-/* unlock access to the DPCache */
-static void ReleaseDPCache(CRLDPCache* dpcache, PRBool writeLocked)
-{
- if (!dpcache)
- {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return;
- }
-#ifdef DPC_RWLOCK
- if (PR_TRUE == writeLocked)
- {
- NSSRWLock_UnlockWrite(dpcache->lock);
- }
- else
- {
- NSSRWLock_UnlockRead(dpcache->lock);
- }
-#else
- PR_Unlock(dpcache->lock);
-#endif
-}
-
-/* check CRL revocation status of given certificate and issuer */
-SECStatus
-CERT_CheckCRL(CERTCertificate* cert, CERTCertificate* issuer, SECItem* dp,
- int64 t, void* wincx)
-{
- PRBool lockedwrite = PR_FALSE;
- SECStatus rv = SECSuccess;
- CRLDPCache* dpcache = NULL;
- if (!cert || !issuer)
- {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
-
- if (SECSuccess != CERT_CheckCertValidTimes(issuer, t, PR_FALSE))
- {
- /* we won't be able to check the CRL's signature if the issuer cert
- is expired as of the time we are verifying. This may cause a valid
- CRL to be cached as bad. short-circuit to avoid this case. */
- PORT_SetError(SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE);
- return SECFailure;
- }
-
- rv = AcquireDPCache(issuer, &issuer->derSubject, dp, t, wincx, &dpcache,
- &lockedwrite);
-
- if (SECSuccess == rv)
- {
- /* now look up the certificate SN in the DP cache's CRL */
- CERTCrlEntry* entry = NULL;
- rv = DPCache_Lookup(dpcache, &cert->serialNumber, &entry);
- if (SECSuccess == rv && entry)
- {
- /* check the time if we have one */
- if (entry->revocationDate.data && entry->revocationDate.len)
- {
- int64 revocationDate = 0;
- if (SECSuccess == DER_DecodeTimeChoice(&revocationDate,
- &entry->revocationDate))
- {
- /* we got a good revocation date, only consider the
- certificate revoked if the time we are inquiring about
- is past the revocation date */
- if (t>=revocationDate)
- {
- rv = SECFailure;
- }
- } else {
- /* invalid revocation date, consider the certificate
- permanently revoked */
- rv = SECFailure;
- }
- } else {
- /* no revocation date, certificate is permanently revoked */
- rv = SECFailure;
- }
- if (SECFailure == rv)
- {
- PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
- }
- }
- }
-
- ReleaseDPCache(dpcache, lockedwrite);
- return rv;
-}
-
-/* retrieve full CRL object that best matches the cache status */
-CERTSignedCrl *
-SEC_FindCrlByName(CERTCertDBHandle *handle, SECItem *crlKey, int type)
-{
- CERTSignedCrl* acrl = NULL;
- CRLDPCache* dpcache = NULL;
- SECStatus rv = SECSuccess;
- PRBool writeLocked = PR_FALSE;
-
- if (!crlKey)
- {
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- return NULL;
- }
-
- rv = AcquireDPCache(NULL, crlKey, NULL, 0, NULL, &dpcache, &writeLocked);
- if (SECSuccess == rv)
- {
- acrl = GetBestCRL(dpcache, PR_TRUE); /* decode entries, because
- SEC_FindCrlByName always returned fully decoded CRLs in the past */
- ReleaseDPCache(dpcache, writeLocked);
- }
- return acrl;
-}
-
-/* invalidate the CRL cache for a given issuer, which forces a refetch of
- CRL objects from PKCS#11 tokens */
-void CERT_CRLCacheRefreshIssuer(CERTCertDBHandle* dbhandle, SECItem* crlKey)
-{
- CRLDPCache* cache = NULL;
- SECStatus rv = SECSuccess;
- PRBool writeLocked = PR_FALSE;
- PRBool readlocked;
-
- (void) dbhandle; /* silence compiler warnings */
-
- /* XCRL we will need to refresh all the DPs of the issuer in the future,
- not just the default one */
- rv = AcquireDPCache(NULL, crlKey, NULL, 0, NULL, &cache, &writeLocked);
- if (SECSuccess != rv)
- {
- return;
- }
- /* we need to invalidate the DPCache here */
- readlocked = (writeLocked == PR_TRUE? PR_FALSE : PR_TRUE);
- DPCache_LockWrite();
- cache->refresh = PR_TRUE;
- DPCache_UnlockWrite();
- ReleaseDPCache(cache, writeLocked);
- return;
-}
-
-/* add the specified RAM CRL object to the cache */
-SECStatus CERT_CacheCRL(CERTCertDBHandle* dbhandle, SECItem* newdercrl)
-{
- CRLDPCache* cache = NULL;
- SECStatus rv = SECSuccess;
- PRBool writeLocked = PR_FALSE;
- PRBool readlocked;
- CachedCrl* returned = NULL;
- PRBool added = PR_FALSE;
- CERTSignedCrl* newcrl = NULL;
- int realerror = 0;
-
- if (!dbhandle || !newdercrl)
- {
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- return SECFailure;
- }
-
- /* first decode the DER CRL to make sure it's OK */
- newcrl = CERT_DecodeDERCrlWithFlags(NULL, newdercrl, SEC_CRL_TYPE,
- CRL_DECODE_DONT_COPY_DER |
- CRL_DECODE_SKIP_ENTRIES);
-
- if (!newcrl)
- {
- return SECFailure;
- }
-
- rv = AcquireDPCache(NULL,
- &newcrl->crl.derName,
- NULL, 0, NULL, &cache, &writeLocked);
- if (SECSuccess == rv)
- {
- readlocked = (writeLocked == PR_TRUE? PR_FALSE : PR_TRUE);
-
- rv = CachedCrl_Create(&returned, newcrl, CRL_OriginExplicit);
- if (SECSuccess == rv && returned)
- {
- DPCache_LockWrite();
- rv = DPCache_AddCRL(cache, returned, &added);
- if (PR_TRUE != added)
- {
- realerror = PORT_GetError();
- CachedCrl_Destroy(returned);
- returned = NULL;
- }
- DPCache_UnlockWrite();
- }
-
- ReleaseDPCache(cache, writeLocked);
-
- if (!added)
- {
- rv = SECFailure;
- }
- }
- SEC_DestroyCrl(newcrl); /* free the CRL. Either it got added to the cache
- and the refcount got bumped, or not, and thus we need to free its
- RAM */
- if (realerror)
- {
- PORT_SetError(realerror);
- }
- return rv;
-}
-
-/* remove the specified RAM CRL object from the cache */
-SECStatus CERT_UncacheCRL(CERTCertDBHandle* dbhandle, SECItem* olddercrl)
-{
- CRLDPCache* cache = NULL;
- SECStatus rv = SECSuccess;
- PRBool writeLocked = PR_FALSE;
- PRBool readlocked;
- PRBool removed = PR_FALSE;
- PRUint32 i;
- CERTSignedCrl* oldcrl = NULL;
-
- if (!dbhandle || !olddercrl)
- {
- PORT_SetError(SEC_ERROR_INVALID_ARGS);
- return SECFailure;
- }
-
- /* first decode the DER CRL to make sure it's OK */
- oldcrl = CERT_DecodeDERCrlWithFlags(NULL, olddercrl, SEC_CRL_TYPE,
- CRL_DECODE_DONT_COPY_DER |
- CRL_DECODE_SKIP_ENTRIES);
-
- if (!oldcrl)
- {
- /* if this DER CRL can't decode, it can't be in the cache */
- return SECFailure;
- }
-
- rv = AcquireDPCache(NULL,
- &oldcrl->crl.derName,
- NULL, 0, NULL, &cache, &writeLocked);
- if (SECSuccess == rv)
- {
- CachedCrl* returned = NULL;
-
- readlocked = (writeLocked == PR_TRUE? PR_FALSE : PR_TRUE);
-
- rv = CachedCrl_Create(&returned, oldcrl, CRL_OriginExplicit);
- if (SECSuccess == rv && returned)
- {
- DPCache_LockWrite();
- for (i=0;i<cache->ncrls;i++)
- {
- PRBool dupe = PR_FALSE, updated = PR_FALSE;
- rv = CachedCrl_Compare(returned, cache->crls[i],
- &dupe, &updated);
- if (SECSuccess != rv)
- {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- break;
- }
- if (PR_TRUE == dupe)
- {
- DPCache_RemoveCRL(cache, i); /* got a match */
- cache->mustchoose = PR_TRUE;
- removed = PR_TRUE;
- break;
- }
- }
-
- DPCache_UnlockWrite();
- }
-
- ReleaseDPCache(cache, writeLocked);
-
- if (PR_TRUE != removed)
- {
- rv = SECFailure;
- }
- }
- SEC_DestroyCrl(oldcrl); /* need to do this because object is refcounted */
- if (PR_TRUE != removed)
- {
- PORT_SetError(SEC_ERROR_CRL_NOT_FOUND);
- }
- return rv;
-}
-
-static SECStatus CachedCrl_Create(CachedCrl** returned, CERTSignedCrl* crl,
- CRLOrigin origin)
-{
- CachedCrl* newcrl = NULL;
- if (!returned)
- {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- newcrl = PORT_ZAlloc(sizeof(CachedCrl));
- if (!newcrl)
- {
- return SECFailure;
- }
- newcrl->crl = SEC_DupCrl(crl);
- newcrl->origin = origin;
- *returned = newcrl;
- return SECSuccess;
-}
-
-/* empty the cache content */
-static SECStatus CachedCrl_Depopulate(CachedCrl* crl)
-{
- if (!crl)
- {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- /* destroy the hash table */
- if (crl->entries)
- {
- PL_HashTableDestroy(crl->entries);
- crl->entries = NULL;
- }
-
- /* free the pre buffer */
- if (crl->prebuffer)
- {
- PreAllocator_Destroy(crl->prebuffer);
- crl->prebuffer = NULL;
- }
- return SECSuccess;
-}
-
-static SECStatus CachedCrl_Destroy(CachedCrl* crl)
-{
- if (!crl)
- {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- CachedCrl_Depopulate(crl);
- SEC_DestroyCrl(crl->crl);
- PORT_Free(crl);
- return SECSuccess;
-}
-
-/* create hash table of CRL entries */
-static SECStatus CachedCrl_Populate(CachedCrl* crlobject)
-{
- SECStatus rv = SECFailure;
- CERTCrlEntry** crlEntry = NULL;
- PRUint32 numEntries = 0;
-
- if (!crlobject)
- {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
- /* complete the entry decoding . XXX thread-safety of CRL object */
- rv = CERT_CompleteCRLDecodeEntries(crlobject->crl);
- if (SECSuccess != rv)
- {
- return SECFailure;
- }
-
- if (crlobject->entries && crlobject->prebuffer)
- {
- /* cache is already built */
- return SECSuccess;
- }
-
- /* build the hash table from the full CRL */
- /* count CRL entries so we can pre-allocate space for hash table entries */
- for (crlEntry = crlobject->crl->crl.entries; crlEntry && *crlEntry;
- crlEntry++)
- {
- numEntries++;
- }
- crlobject->prebuffer = PreAllocator_Create(numEntries*sizeof(PLHashEntry));
- PORT_Assert(crlobject->prebuffer);
- if (!crlobject->prebuffer)
- {
- return SECFailure;
- }
- /* create a new hash table */
- crlobject->entries = PL_NewHashTable(0, SECITEM_Hash, SECITEM_HashCompare,
- PL_CompareValues, &preAllocOps, crlobject->prebuffer);
- PORT_Assert(crlobject->entries);
- if (!crlobject->entries)
- {
- return SECFailure;
- }
- /* add all serial numbers to the hash table */
- for (crlEntry = crlobject->crl->crl.entries; crlEntry && *crlEntry;
- crlEntry++)
- {
- PL_HashTableAdd(crlobject->entries, &(*crlEntry)->serialNumber,
- *crlEntry);
- }
-
- return SECSuccess;
-}
-
-/* returns true if there are CRLs from PKCS#11 slots */
-static PRBool DPCache_HasTokenCRLs(CRLDPCache* cache)
-{
- PRBool answer = PR_FALSE;
- PRUint32 i;
- for (i=0;i<cache->ncrls;i++)
- {
- if (cache->crls[i] && (CRL_OriginToken == cache->crls[i]->origin) )
- {
- answer = PR_TRUE;
- break;
- }
- }
- return answer;
-}
-
-/* are these CRLs the same, as far as the cache is concerned ? */
-/* are these CRLs the same token object but with different DER ?
- This can happen if the DER CRL got updated in the token, but the PKCS#11
- object ID did not change. NSS softoken has the unfortunate property to
- never change the object ID for CRL objects. */
-static SECStatus CachedCrl_Compare(CachedCrl* a, CachedCrl* b, PRBool* isDupe,
- PRBool* isUpdated)
-{
- PORT_Assert(a);
- PORT_Assert(b);
- PORT_Assert(isDupe);
- PORT_Assert(isUpdated);
- if (!a || !b || !isDupe || !isUpdated || !a->crl || !b->crl)
- {
- PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
- return SECFailure;
- }
-
- *isDupe = *isUpdated = PR_FALSE;
-
- if (a == b)
- {
- /* dupe */
- *isDupe = PR_TRUE;
- *isUpdated = PR_FALSE;
- return SECSuccess;
- }
- if (b->origin != a->origin)
- {
- /* CRLs of different origins are not considered dupes,
- and can't be updated either */
- return SECSuccess;
- }
- if (CRL_OriginToken == b->origin)
- {
- /* for token CRLs, slot and PKCS#11 object handle must match for CRL
- to truly be a dupe */
- if ( (b->crl->slot == a->crl->slot) &&
- (b->crl->pkcs11ID == a->crl->pkcs11ID) )
- {
- /* ASN.1 DER needs to match for dupe check */
- /* could optimize by just checking a few fields like thisUpdate */
- if ( SECEqual == SECITEM_CompareItem(b->crl->derCrl,
- a->crl->derCrl) )
- {
- *isDupe = PR_TRUE;
- }
- else
- {
- *isUpdated = PR_TRUE;
- }
- }
- return SECSuccess;
- }
- if (CRL_OriginExplicit == b->origin)
- {
- /* We need to make sure this is the same object that the user provided
- to CERT_CacheCRL previously. That API takes a SECItem*, thus, we
- just do a pointer comparison here.
- */
- if (b->crl->derCrl == a->crl->derCrl)
- {
- *isDupe = PR_TRUE;
- }
- }
- return SECSuccess;
-}
-
-
-