diff nss/lib/certhigh/ocsp.c @ 0:1e5118fa0cb1

This is NSS with a Cmake Buildsyste To compile a static NSS library for Windows we've used the Chromium-NSS fork and added a Cmake buildsystem to compile it statically for Windows. See README.chromium for chromium changes and README.trustbridge for our modifications.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 28 Jul 2014 10:47:06 +0200
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nss/lib/certhigh/ocsp.c	Mon Jul 28 10:47:06 2014 +0200
@@ -0,0 +1,6182 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Implementation of OCSP services, for both client and server.
+ * (XXX, really, mostly just for client right now, but intended to do both.)
+ */
+
+#include "prerror.h"
+#include "prprf.h"
+#include "plarena.h"
+#include "prnetdb.h"
+
+#include "seccomon.h"
+#include "secitem.h"
+#include "secoidt.h"
+#include "secasn1.h"
+#include "secder.h"
+#include "cert.h"
+#include "certi.h"
+#include "xconst.h"
+#include "secerr.h"
+#include "secoid.h"
+#include "hasht.h"
+#include "sechash.h"
+#include "secasn1.h"
+#include "plbase64.h"
+#include "keyhi.h"
+#include "cryptohi.h"
+#include "ocsp.h"
+#include "ocspti.h"
+#include "ocspi.h"
+#include "genname.h"
+#include "certxutl.h"
+#include "pk11func.h"	/* for PK11_HashBuf */
+#include <stdarg.h>
+#include <plhash.h>
+
+#define DEFAULT_OCSP_CACHE_SIZE 1000
+#define DEFAULT_MINIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT 1*60*60L
+#define DEFAULT_MAXIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT 24*60*60L
+#define DEFAULT_OSCP_TIMEOUT_SECONDS 60
+#define MICROSECONDS_PER_SECOND 1000000L
+
+typedef struct OCSPCacheItemStr OCSPCacheItem;
+typedef struct OCSPCacheDataStr OCSPCacheData;
+
+struct OCSPCacheItemStr {
+    /* LRU linking */
+    OCSPCacheItem *moreRecent;
+    OCSPCacheItem *lessRecent;
+
+    /* key */
+    CERTOCSPCertID *certID;
+    /* CertID's arena also used to allocate "this" cache item */
+
+    /* cache control information */
+    PRTime nextFetchAttemptTime;
+
+    /* Cached contents. Use a separate arena, because lifetime is different */
+    PLArenaPool *certStatusArena; /* NULL means: no cert status cached */
+    ocspCertStatus certStatus;
+
+    /* This may contain an error code when no OCSP response is available. */
+    SECErrorCodes missingResponseError;
+
+    PRPackedBool haveThisUpdate;
+    PRPackedBool haveNextUpdate;
+    PRTime thisUpdate;
+    PRTime nextUpdate;
+};
+
+struct OCSPCacheDataStr {
+    PLHashTable *entries;
+    PRUint32 numberOfEntries;
+    OCSPCacheItem *MRUitem; /* most recently used cache item */
+    OCSPCacheItem *LRUitem; /* least recently used cache item */
+};
+
+static struct OCSPGlobalStruct {
+    PRMonitor *monitor;
+    const SEC_HttpClientFcn *defaultHttpClientFcn;
+    PRInt32 maxCacheEntries;
+    PRUint32 minimumSecondsToNextFetchAttempt;
+    PRUint32 maximumSecondsToNextFetchAttempt;
+    PRUint32 timeoutSeconds;
+    OCSPCacheData cache;
+    SEC_OcspFailureMode ocspFailureMode;
+    CERT_StringFromCertFcn alternateOCSPAIAFcn;
+    PRBool forcePost;
+} OCSP_Global = { NULL, 
+                  NULL, 
+                  DEFAULT_OCSP_CACHE_SIZE, 
+                  DEFAULT_MINIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT,
+                  DEFAULT_MAXIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT,
+                  DEFAULT_OSCP_TIMEOUT_SECONDS,
+                  {NULL, 0, NULL, NULL},
+                  ocspMode_FailureIsVerificationFailure,
+                  NULL,
+                  PR_FALSE
+                };
+
+
+
+/* Forward declarations */
+static SECItem *
+ocsp_GetEncodedOCSPResponseFromRequest(PLArenaPool *arena, 
+                                       CERTOCSPRequest *request,
+                                       const char *location,
+				       const char *method,
+				       PRTime time,
+                                       PRBool addServiceLocator,
+                                       void *pwArg,
+                                       CERTOCSPRequest **pRequest);
+static SECStatus
+ocsp_GetOCSPStatusFromNetwork(CERTCertDBHandle *handle, 
+                              CERTOCSPCertID *certID, 
+                              CERTCertificate *cert, 
+                              PRTime time, 
+                              void *pwArg,
+                              PRBool *certIDWasConsumed,
+                              SECStatus *rv_ocsp);
+
+static SECStatus
+ocsp_GetDecodedVerifiedSingleResponseForID(CERTCertDBHandle *handle,
+					   CERTOCSPCertID *certID,
+					   CERTCertificate *cert,
+					   PRTime time,
+					   void *pwArg,
+					   const SECItem *encodedResponse,
+					   CERTOCSPResponse **pDecodedResponse,
+					   CERTOCSPSingleResponse **pSingle);
+
+static SECStatus
+ocsp_CertRevokedAfter(ocspRevokedInfo *revokedInfo, PRTime time);
+
+static CERTOCSPCertID *
+cert_DupOCSPCertID(const CERTOCSPCertID *src);
+
+#ifndef DEBUG
+#define OCSP_TRACE(msg)
+#define OCSP_TRACE_TIME(msg, time)
+#define OCSP_TRACE_CERT(cert)
+#define OCSP_TRACE_CERTID(certid)
+#else
+#define OCSP_TRACE(msg) ocsp_Trace msg
+#define OCSP_TRACE_TIME(msg, time) ocsp_dumpStringWithTime(msg, time)
+#define OCSP_TRACE_CERT(cert) dumpCertificate(cert)
+#define OCSP_TRACE_CERTID(certid) dumpCertID(certid)
+
+#if defined(XP_UNIX) || defined(XP_WIN32) || defined(XP_BEOS) \
+     || defined(XP_MACOSX)
+#define NSS_HAVE_GETENV 1
+#endif
+
+static PRBool wantOcspTrace(void)
+{
+    static PRBool firstTime = PR_TRUE;
+    static PRBool wantTrace = PR_FALSE;
+
+#ifdef NSS_HAVE_GETENV
+    if (firstTime) {
+        char *ev = getenv("NSS_TRACE_OCSP");
+        if (ev && ev[0]) {
+            wantTrace = PR_TRUE;
+        }
+        firstTime = PR_FALSE;
+    }
+#endif
+    return wantTrace;
+}
+
+static void
+ocsp_Trace(const char *format, ...)
+{
+    char buf[2000];
+    va_list args;
+  
+    if (!wantOcspTrace())
+        return;
+    va_start(args, format);
+    PR_vsnprintf(buf, sizeof(buf), format, args);
+    va_end(args);
+    PR_LogPrint("%s", buf);
+}
+
+static void
+ocsp_dumpStringWithTime(const char *str, PRTime time)
+{
+    PRExplodedTime timePrintable;
+    char timestr[256];
+
+    if (!wantOcspTrace())
+        return;
+    PR_ExplodeTime(time, PR_GMTParameters, &timePrintable);
+    if (PR_FormatTime(timestr, 256, "%a %b %d %H:%M:%S %Y", &timePrintable)) {
+        ocsp_Trace("OCSP %s %s\n", str, timestr);
+    }
+}
+
+static void
+printHexString(const char *prefix, SECItem *hexval)
+{
+    unsigned int i;
+    char *hexbuf = NULL;
+
+    for (i = 0; i < hexval->len; i++) {
+        if (i != hexval->len - 1) {
+            hexbuf = PR_sprintf_append(hexbuf, "%02x:", hexval->data[i]);
+        } else {
+            hexbuf = PR_sprintf_append(hexbuf, "%02x", hexval->data[i]);
+        }
+    }
+    if (hexbuf) {
+        ocsp_Trace("%s %s\n", prefix, hexbuf);
+        PR_smprintf_free(hexbuf);
+    }
+}
+
+static void
+dumpCertificate(CERTCertificate *cert)
+{
+    if (!wantOcspTrace())
+        return;
+
+    ocsp_Trace("OCSP ----------------\n");
+    ocsp_Trace("OCSP ## SUBJECT:  %s\n", cert->subjectName);
+    {
+        PRTime timeBefore, timeAfter;
+        PRExplodedTime beforePrintable, afterPrintable;
+        char beforestr[256], afterstr[256];
+        PRStatus rv1, rv2;
+        DER_DecodeTimeChoice(&timeBefore, &cert->validity.notBefore);
+        DER_DecodeTimeChoice(&timeAfter, &cert->validity.notAfter);
+        PR_ExplodeTime(timeBefore, PR_GMTParameters, &beforePrintable);
+        PR_ExplodeTime(timeAfter, PR_GMTParameters, &afterPrintable);
+        rv1 = PR_FormatTime(beforestr, 256, "%a %b %d %H:%M:%S %Y", 
+                      &beforePrintable);
+        rv2 = PR_FormatTime(afterstr, 256, "%a %b %d %H:%M:%S %Y", 
+                      &afterPrintable);
+        ocsp_Trace("OCSP ## VALIDITY:  %s to %s\n", rv1 ? beforestr : "",
+                   rv2 ? afterstr : "");
+    }
+    ocsp_Trace("OCSP ## ISSUER:  %s\n", cert->issuerName);
+    printHexString("OCSP ## SERIAL NUMBER:", &cert->serialNumber);
+}
+
+static void
+dumpCertID(CERTOCSPCertID *certID)
+{
+    if (!wantOcspTrace())
+        return;
+
+    printHexString("OCSP certID issuer", &certID->issuerNameHash);
+    printHexString("OCSP certID serial", &certID->serialNumber);
+}
+#endif
+
+SECStatus
+SEC_RegisterDefaultHttpClient(const SEC_HttpClientFcn *fcnTable)
+{
+    if (!OCSP_Global.monitor) {
+      PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+      return SECFailure;
+    }
+    
+    PR_EnterMonitor(OCSP_Global.monitor);
+    OCSP_Global.defaultHttpClientFcn = fcnTable;
+    PR_ExitMonitor(OCSP_Global.monitor);
+    
+    return SECSuccess;
+}
+
+SECStatus
+CERT_RegisterAlternateOCSPAIAInfoCallBack(
+			CERT_StringFromCertFcn   newCallback,
+			CERT_StringFromCertFcn * oldCallback)
+{
+    CERT_StringFromCertFcn old;
+
+    if (!OCSP_Global.monitor) {
+      PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+      return SECFailure;
+    }
+
+    PR_EnterMonitor(OCSP_Global.monitor);
+    old = OCSP_Global.alternateOCSPAIAFcn;
+    OCSP_Global.alternateOCSPAIAFcn = newCallback;
+    PR_ExitMonitor(OCSP_Global.monitor);
+    if (oldCallback)
+    	*oldCallback = old;
+    return SECSuccess;
+}
+
+static PLHashNumber PR_CALLBACK
+ocsp_CacheKeyHashFunction(const void *key)
+{
+    CERTOCSPCertID *cid = (CERTOCSPCertID *)key;
+    PLHashNumber hash = 0;
+    unsigned int i;
+    unsigned char *walk;
+  
+    /* a very simple hash calculation for the initial coding phase */
+    walk = (unsigned char*)cid->issuerNameHash.data;
+    for (i=0; i < cid->issuerNameHash.len; ++i, ++walk) {
+        hash += *walk;
+    }
+    walk = (unsigned char*)cid->issuerKeyHash.data;
+    for (i=0; i < cid->issuerKeyHash.len; ++i, ++walk) {
+        hash += *walk;
+    }
+    walk = (unsigned char*)cid->serialNumber.data;
+    for (i=0; i < cid->serialNumber.len; ++i, ++walk) {
+        hash += *walk;
+    }
+    return hash;
+}
+
+static PRIntn PR_CALLBACK
+ocsp_CacheKeyCompareFunction(const void *v1, const void *v2)
+{
+    CERTOCSPCertID *cid1 = (CERTOCSPCertID *)v1;
+    CERTOCSPCertID *cid2 = (CERTOCSPCertID *)v2;
+  
+    return (SECEqual == SECITEM_CompareItem(&cid1->issuerNameHash, 
+                                            &cid2->issuerNameHash)
+            && SECEqual == SECITEM_CompareItem(&cid1->issuerKeyHash, 
+                                               &cid2->issuerKeyHash)
+            && SECEqual == SECITEM_CompareItem(&cid1->serialNumber, 
+                                               &cid2->serialNumber));
+}
+
+static SECStatus
+ocsp_CopyRevokedInfo(PLArenaPool *arena, ocspCertStatus *dest,
+                     ocspRevokedInfo *src)
+{
+    SECStatus rv = SECFailure;
+    void *mark;
+  
+    mark = PORT_ArenaMark(arena);
+  
+    dest->certStatusInfo.revokedInfo = 
+        (ocspRevokedInfo *) PORT_ArenaZAlloc(arena, sizeof(ocspRevokedInfo));
+    if (!dest->certStatusInfo.revokedInfo) {
+        goto loser;
+    }
+  
+    rv = SECITEM_CopyItem(arena, 
+                          &dest->certStatusInfo.revokedInfo->revocationTime, 
+                          &src->revocationTime);
+    if (rv != SECSuccess) {
+        goto loser;
+    }
+  
+    if (src->revocationReason) {
+        dest->certStatusInfo.revokedInfo->revocationReason = 
+            SECITEM_ArenaDupItem(arena, src->revocationReason);
+        if (!dest->certStatusInfo.revokedInfo->revocationReason) {
+            goto loser;
+        }
+    }  else {
+        dest->certStatusInfo.revokedInfo->revocationReason = NULL;
+    }
+  
+    PORT_ArenaUnmark(arena, mark);
+    return SECSuccess;
+
+loser:
+    PORT_ArenaRelease(arena, mark);
+    return SECFailure;
+}
+
+static SECStatus
+ocsp_CopyCertStatus(PLArenaPool *arena, ocspCertStatus *dest,
+                    ocspCertStatus*src)
+{
+    SECStatus rv = SECFailure;
+    dest->certStatusType = src->certStatusType;
+  
+    switch (src->certStatusType) {
+    case ocspCertStatus_good:
+        dest->certStatusInfo.goodInfo = 
+            SECITEM_ArenaDupItem(arena, src->certStatusInfo.goodInfo);
+        if (dest->certStatusInfo.goodInfo != NULL) {
+            rv = SECSuccess;
+        }
+        break;
+    case ocspCertStatus_revoked:
+        rv = ocsp_CopyRevokedInfo(arena, dest, 
+                                  src->certStatusInfo.revokedInfo);
+        break;
+    case ocspCertStatus_unknown:
+        dest->certStatusInfo.unknownInfo = 
+            SECITEM_ArenaDupItem(arena, src->certStatusInfo.unknownInfo);
+        if (dest->certStatusInfo.unknownInfo != NULL) {
+            rv = SECSuccess;
+        }
+        break;
+    case ocspCertStatus_other:
+    default:
+        PORT_Assert(src->certStatusType == ocspCertStatus_other);
+        dest->certStatusInfo.otherInfo = 
+            SECITEM_ArenaDupItem(arena, src->certStatusInfo.otherInfo);
+        if (dest->certStatusInfo.otherInfo != NULL) {
+            rv = SECSuccess;
+        }
+        break;
+    }
+    return rv;
+}
+
+static void
+ocsp_AddCacheItemToLinkedList(OCSPCacheData *cache, OCSPCacheItem *new_most_recent)
+{
+    PR_EnterMonitor(OCSP_Global.monitor);
+
+    if (!cache->LRUitem) {
+        cache->LRUitem = new_most_recent;
+    }
+    new_most_recent->lessRecent = cache->MRUitem;
+    new_most_recent->moreRecent = NULL;
+
+    if (cache->MRUitem) {
+        cache->MRUitem->moreRecent = new_most_recent;
+    }
+    cache->MRUitem = new_most_recent;
+
+    PR_ExitMonitor(OCSP_Global.monitor);
+}
+
+static void
+ocsp_RemoveCacheItemFromLinkedList(OCSPCacheData *cache, OCSPCacheItem *item)
+{
+    PR_EnterMonitor(OCSP_Global.monitor);
+
+    if (!item->lessRecent && !item->moreRecent) {
+        /*
+         * Fail gracefully on attempts to remove an item from the list,
+         * which is currently not part of the list.
+         * But check for the edge case it is the single entry in the list.
+         */
+        if (item == cache->LRUitem &&
+            item == cache->MRUitem) {
+            /* remove the single entry */
+            PORT_Assert(cache->numberOfEntries == 1);
+            PORT_Assert(item->moreRecent == NULL);
+            cache->MRUitem = NULL;
+            cache->LRUitem = NULL;
+        }
+        PR_ExitMonitor(OCSP_Global.monitor);
+        return;
+    }
+
+    PORT_Assert(cache->numberOfEntries > 1);
+  
+    if (item == cache->LRUitem) {
+        PORT_Assert(item != cache->MRUitem);
+        PORT_Assert(item->lessRecent == NULL);
+        PORT_Assert(item->moreRecent != NULL);
+        PORT_Assert(item->moreRecent->lessRecent == item);
+        cache->LRUitem = item->moreRecent;
+        cache->LRUitem->lessRecent = NULL;
+    }
+    else if (item == cache->MRUitem) {
+        PORT_Assert(item->moreRecent == NULL);
+        PORT_Assert(item->lessRecent != NULL);
+        PORT_Assert(item->lessRecent->moreRecent == item);
+        cache->MRUitem = item->lessRecent;
+        cache->MRUitem->moreRecent = NULL;
+    } else {
+        /* remove an entry in the middle of the list */
+        PORT_Assert(item->moreRecent != NULL);
+        PORT_Assert(item->lessRecent != NULL);
+        PORT_Assert(item->lessRecent->moreRecent == item);
+        PORT_Assert(item->moreRecent->lessRecent == item);
+        item->moreRecent->lessRecent = item->lessRecent;
+        item->lessRecent->moreRecent = item->moreRecent;
+    }
+
+    item->lessRecent = NULL;
+    item->moreRecent = NULL;
+
+    PR_ExitMonitor(OCSP_Global.monitor);
+}
+
+static void
+ocsp_MakeCacheEntryMostRecent(OCSPCacheData *cache, OCSPCacheItem *new_most_recent)
+{
+    OCSP_TRACE(("OCSP ocsp_MakeCacheEntryMostRecent THREADID %p\n", 
+                PR_GetCurrentThread()));
+    PR_EnterMonitor(OCSP_Global.monitor);
+    if (cache->MRUitem == new_most_recent) {
+        OCSP_TRACE(("OCSP ocsp_MakeCacheEntryMostRecent ALREADY MOST\n"));
+        PR_ExitMonitor(OCSP_Global.monitor);
+        return;
+    }
+    OCSP_TRACE(("OCSP ocsp_MakeCacheEntryMostRecent NEW entry\n"));
+    ocsp_RemoveCacheItemFromLinkedList(cache, new_most_recent);
+    ocsp_AddCacheItemToLinkedList(cache, new_most_recent);
+    PR_ExitMonitor(OCSP_Global.monitor);
+}
+
+static PRBool
+ocsp_IsCacheDisabled(void)
+{
+    /* 
+     * maxCacheEntries == 0 means unlimited cache entries
+     * maxCacheEntries  < 0 means cache is disabled
+     */
+    PRBool retval;
+    PR_EnterMonitor(OCSP_Global.monitor);
+    retval = (OCSP_Global.maxCacheEntries < 0);
+    PR_ExitMonitor(OCSP_Global.monitor);
+    return retval;
+}
+
+static OCSPCacheItem *
+ocsp_FindCacheEntry(OCSPCacheData *cache, CERTOCSPCertID *certID)
+{
+    OCSPCacheItem *found_ocsp_item = NULL;
+    OCSP_TRACE(("OCSP ocsp_FindCacheEntry\n"));
+    OCSP_TRACE_CERTID(certID);
+    PR_EnterMonitor(OCSP_Global.monitor);
+    if (ocsp_IsCacheDisabled())
+        goto loser;
+  
+    found_ocsp_item = (OCSPCacheItem *)PL_HashTableLookup(
+                          cache->entries, certID);
+    if (!found_ocsp_item)
+        goto loser;
+  
+    OCSP_TRACE(("OCSP ocsp_FindCacheEntry FOUND!\n"));
+    ocsp_MakeCacheEntryMostRecent(cache, found_ocsp_item);
+
+loser:
+    PR_ExitMonitor(OCSP_Global.monitor);
+    return found_ocsp_item;
+}
+
+static void
+ocsp_FreeCacheItem(OCSPCacheItem *item)
+{
+    OCSP_TRACE(("OCSP ocsp_FreeCacheItem\n"));
+    if (item->certStatusArena) {
+        PORT_FreeArena(item->certStatusArena, PR_FALSE);
+    }
+    if (item->certID->poolp) {
+        /* freeing this poolp arena will also free item */
+        PORT_FreeArena(item->certID->poolp, PR_FALSE);
+    }
+}
+
+static void
+ocsp_RemoveCacheItem(OCSPCacheData *cache, OCSPCacheItem *item)
+{
+    /* The item we're removing could be either the least recently used item,
+     * or it could be an item that couldn't get updated with newer status info
+     * because of an allocation failure, or it could get removed because we're 
+     * cleaning up.
+     */
+    PRBool couldRemoveFromHashTable;
+    OCSP_TRACE(("OCSP ocsp_RemoveCacheItem, THREADID %p\n", PR_GetCurrentThread()));
+    PR_EnterMonitor(OCSP_Global.monitor);
+
+    ocsp_RemoveCacheItemFromLinkedList(cache, item);
+    couldRemoveFromHashTable = PL_HashTableRemove(cache->entries, 
+                                                  item->certID);
+    PORT_Assert(couldRemoveFromHashTable);
+    --cache->numberOfEntries;
+    ocsp_FreeCacheItem(item);
+    PR_ExitMonitor(OCSP_Global.monitor);
+}
+
+static void
+ocsp_CheckCacheSize(OCSPCacheData *cache)
+{
+    OCSP_TRACE(("OCSP ocsp_CheckCacheSize\n"));
+    PR_EnterMonitor(OCSP_Global.monitor);
+    if (OCSP_Global.maxCacheEntries > 0) {
+        /* Cache is not disabled. Number of cache entries is limited.
+         * The monitor ensures that maxCacheEntries remains positive.
+         */
+        while (cache->numberOfEntries > 
+                     (PRUint32)OCSP_Global.maxCacheEntries) {
+            ocsp_RemoveCacheItem(cache, cache->LRUitem);
+        }
+    }
+    PR_ExitMonitor(OCSP_Global.monitor);
+}
+
+SECStatus
+CERT_ClearOCSPCache(void)
+{
+    OCSP_TRACE(("OCSP CERT_ClearOCSPCache\n"));
+    PR_EnterMonitor(OCSP_Global.monitor);
+    while (OCSP_Global.cache.numberOfEntries > 0) {
+        ocsp_RemoveCacheItem(&OCSP_Global.cache, 
+                             OCSP_Global.cache.LRUitem);
+    }
+    PR_ExitMonitor(OCSP_Global.monitor);
+    return SECSuccess;
+}
+
+static SECStatus
+ocsp_CreateCacheItemAndConsumeCertID(OCSPCacheData *cache,
+                                     CERTOCSPCertID *certID, 
+                                     OCSPCacheItem **pCacheItem)
+{
+    PLArenaPool *arena;
+    void *mark;
+    PLHashEntry *new_hash_entry;
+    OCSPCacheItem *item;
+  
+    PORT_Assert(pCacheItem != NULL);
+    *pCacheItem = NULL;
+
+    PR_EnterMonitor(OCSP_Global.monitor);
+    arena = certID->poolp;
+    mark = PORT_ArenaMark(arena);
+  
+    /* ZAlloc will init all Bools to False and all Pointers to NULL
+       and all error codes to zero/good. */
+    item = (OCSPCacheItem *)PORT_ArenaZAlloc(certID->poolp, 
+                                             sizeof(OCSPCacheItem));
+    if (!item) {
+        goto loser; 
+    }
+    item->certID = certID;
+    new_hash_entry = PL_HashTableAdd(cache->entries, item->certID, 
+                                     item);
+    if (!new_hash_entry) {
+        goto loser;
+    }
+    ++cache->numberOfEntries;
+    PORT_ArenaUnmark(arena, mark);
+    ocsp_AddCacheItemToLinkedList(cache, item);
+    *pCacheItem = item;
+
+    PR_ExitMonitor(OCSP_Global.monitor);
+    return SECSuccess;
+  
+loser:
+    PORT_ArenaRelease(arena, mark);
+    PR_ExitMonitor(OCSP_Global.monitor);
+    return SECFailure;
+}
+
+static SECStatus
+ocsp_SetCacheItemResponse(OCSPCacheItem *item,
+                          const CERTOCSPSingleResponse *response)
+{
+    if (item->certStatusArena) {
+        PORT_FreeArena(item->certStatusArena, PR_FALSE);
+        item->certStatusArena = NULL;
+    }
+    item->haveThisUpdate = item->haveNextUpdate = PR_FALSE;
+    if (response) {
+        SECStatus rv;
+        item->certStatusArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+        if (item->certStatusArena == NULL) {
+            return SECFailure;
+        }
+        rv = ocsp_CopyCertStatus(item->certStatusArena, &item->certStatus, 
+                                 response->certStatus);
+        if (rv != SECSuccess) {
+            PORT_FreeArena(item->certStatusArena, PR_FALSE);
+            item->certStatusArena = NULL;
+            return rv;
+        }
+        item->missingResponseError = 0;
+        rv = DER_GeneralizedTimeToTime(&item->thisUpdate, 
+                                       &response->thisUpdate);
+        item->haveThisUpdate = (rv == SECSuccess);
+        if (response->nextUpdate) {
+            rv = DER_GeneralizedTimeToTime(&item->nextUpdate, 
+                                           response->nextUpdate);
+            item->haveNextUpdate = (rv == SECSuccess);
+        } else {
+            item->haveNextUpdate = PR_FALSE;
+        }
+    }
+    return SECSuccess;
+}
+
+static void
+ocsp_FreshenCacheItemNextFetchAttemptTime(OCSPCacheItem *cacheItem)
+{
+    PRTime now;
+    PRTime earliestAllowedNextFetchAttemptTime;
+    PRTime latestTimeWhenResponseIsConsideredFresh;
+  
+    OCSP_TRACE(("OCSP ocsp_FreshenCacheItemNextFetchAttemptTime\n"));
+
+    PR_EnterMonitor(OCSP_Global.monitor);
+  
+    now = PR_Now();
+    OCSP_TRACE_TIME("now:", now);
+  
+    if (cacheItem->haveThisUpdate) {
+        OCSP_TRACE_TIME("thisUpdate:", cacheItem->thisUpdate);
+        latestTimeWhenResponseIsConsideredFresh = cacheItem->thisUpdate +
+            OCSP_Global.maximumSecondsToNextFetchAttempt * 
+                MICROSECONDS_PER_SECOND;
+        OCSP_TRACE_TIME("latestTimeWhenResponseIsConsideredFresh:", 
+                        latestTimeWhenResponseIsConsideredFresh);
+    } else {
+        latestTimeWhenResponseIsConsideredFresh = now +
+            OCSP_Global.minimumSecondsToNextFetchAttempt *
+                MICROSECONDS_PER_SECOND;
+        OCSP_TRACE_TIME("no thisUpdate, "
+                        "latestTimeWhenResponseIsConsideredFresh:", 
+                        latestTimeWhenResponseIsConsideredFresh);
+    }
+  
+    if (cacheItem->haveNextUpdate) {
+        OCSP_TRACE_TIME("have nextUpdate:", cacheItem->nextUpdate);
+    }
+  
+    if (cacheItem->haveNextUpdate &&
+        cacheItem->nextUpdate < latestTimeWhenResponseIsConsideredFresh) {
+        latestTimeWhenResponseIsConsideredFresh = cacheItem->nextUpdate;
+        OCSP_TRACE_TIME("nextUpdate is smaller than latestFresh, setting "
+                        "latestTimeWhenResponseIsConsideredFresh:", 
+                        latestTimeWhenResponseIsConsideredFresh);
+    }
+  
+    earliestAllowedNextFetchAttemptTime = now +
+        OCSP_Global.minimumSecondsToNextFetchAttempt * 
+            MICROSECONDS_PER_SECOND;
+    OCSP_TRACE_TIME("earliestAllowedNextFetchAttemptTime:", 
+                    earliestAllowedNextFetchAttemptTime);
+  
+    if (latestTimeWhenResponseIsConsideredFresh < 
+        earliestAllowedNextFetchAttemptTime) {
+        latestTimeWhenResponseIsConsideredFresh = 
+            earliestAllowedNextFetchAttemptTime;
+        OCSP_TRACE_TIME("latest < earliest, setting latest to:", 
+                        latestTimeWhenResponseIsConsideredFresh);
+    }
+  
+    cacheItem->nextFetchAttemptTime = 
+        latestTimeWhenResponseIsConsideredFresh;
+    OCSP_TRACE_TIME("nextFetchAttemptTime", 
+        latestTimeWhenResponseIsConsideredFresh);
+
+    PR_ExitMonitor(OCSP_Global.monitor);
+}
+
+static PRBool
+ocsp_IsCacheItemFresh(OCSPCacheItem *cacheItem)
+{
+    PRTime now;
+    PRBool fresh;
+
+    now = PR_Now();
+
+    fresh = cacheItem->nextFetchAttemptTime > now;
+
+    /* Work around broken OCSP responders that return unknown responses for
+     * certificates, especially certificates that were just recently issued.
+     */
+    if (fresh && cacheItem->certStatusArena &&
+        cacheItem->certStatus.certStatusType == ocspCertStatus_unknown) {
+        fresh = PR_FALSE;
+    }
+
+    OCSP_TRACE(("OCSP ocsp_IsCacheItemFresh: %d\n", fresh));
+
+    return fresh;
+}
+
+/*
+ * Status in *certIDWasConsumed will always be correct, regardless of 
+ * return value.
+ * If the caller is unable to transfer ownership of certID,
+ * then the caller must set certIDWasConsumed to NULL,
+ * and this function will potentially duplicate the certID object.
+ */
+static SECStatus
+ocsp_CreateOrUpdateCacheEntry(OCSPCacheData *cache, 
+                              CERTOCSPCertID *certID,
+                              CERTOCSPSingleResponse *single,
+                              PRBool *certIDWasConsumed)
+{
+    SECStatus rv;
+    OCSPCacheItem *cacheItem;
+    OCSP_TRACE(("OCSP ocsp_CreateOrUpdateCacheEntry\n"));
+  
+    if (certIDWasConsumed)
+        *certIDWasConsumed = PR_FALSE;
+  
+    PR_EnterMonitor(OCSP_Global.monitor);
+    PORT_Assert(OCSP_Global.maxCacheEntries >= 0);
+  
+    cacheItem = ocsp_FindCacheEntry(cache, certID);
+
+    /* Don't replace an unknown or revoked entry with an error entry, even if
+     * the existing entry is expired. Instead, we'll continue to use the
+     * existing (possibly expired) cache entry until we receive a valid signed
+     * response to replace it.
+     */
+    if (!single && cacheItem && cacheItem->certStatusArena &&
+        (cacheItem->certStatus.certStatusType == ocspCertStatus_revoked ||
+         cacheItem->certStatus.certStatusType == ocspCertStatus_unknown)) {
+        PR_ExitMonitor(OCSP_Global.monitor);
+        return SECSuccess;
+    }
+
+    if (!cacheItem) {
+        CERTOCSPCertID *myCertID;
+        if (certIDWasConsumed) {
+            myCertID = certID;
+            *certIDWasConsumed = PR_TRUE;
+        } else {
+            myCertID = cert_DupOCSPCertID(certID);
+            if (!myCertID) {
+                PR_ExitMonitor(OCSP_Global.monitor);
+                PORT_SetError(PR_OUT_OF_MEMORY_ERROR);
+                return SECFailure;
+            }
+        }
+
+        rv = ocsp_CreateCacheItemAndConsumeCertID(cache, myCertID,
+                                                  &cacheItem);
+        if (rv != SECSuccess) {
+            PR_ExitMonitor(OCSP_Global.monitor);
+            return rv;
+        }
+    }
+    if (single) {
+        PRTime thisUpdate;
+        rv = DER_GeneralizedTimeToTime(&thisUpdate, &single->thisUpdate);
+
+        if (!cacheItem->haveThisUpdate ||
+            (rv == SECSuccess && cacheItem->thisUpdate < thisUpdate)) {
+            rv = ocsp_SetCacheItemResponse(cacheItem, single);
+            if (rv != SECSuccess) {
+                ocsp_RemoveCacheItem(cache, cacheItem);
+                PR_ExitMonitor(OCSP_Global.monitor);
+                return rv;
+            }
+        } else {
+            OCSP_TRACE(("Not caching response because the response is not "
+                        "newer than the cache"));
+        }
+    } else {
+        cacheItem->missingResponseError = PORT_GetError();
+        if (cacheItem->certStatusArena) {
+            PORT_FreeArena(cacheItem->certStatusArena, PR_FALSE);
+            cacheItem->certStatusArena = NULL;
+        }
+    }
+    ocsp_FreshenCacheItemNextFetchAttemptTime(cacheItem);
+    ocsp_CheckCacheSize(cache);
+
+    PR_ExitMonitor(OCSP_Global.monitor);
+    return SECSuccess;
+}
+
+extern SECStatus
+CERT_SetOCSPFailureMode(SEC_OcspFailureMode ocspFailureMode)
+{
+    switch (ocspFailureMode) {
+    case ocspMode_FailureIsVerificationFailure:
+    case ocspMode_FailureIsNotAVerificationFailure:
+        break;
+    default:
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+
+    PR_EnterMonitor(OCSP_Global.monitor);
+    OCSP_Global.ocspFailureMode = ocspFailureMode;
+    PR_ExitMonitor(OCSP_Global.monitor);
+    return SECSuccess;
+}
+
+SECStatus
+CERT_OCSPCacheSettings(PRInt32 maxCacheEntries,
+                       PRUint32 minimumSecondsToNextFetchAttempt,
+                       PRUint32 maximumSecondsToNextFetchAttempt)
+{
+    if (minimumSecondsToNextFetchAttempt > maximumSecondsToNextFetchAttempt
+        || maxCacheEntries < -1) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+  
+    PR_EnterMonitor(OCSP_Global.monitor);
+  
+    if (maxCacheEntries < 0) {
+        OCSP_Global.maxCacheEntries = -1; /* disable cache */
+    } else if (maxCacheEntries == 0) {
+        OCSP_Global.maxCacheEntries = 0; /* unlimited cache entries */
+    } else {
+        OCSP_Global.maxCacheEntries = maxCacheEntries;
+    }
+  
+    if (minimumSecondsToNextFetchAttempt < 
+            OCSP_Global.minimumSecondsToNextFetchAttempt
+        || maximumSecondsToNextFetchAttempt < 
+            OCSP_Global.maximumSecondsToNextFetchAttempt) {
+        /*
+         * Ensure our existing cache entries are not used longer than the 
+         * new settings allow, we're lazy and just clear the cache
+         */
+        CERT_ClearOCSPCache();
+    }
+  
+    OCSP_Global.minimumSecondsToNextFetchAttempt = 
+        minimumSecondsToNextFetchAttempt;
+    OCSP_Global.maximumSecondsToNextFetchAttempt = 
+        maximumSecondsToNextFetchAttempt;
+    ocsp_CheckCacheSize(&OCSP_Global.cache);
+  
+    PR_ExitMonitor(OCSP_Global.monitor);
+    return SECSuccess;
+}
+
+SECStatus
+CERT_SetOCSPTimeout(PRUint32 seconds)
+{
+    /* no locking, see bug 406120 */
+    OCSP_Global.timeoutSeconds = seconds;
+    return SECSuccess;
+}
+
+/* this function is called at NSS initialization time */
+SECStatus OCSP_InitGlobal(void)
+{
+    SECStatus rv = SECFailure;
+
+    if (OCSP_Global.monitor == NULL) {
+        OCSP_Global.monitor = PR_NewMonitor();
+    }
+    if (!OCSP_Global.monitor)
+        return SECFailure;
+
+    PR_EnterMonitor(OCSP_Global.monitor);
+    if (!OCSP_Global.cache.entries) {
+        OCSP_Global.cache.entries = 
+            PL_NewHashTable(0, 
+                            ocsp_CacheKeyHashFunction, 
+                            ocsp_CacheKeyCompareFunction, 
+                            PL_CompareValues, 
+                            NULL, 
+                            NULL);
+        OCSP_Global.ocspFailureMode = ocspMode_FailureIsVerificationFailure;
+        OCSP_Global.cache.numberOfEntries = 0;
+        OCSP_Global.cache.MRUitem = NULL;
+        OCSP_Global.cache.LRUitem = NULL;
+    } else {
+        /*
+         * NSS might call this function twice while attempting to init.
+         * But it's not allowed to call this again after any activity.
+         */
+        PORT_Assert(OCSP_Global.cache.numberOfEntries == 0);
+        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+    }
+    if (OCSP_Global.cache.entries)
+        rv = SECSuccess;
+    PR_ExitMonitor(OCSP_Global.monitor);
+    return rv;
+}
+
+SECStatus OCSP_ShutdownGlobal(void)
+{
+    if (!OCSP_Global.monitor)
+        return SECSuccess;
+
+    PR_EnterMonitor(OCSP_Global.monitor);
+    if (OCSP_Global.cache.entries) {
+        CERT_ClearOCSPCache();
+        PL_HashTableDestroy(OCSP_Global.cache.entries);
+        OCSP_Global.cache.entries = NULL;
+    }
+    PORT_Assert(OCSP_Global.cache.numberOfEntries == 0);
+    OCSP_Global.cache.MRUitem = NULL;
+    OCSP_Global.cache.LRUitem = NULL;
+
+    OCSP_Global.defaultHttpClientFcn = NULL;
+    OCSP_Global.maxCacheEntries = DEFAULT_OCSP_CACHE_SIZE;
+    OCSP_Global.minimumSecondsToNextFetchAttempt = 
+      DEFAULT_MINIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT;
+    OCSP_Global.maximumSecondsToNextFetchAttempt =
+      DEFAULT_MAXIMUM_SECONDS_TO_NEXT_OCSP_FETCH_ATTEMPT;
+    OCSP_Global.ocspFailureMode =
+      ocspMode_FailureIsVerificationFailure;
+    PR_ExitMonitor(OCSP_Global.monitor);
+
+    PR_DestroyMonitor(OCSP_Global.monitor);
+    OCSP_Global.monitor = NULL;
+    return SECSuccess;
+}
+
+/*
+ * A return value of NULL means: 
+ *   The application did not register it's own HTTP client.
+ */
+const SEC_HttpClientFcn *SEC_GetRegisteredHttpClient(void)
+{
+    const SEC_HttpClientFcn *retval;
+
+    if (!OCSP_Global.monitor) {
+      PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+      return NULL;
+    }
+
+    PR_EnterMonitor(OCSP_Global.monitor);
+    retval = OCSP_Global.defaultHttpClientFcn;
+    PR_ExitMonitor(OCSP_Global.monitor);
+    
+    return retval;
+}
+
+/*
+ * The following structure is only used internally.  It is allocated when
+ * someone turns on OCSP checking, and hangs off of the status-configuration
+ * structure in the certdb structure.  We use it to keep configuration
+ * information specific to OCSP checking.
+ */
+typedef struct ocspCheckingContextStr {
+    PRBool useDefaultResponder;
+    char *defaultResponderURI;
+    char *defaultResponderNickname;
+    CERTCertificate *defaultResponderCert;
+} ocspCheckingContext;
+
+SEC_ASN1_MKSUB(SEC_AnyTemplate)
+SEC_ASN1_MKSUB(SEC_IntegerTemplate)
+SEC_ASN1_MKSUB(SEC_NullTemplate)
+SEC_ASN1_MKSUB(SEC_OctetStringTemplate)
+SEC_ASN1_MKSUB(SEC_PointerToAnyTemplate)
+SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
+SEC_ASN1_MKSUB(SEC_SequenceOfAnyTemplate)
+SEC_ASN1_MKSUB(SEC_PointerToGeneralizedTimeTemplate)
+SEC_ASN1_MKSUB(SEC_PointerToEnumeratedTemplate)
+
+/*
+ * Forward declarations of sub-types, so I can lay out the types in the
+ * same order as the ASN.1 is laid out in the OCSP spec itself.
+ *
+ * These are in alphabetical order (case-insensitive); please keep it that way!
+ */
+extern const SEC_ASN1Template ocsp_CertIDTemplate[];
+extern const SEC_ASN1Template ocsp_PointerToSignatureTemplate[];
+extern const SEC_ASN1Template ocsp_PointerToResponseBytesTemplate[];
+extern const SEC_ASN1Template ocsp_ResponseDataTemplate[];
+extern const SEC_ASN1Template ocsp_RevokedInfoTemplate[];
+extern const SEC_ASN1Template ocsp_SingleRequestTemplate[];
+extern const SEC_ASN1Template ocsp_SingleResponseTemplate[];
+extern const SEC_ASN1Template ocsp_TBSRequestTemplate[];
+
+
+/*
+ * Request-related templates...
+ */
+
+/*
+ * OCSPRequest	::=	SEQUENCE {
+ *	tbsRequest		TBSRequest,
+ *	optionalSignature	[0] EXPLICIT Signature OPTIONAL }
+ */
+static const SEC_ASN1Template ocsp_OCSPRequestTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	0, NULL, sizeof(CERTOCSPRequest) },
+    { SEC_ASN1_POINTER,
+	offsetof(CERTOCSPRequest, tbsRequest),
+	ocsp_TBSRequestTemplate },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+      SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+	offsetof(CERTOCSPRequest, optionalSignature),
+	ocsp_PointerToSignatureTemplate },
+    { 0 }
+};
+
+/*
+ * TBSRequest	::=	SEQUENCE {
+ *	version			[0] EXPLICIT Version DEFAULT v1,
+ *	requestorName		[1] EXPLICIT GeneralName OPTIONAL,
+ *	requestList		SEQUENCE OF Request,
+ *	requestExtensions	[2] EXPLICIT Extensions OPTIONAL }
+ *
+ * Version	::=	INTEGER { v1(0) }
+ *
+ * Note: this should be static but the AIX compiler doesn't like it (because it
+ * was forward-declared above); it is not meant to be exported, but this
+ * is the only way it will compile.
+ */
+const SEC_ASN1Template ocsp_TBSRequestTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	0, NULL, sizeof(ocspTBSRequest) },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |		/* XXX DER_DEFAULT */
+      SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+	offsetof(ocspTBSRequest, version),
+	SEC_ASN1_SUB(SEC_IntegerTemplate) },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+      SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 1,
+	offsetof(ocspTBSRequest, derRequestorName),
+	SEC_ASN1_SUB(SEC_PointerToAnyTemplate) },
+    { SEC_ASN1_SEQUENCE_OF,
+	offsetof(ocspTBSRequest, requestList),
+	ocsp_SingleRequestTemplate },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+      SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 2,
+	offsetof(ocspTBSRequest, requestExtensions),
+	CERT_SequenceOfCertExtensionTemplate },
+    { 0 }
+};
+
+/*
+ * Signature	::=	SEQUENCE {
+ *	signatureAlgorithm	AlgorithmIdentifier,
+ *	signature		BIT STRING,
+ *	certs			[0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+ */
+static const SEC_ASN1Template ocsp_SignatureTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	0, NULL, sizeof(ocspSignature) },
+    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+	offsetof(ocspSignature, signatureAlgorithm),
+	SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+    { SEC_ASN1_BIT_STRING,
+	offsetof(ocspSignature, signature) },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+      SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+	offsetof(ocspSignature, derCerts), 
+	SEC_ASN1_SUB(SEC_SequenceOfAnyTemplate) },
+    { 0 }
+};
+
+/*
+ * This template is just an extra level to use in an explicitly-tagged
+ * reference to a Signature.
+ *
+ * Note: this should be static but the AIX compiler doesn't like it (because it
+ * was forward-declared above); it is not meant to be exported, but this
+ * is the only way it will compile.
+ */
+const SEC_ASN1Template ocsp_PointerToSignatureTemplate[] = {
+    { SEC_ASN1_POINTER, 0, ocsp_SignatureTemplate }
+};
+
+/*
+ * Request	::=	SEQUENCE {
+ *	reqCert			CertID,
+ *	singleRequestExtensions	[0] EXPLICIT Extensions OPTIONAL }
+ *
+ * Note: this should be static but the AIX compiler doesn't like it (because it
+ * was forward-declared above); it is not meant to be exported, but this
+ * is the only way it will compile.
+ */
+const SEC_ASN1Template ocsp_SingleRequestTemplate[] = {
+    { SEC_ASN1_SEQUENCE, 
+	0, NULL, sizeof(ocspSingleRequest) },
+    { SEC_ASN1_POINTER,
+	offsetof(ocspSingleRequest, reqCert),
+	ocsp_CertIDTemplate },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+      SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+	offsetof(ocspSingleRequest, singleRequestExtensions),
+	CERT_SequenceOfCertExtensionTemplate },
+    { 0 }
+};
+
+
+/*
+ * This data structure and template (CertID) is used by both OCSP
+ * requests and responses.  It is the only one that is shared.
+ *
+ * CertID	::=	SEQUENCE {
+ *	hashAlgorithm		AlgorithmIdentifier,
+ *	issuerNameHash		OCTET STRING,	-- Hash of Issuer DN
+ *	issuerKeyHash		OCTET STRING,	-- Hash of Issuer public key
+ *	serialNumber		CertificateSerialNumber }
+ *
+ * CertificateSerialNumber ::=	INTEGER
+ *
+ * Note: this should be static but the AIX compiler doesn't like it (because it
+ * was forward-declared above); it is not meant to be exported, but this
+ * is the only way it will compile.
+ */
+const SEC_ASN1Template ocsp_CertIDTemplate[] = {
+    { SEC_ASN1_SEQUENCE, 
+	0, NULL, sizeof(CERTOCSPCertID) },
+    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+	offsetof(CERTOCSPCertID, hashAlgorithm),
+	SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+    { SEC_ASN1_OCTET_STRING,
+	offsetof(CERTOCSPCertID, issuerNameHash) },
+    { SEC_ASN1_OCTET_STRING,
+	offsetof(CERTOCSPCertID, issuerKeyHash) },
+    { SEC_ASN1_INTEGER, 
+	offsetof(CERTOCSPCertID, serialNumber) },
+    { 0 }
+};
+
+
+/*
+ * Response-related templates...
+ */
+
+/*
+ * OCSPResponse	::=	SEQUENCE {
+ *	responseStatus		OCSPResponseStatus,
+ *	responseBytes		[0] EXPLICIT ResponseBytes OPTIONAL }
+ */
+const SEC_ASN1Template ocsp_OCSPResponseTemplate[] = {
+    { SEC_ASN1_SEQUENCE, 
+	0, NULL, sizeof(CERTOCSPResponse) },
+    { SEC_ASN1_ENUMERATED, 
+	offsetof(CERTOCSPResponse, responseStatus) },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+      SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 0,
+	offsetof(CERTOCSPResponse, responseBytes),
+	ocsp_PointerToResponseBytesTemplate },
+    { 0 }
+};
+
+/*
+ * ResponseBytes	::=	SEQUENCE {
+ *	responseType		OBJECT IDENTIFIER,
+ *	response		OCTET STRING }
+ */
+const SEC_ASN1Template ocsp_ResponseBytesTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	0, NULL, sizeof(ocspResponseBytes) },
+    { SEC_ASN1_OBJECT_ID,
+	offsetof(ocspResponseBytes, responseType) },
+    { SEC_ASN1_OCTET_STRING,
+	offsetof(ocspResponseBytes, response) },
+    { 0 }
+};
+
+/*
+ * This template is just an extra level to use in an explicitly-tagged
+ * reference to a ResponseBytes.
+ *
+ * Note: this should be static but the AIX compiler doesn't like it (because it
+ * was forward-declared above); it is not meant to be exported, but this
+ * is the only way it will compile.
+ */
+const SEC_ASN1Template ocsp_PointerToResponseBytesTemplate[] = {
+    { SEC_ASN1_POINTER, 0, ocsp_ResponseBytesTemplate }
+};
+
+/*
+ * BasicOCSPResponse	::=	SEQUENCE {
+ *	tbsResponseData		ResponseData,
+ *	signatureAlgorithm	AlgorithmIdentifier,
+ *	signature		BIT STRING,
+ *	certs			[0] EXPLICIT SEQUENCE OF Certificate OPTIONAL }
+ */
+static const SEC_ASN1Template ocsp_BasicOCSPResponseTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	0, NULL, sizeof(ocspBasicOCSPResponse) },
+    { SEC_ASN1_ANY | SEC_ASN1_SAVE,
+	offsetof(ocspBasicOCSPResponse, tbsResponseDataDER) },
+    { SEC_ASN1_POINTER,
+	offsetof(ocspBasicOCSPResponse, tbsResponseData),
+	ocsp_ResponseDataTemplate },
+    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
+	offsetof(ocspBasicOCSPResponse, responseSignature.signatureAlgorithm),
+	SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
+    { SEC_ASN1_BIT_STRING,
+	offsetof(ocspBasicOCSPResponse, responseSignature.signature) },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+      SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+	offsetof(ocspBasicOCSPResponse, responseSignature.derCerts),
+	SEC_ASN1_SUB(SEC_SequenceOfAnyTemplate) },
+    { 0 }
+};
+
+/*
+ * ResponseData	::=	SEQUENCE {
+ *	version			[0] EXPLICIT Version DEFAULT v1,
+ *	responderID		ResponderID,
+ *	producedAt		GeneralizedTime,
+ *	responses		SEQUENCE OF SingleResponse,
+ *	responseExtensions	[1] EXPLICIT Extensions OPTIONAL }
+ *
+ * Note: this should be static but the AIX compiler doesn't like it (because it
+ * was forward-declared above); it is not meant to be exported, but this
+ * is the only way it will compile.
+ */
+const SEC_ASN1Template ocsp_ResponseDataTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	0, NULL, sizeof(ocspResponseData) },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |		/* XXX DER_DEFAULT */
+      SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+	offsetof(ocspResponseData, version),
+	SEC_ASN1_SUB(SEC_IntegerTemplate) },
+    { SEC_ASN1_ANY,
+	offsetof(ocspResponseData, derResponderID) },
+    { SEC_ASN1_GENERALIZED_TIME,
+	offsetof(ocspResponseData, producedAt) },
+    { SEC_ASN1_SEQUENCE_OF,
+	offsetof(ocspResponseData, responses),
+	ocsp_SingleResponseTemplate },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+      SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+	offsetof(ocspResponseData, responseExtensions),
+	CERT_SequenceOfCertExtensionTemplate },
+    { 0 }
+};
+
+/*
+ * ResponderID	::=	CHOICE {
+ *	byName			[1] EXPLICIT Name,
+ *	byKey			[2] EXPLICIT KeyHash }
+ *
+ * KeyHash ::=	OCTET STRING -- SHA-1 hash of responder's public key
+ * (excluding the tag and length fields)
+ *
+ * XXX Because the ASN.1 encoder and decoder currently do not provide
+ * a way to automatically handle a CHOICE, we need to do it in two
+ * steps, looking at the type tag and feeding the exact choice back
+ * to the ASN.1 code.  Hopefully that will change someday and this
+ * can all be simplified down into a single template.  Anyway, for
+ * now we list each choice as its own template:
+ */
+const SEC_ASN1Template ocsp_ResponderIDByNameTemplate[] = {
+    { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+	offsetof(ocspResponderID, responderIDValue.name),
+	CERT_NameTemplate }
+};
+const SEC_ASN1Template ocsp_ResponderIDByKeyTemplate[] = {
+    { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
+        SEC_ASN1_XTRN | 2,
+	offsetof(ocspResponderID, responderIDValue.keyHash),
+	SEC_ASN1_SUB(SEC_OctetStringTemplate) }
+};
+static const SEC_ASN1Template ocsp_ResponderIDOtherTemplate[] = {
+    { SEC_ASN1_ANY,
+	offsetof(ocspResponderID, responderIDValue.other) }
+};
+
+/* Decode choice container, but leave x509 name object encoded */
+static const SEC_ASN1Template ocsp_ResponderIDDerNameTemplate[] = {
+    { SEC_ASN1_EXPLICIT | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
+        SEC_ASN1_XTRN | 1, 0, SEC_ASN1_SUB(SEC_AnyTemplate) }
+};
+
+/*
+ * SingleResponse	::=	SEQUENCE {
+ *	certID			CertID,
+ *	certStatus		CertStatus,
+ *	thisUpdate		GeneralizedTime,
+ *	nextUpdate		[0] EXPLICIT GeneralizedTime OPTIONAL,
+ *	singleExtensions	[1] EXPLICIT Extensions OPTIONAL }
+ *
+ * Note: this should be static but the AIX compiler doesn't like it (because it
+ * was forward-declared above); it is not meant to be exported, but this
+ * is the only way it will compile.
+ */
+const SEC_ASN1Template ocsp_SingleResponseTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	0, NULL, sizeof(CERTOCSPSingleResponse) },
+    { SEC_ASN1_POINTER,
+	offsetof(CERTOCSPSingleResponse, certID),
+	ocsp_CertIDTemplate },
+    { SEC_ASN1_ANY,
+	offsetof(CERTOCSPSingleResponse, derCertStatus) },
+    { SEC_ASN1_GENERALIZED_TIME,
+	offsetof(CERTOCSPSingleResponse, thisUpdate) },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+      SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+	offsetof(CERTOCSPSingleResponse, nextUpdate),
+	SEC_ASN1_SUB(SEC_PointerToGeneralizedTimeTemplate) },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+      SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1,
+	offsetof(CERTOCSPSingleResponse, singleExtensions),
+	CERT_SequenceOfCertExtensionTemplate },
+    { 0 }
+};
+
+/*
+ * CertStatus	::=	CHOICE {
+ *	good			[0] IMPLICIT NULL,
+ *	revoked			[1] IMPLICIT RevokedInfo,
+ *	unknown			[2] IMPLICIT UnknownInfo }
+ *
+ * Because the ASN.1 encoder and decoder currently do not provide
+ * a way to automatically handle a CHOICE, we need to do it in two
+ * steps, looking at the type tag and feeding the exact choice back
+ * to the ASN.1 code.  Hopefully that will change someday and this
+ * can all be simplified down into a single template.  Anyway, for
+ * now we list each choice as its own template:
+ */
+static const SEC_ASN1Template ocsp_CertStatusGoodTemplate[] = {
+    { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 0,
+	offsetof(ocspCertStatus, certStatusInfo.goodInfo),
+	SEC_ASN1_SUB(SEC_NullTemplate) }
+};
+static const SEC_ASN1Template ocsp_CertStatusRevokedTemplate[] = {
+    { SEC_ASN1_POINTER | SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC | 1, 
+	offsetof(ocspCertStatus, certStatusInfo.revokedInfo),
+	ocsp_RevokedInfoTemplate }
+};
+static const SEC_ASN1Template ocsp_CertStatusUnknownTemplate[] = {
+    { SEC_ASN1_POINTER | SEC_ASN1_CONTEXT_SPECIFIC | SEC_ASN1_XTRN | 2,
+	offsetof(ocspCertStatus, certStatusInfo.unknownInfo),
+	SEC_ASN1_SUB(SEC_NullTemplate) }
+};
+static const SEC_ASN1Template ocsp_CertStatusOtherTemplate[] = {
+    { SEC_ASN1_POINTER | SEC_ASN1_XTRN,
+	offsetof(ocspCertStatus, certStatusInfo.otherInfo),
+	SEC_ASN1_SUB(SEC_AnyTemplate) }
+};
+
+/*
+ * RevokedInfo	::=	SEQUENCE {
+ *	revocationTime		GeneralizedTime,
+ *	revocationReason	[0] EXPLICIT CRLReason OPTIONAL }
+ *
+ * Note: this should be static but the AIX compiler doesn't like it (because it
+ * was forward-declared above); it is not meant to be exported, but this
+ * is the only way it will compile.
+ */
+const SEC_ASN1Template ocsp_RevokedInfoTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	0, NULL, sizeof(ocspRevokedInfo) },
+    { SEC_ASN1_GENERALIZED_TIME,
+	offsetof(ocspRevokedInfo, revocationTime) },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_EXPLICIT |
+      SEC_ASN1_CONSTRUCTED | SEC_ASN1_CONTEXT_SPECIFIC |
+        SEC_ASN1_XTRN | 0,
+	offsetof(ocspRevokedInfo, revocationReason), 
+	SEC_ASN1_SUB(SEC_PointerToEnumeratedTemplate) },
+    { 0 }
+};
+
+
+/*
+ * OCSP-specific extension templates:
+ */
+
+/*
+ * ServiceLocator	::=	SEQUENCE {
+ *	issuer			Name,
+ *	locator			AuthorityInfoAccessSyntax OPTIONAL }
+ */
+static const SEC_ASN1Template ocsp_ServiceLocatorTemplate[] = {
+    { SEC_ASN1_SEQUENCE,
+	0, NULL, sizeof(ocspServiceLocator) },
+    { SEC_ASN1_POINTER,
+	offsetof(ocspServiceLocator, issuer),
+	CERT_NameTemplate },
+    { SEC_ASN1_OPTIONAL | SEC_ASN1_ANY,
+	offsetof(ocspServiceLocator, locator) },
+    { 0 }
+};
+
+
+/*
+ * REQUEST SUPPORT FUNCTIONS (encode/create/decode/destroy):
+ */
+
+/* 
+ * FUNCTION: CERT_EncodeOCSPRequest
+ *   DER encodes an OCSP Request, possibly adding a signature as well.
+ *   XXX Signing is not yet supported, however; see comments in code.
+ * INPUTS: 
+ *   PLArenaPool *arena
+ *     The return value is allocated from here.
+ *     If a NULL is passed in, allocation is done from the heap instead.
+ *   CERTOCSPRequest *request
+ *     The request to be encoded.
+ *   void *pwArg
+ *     Pointer to argument for password prompting, if needed.  (Definitely
+ *     not needed if not signing.)
+ * RETURN:
+ *   Returns a NULL on error and a pointer to the SECItem with the
+ *   encoded value otherwise.  Any error is likely to be low-level
+ *   (e.g. no memory).
+ */
+SECItem *
+CERT_EncodeOCSPRequest(PLArenaPool *arena, CERTOCSPRequest *request,
+		       void *pwArg)
+{
+    SECStatus rv;
+
+    /* XXX All of these should generate errors if they fail. */
+    PORT_Assert(request);
+    PORT_Assert(request->tbsRequest);
+
+    if (request->tbsRequest->extensionHandle != NULL) {
+	rv = CERT_FinishExtensions(request->tbsRequest->extensionHandle);
+	request->tbsRequest->extensionHandle = NULL;
+	if (rv != SECSuccess)
+	    return NULL;
+    }
+
+    /*
+     * XXX When signed requests are supported and request->optionalSignature
+     * is not NULL:
+     *  - need to encode tbsRequest->requestorName
+     *  - need to encode tbsRequest
+     *  - need to sign that encoded result (using cert in sig), filling in the
+     *    request->optionalSignature structure with the result, the signing
+     *    algorithm and (perhaps?) the cert (and its chain?) in derCerts
+     */
+
+    return SEC_ASN1EncodeItem(arena, NULL, request, ocsp_OCSPRequestTemplate);
+}
+
+
+/*
+ * FUNCTION: CERT_DecodeOCSPRequest
+ *   Decode a DER encoded OCSP Request.
+ * INPUTS:
+ *   SECItem *src
+ *     Pointer to a SECItem holding DER encoded OCSP Request.
+ * RETURN:
+ *   Returns a pointer to a CERTOCSPRequest containing the decoded request.
+ *   On error, returns NULL.  Most likely error is trouble decoding
+ *   (SEC_ERROR_OCSP_MALFORMED_REQUEST), or low-level problem (no memory).
+ */
+CERTOCSPRequest *
+CERT_DecodeOCSPRequest(const SECItem *src)
+{
+    PLArenaPool *arena = NULL;
+    SECStatus rv = SECFailure;
+    CERTOCSPRequest *dest = NULL;
+    int i;
+    SECItem newSrc;
+
+    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    if (arena == NULL) {
+	goto loser;
+    }
+    dest = (CERTOCSPRequest *) PORT_ArenaZAlloc(arena, 
+						sizeof(CERTOCSPRequest));
+    if (dest == NULL) {
+	goto loser;
+    }
+    dest->arena = arena;
+
+    /* copy the DER into the arena, since Quick DER returns data that points
+       into the DER input, which may get freed by the caller */
+    rv = SECITEM_CopyItem(arena, &newSrc, src);
+    if ( rv != SECSuccess ) {
+	goto loser;
+    }
+
+    rv = SEC_QuickDERDecodeItem(arena, dest, ocsp_OCSPRequestTemplate, &newSrc);
+    if (rv != SECSuccess) {
+	if (PORT_GetError() == SEC_ERROR_BAD_DER)
+	    PORT_SetError(SEC_ERROR_OCSP_MALFORMED_REQUEST);
+	goto loser;
+    }
+
+    /*
+     * XXX I would like to find a way to get rid of the necessity
+     * of doing this copying of the arena pointer.
+     */
+    for (i = 0; dest->tbsRequest->requestList[i] != NULL; i++) {
+	dest->tbsRequest->requestList[i]->arena = arena;
+    }
+
+    return dest;
+
+loser:
+    if (arena != NULL) {
+	PORT_FreeArena(arena, PR_FALSE);
+    }
+    return NULL;
+}
+
+SECStatus
+CERT_DestroyOCSPCertID(CERTOCSPCertID* certID)
+{
+    if (certID && certID->poolp) {
+	PORT_FreeArena(certID->poolp, PR_FALSE);
+	return SECSuccess;
+    }
+    PORT_SetError(SEC_ERROR_INVALID_ARGS);
+    return SECFailure;
+}
+
+/*
+ * Digest data using the specified algorithm.
+ * The necessary storage for the digest data is allocated.  If "fill" is
+ * non-null, the data is put there, otherwise a SECItem is allocated.
+ * Allocation from "arena" if it is non-null, heap otherwise.  Any problem
+ * results in a NULL being returned (and an appropriate error set).
+ */
+
+SECItem *
+ocsp_DigestValue(PLArenaPool *arena, SECOidTag digestAlg, 
+                 SECItem *fill, const SECItem *src)
+{
+    const SECHashObject *digestObject;
+    SECItem *result = NULL;
+    void *mark = NULL;
+    void *digestBuff = NULL;
+
+    if ( arena != NULL ) {
+        mark = PORT_ArenaMark(arena);
+    }
+
+    digestObject = HASH_GetHashObjectByOidTag(digestAlg);
+    if ( digestObject == NULL ) {
+        goto loser;
+    }
+
+    if (fill == NULL || fill->data == NULL) {
+	result = SECITEM_AllocItem(arena, fill, digestObject->length);
+	if ( result == NULL ) {
+	   goto loser;
+	}
+	digestBuff = result->data;
+    } else {
+	if (fill->len < digestObject->length) {
+	    PORT_SetError(SEC_ERROR_INVALID_ARGS);
+	    goto loser;
+	}
+	digestBuff = fill->data;
+    }
+
+    if (PK11_HashBuf(digestAlg, digestBuff,
+                     src->data, src->len) != SECSuccess) {
+        goto loser;
+    }
+
+    if ( arena != NULL ) {
+        PORT_ArenaUnmark(arena, mark);
+    }
+
+    if (result == NULL) {
+        result = fill;
+    }
+    return result;
+
+loser:
+    if (arena != NULL) {
+        PORT_ArenaRelease(arena, mark);
+    } else {
+        if (result != NULL) {
+            SECITEM_FreeItem(result, (fill == NULL) ? PR_TRUE : PR_FALSE);
+        }
+    }
+    return(NULL);
+}
+
+/*
+ * Digest the cert's subject public key using the specified algorithm.
+ * The necessary storage for the digest data is allocated.  If "fill" is
+ * non-null, the data is put there, otherwise a SECItem is allocated.
+ * Allocation from "arena" if it is non-null, heap otherwise.  Any problem
+ * results in a NULL being returned (and an appropriate error set).
+ */
+SECItem *
+CERT_GetSubjectPublicKeyDigest(PLArenaPool *arena, const CERTCertificate *cert,
+                               SECOidTag digestAlg, SECItem *fill)
+{
+    SECItem spk;
+
+    /*
+     * Copy just the length and data pointer (nothing needs to be freed)
+     * of the subject public key so we can convert the length from bits
+     * to bytes, which is what the digest function expects.
+     */
+    spk = cert->subjectPublicKeyInfo.subjectPublicKey;
+    DER_ConvertBitString(&spk);
+
+    return ocsp_DigestValue(arena, digestAlg, fill, &spk);
+}
+
+/*
+ * Digest the cert's subject name using the specified algorithm.
+ */
+SECItem *
+CERT_GetSubjectNameDigest(PLArenaPool *arena, const CERTCertificate *cert,
+                          SECOidTag digestAlg, SECItem *fill)
+{
+    SECItem name;
+
+    /*
+     * Copy just the length and data pointer (nothing needs to be freed)
+     * of the subject name
+     */
+    name = cert->derSubject;
+
+    return ocsp_DigestValue(arena, digestAlg, fill, &name);
+}
+
+/*
+ * Create and fill-in a CertID.  This function fills in the hash values
+ * (issuerNameHash and issuerKeyHash), and is hardwired to use SHA1.
+ * Someday it might need to be more flexible about hash algorithm, but
+ * for now we have no intention/need to create anything else.
+ *
+ * Error causes a null to be returned; most likely cause is trouble
+ * finding the certificate issuer (SEC_ERROR_UNKNOWN_ISSUER).
+ * Other errors are low-level problems (no memory, bad database, etc.).
+ */
+static CERTOCSPCertID *
+ocsp_CreateCertID(PLArenaPool *arena, CERTCertificate *cert, PRTime time)
+{
+    CERTOCSPCertID *certID;
+    CERTCertificate *issuerCert = NULL;
+    void *mark = PORT_ArenaMark(arena);
+    SECStatus rv;
+
+    PORT_Assert(arena != NULL);
+
+    certID = PORT_ArenaZNew(arena, CERTOCSPCertID);
+    if (certID == NULL) {
+	goto loser;
+    }
+
+    rv = SECOID_SetAlgorithmID(arena, &certID->hashAlgorithm, SEC_OID_SHA1,
+			       NULL);
+    if (rv != SECSuccess) {
+	goto loser; 
+    }
+
+    issuerCert = CERT_FindCertIssuer(cert, time, certUsageAnyCA);
+    if (issuerCert == NULL) {
+	goto loser;
+    }
+
+    if (CERT_GetSubjectNameDigest(arena, issuerCert, SEC_OID_SHA1,
+                                  &(certID->issuerNameHash)) == NULL) {
+        goto loser;
+    }
+    certID->issuerSHA1NameHash.data = certID->issuerNameHash.data;
+    certID->issuerSHA1NameHash.len = certID->issuerNameHash.len;
+
+    if (CERT_GetSubjectNameDigest(arena, issuerCert, SEC_OID_MD5,
+                                  &(certID->issuerMD5NameHash)) == NULL) {
+        goto loser;
+    }
+
+    if (CERT_GetSubjectNameDigest(arena, issuerCert, SEC_OID_MD2,
+                                  &(certID->issuerMD2NameHash)) == NULL) {
+        goto loser;
+    }
+
+    if (CERT_GetSubjectPublicKeyDigest(arena, issuerCert, SEC_OID_SHA1,
+				       &certID->issuerKeyHash) == NULL) {
+	goto loser;
+    }
+    certID->issuerSHA1KeyHash.data = certID->issuerKeyHash.data;
+    certID->issuerSHA1KeyHash.len = certID->issuerKeyHash.len;
+    /* cache the other two hash algorithms as well */
+    if (CERT_GetSubjectPublicKeyDigest(arena, issuerCert, SEC_OID_MD5,
+				       &certID->issuerMD5KeyHash) == NULL) {
+	goto loser;
+    }
+    if (CERT_GetSubjectPublicKeyDigest(arena, issuerCert, SEC_OID_MD2,
+				       &certID->issuerMD2KeyHash) == NULL) {
+	goto loser;
+    }
+
+
+    /* now we are done with issuerCert */
+    CERT_DestroyCertificate(issuerCert);
+    issuerCert = NULL;
+
+    rv = SECITEM_CopyItem(arena, &certID->serialNumber, &cert->serialNumber);
+    if (rv != SECSuccess) {
+	goto loser; 
+    }
+
+    PORT_ArenaUnmark(arena, mark);
+    return certID;
+
+loser:
+    if (issuerCert != NULL) {
+	CERT_DestroyCertificate(issuerCert);
+    }
+    PORT_ArenaRelease(arena, mark);
+    return NULL;
+}
+
+CERTOCSPCertID*
+CERT_CreateOCSPCertID(CERTCertificate *cert, PRTime time)
+{
+    PLArenaPool *arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    CERTOCSPCertID *certID;
+    PORT_Assert(arena != NULL);
+    if (!arena)
+	return NULL;
+    
+    certID = ocsp_CreateCertID(arena, cert, time);
+    if (!certID) {
+	PORT_FreeArena(arena, PR_FALSE);
+	return NULL;
+    }
+    certID->poolp = arena;
+    return certID;
+}
+
+static CERTOCSPCertID *
+cert_DupOCSPCertID(const CERTOCSPCertID *src)
+{
+    CERTOCSPCertID *dest;
+    PLArenaPool *arena = NULL;
+
+    if (!src) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return NULL;
+    }
+
+    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    if (!arena)
+        goto loser;
+
+    dest = PORT_ArenaZNew(arena, CERTOCSPCertID);
+    if (!dest)
+        goto loser;
+
+#define DUPHELP(element) \
+    if (src->element.data && \
+        SECITEM_CopyItem(arena, &dest->element, &src->element) \
+        != SECSuccess) { \
+        goto loser; \
+    }
+
+    DUPHELP(hashAlgorithm.algorithm)
+    DUPHELP(hashAlgorithm.parameters)
+    DUPHELP(issuerNameHash)
+    DUPHELP(issuerKeyHash)
+    DUPHELP(serialNumber)
+    DUPHELP(issuerSHA1NameHash)
+    DUPHELP(issuerMD5NameHash)
+    DUPHELP(issuerMD2NameHash)
+    DUPHELP(issuerSHA1KeyHash)
+    DUPHELP(issuerMD5KeyHash)
+    DUPHELP(issuerMD2KeyHash)
+
+    dest->poolp = arena;
+    return dest;
+
+loser:
+    if (arena)
+        PORT_FreeArena(arena, PR_FALSE);
+    PORT_SetError(PR_OUT_OF_MEMORY_ERROR);
+    return NULL;
+}
+
+/*
+ * Callback to set Extensions in request object
+ */
+void SetSingleReqExts(void *object, CERTCertExtension **exts)
+{
+  ocspSingleRequest *singleRequest =
+    (ocspSingleRequest *)object;
+
+  singleRequest->singleRequestExtensions = exts;
+}
+
+/*
+ * Add the Service Locator extension to the singleRequestExtensions
+ * for the given singleRequest.
+ *
+ * All errors are internal or low-level problems (e.g. no memory).
+ */
+static SECStatus
+ocsp_AddServiceLocatorExtension(ocspSingleRequest *singleRequest,
+				CERTCertificate *cert)
+{
+    ocspServiceLocator *serviceLocator = NULL;
+    void *extensionHandle = NULL;
+    SECStatus rv = SECFailure;
+
+    serviceLocator = PORT_ZNew(ocspServiceLocator);
+    if (serviceLocator == NULL)
+	goto loser;
+
+    /*
+     * Normally it would be a bad idea to do a direct reference like
+     * this rather than allocate and copy the name *or* at least dup
+     * a reference of the cert.  But all we need is to be able to read
+     * the issuer name during the encoding we are about to do, so a
+     * copy is just a waste of time.
+     */
+    serviceLocator->issuer = &cert->issuer;
+
+    rv = CERT_FindCertExtension(cert, SEC_OID_X509_AUTH_INFO_ACCESS,
+				&serviceLocator->locator);
+    if (rv != SECSuccess) {
+	if (PORT_GetError() != SEC_ERROR_EXTENSION_NOT_FOUND)
+	    goto loser;
+    }
+
+    /* prepare for following loser gotos */
+    rv = SECFailure;
+    PORT_SetError(0);
+
+    extensionHandle = cert_StartExtensions(singleRequest,
+                       singleRequest->arena, SetSingleReqExts);
+    if (extensionHandle == NULL)
+	goto loser;
+
+    rv = CERT_EncodeAndAddExtension(extensionHandle,
+				    SEC_OID_PKIX_OCSP_SERVICE_LOCATOR,
+				    serviceLocator, PR_FALSE,
+				    ocsp_ServiceLocatorTemplate);
+
+loser:
+    if (extensionHandle != NULL) {
+	/*
+	 * Either way we have to finish out the extension context (so it gets
+	 * freed).  But careful not to override any already-set bad status.
+	 */
+	SECStatus tmprv = CERT_FinishExtensions(extensionHandle);
+	if (rv == SECSuccess)
+	    rv = tmprv;
+    }
+
+    /*
+     * Finally, free the serviceLocator structure itself and we are done.
+     */
+    if (serviceLocator != NULL) {
+	if (serviceLocator->locator.data != NULL)
+	    SECITEM_FreeItem(&serviceLocator->locator, PR_FALSE);
+	PORT_Free(serviceLocator);
+    }
+
+    return rv;
+}
+
+/*
+ * Creates an array of ocspSingleRequest based on a list of certs.
+ * Note that the code which later compares the request list with the
+ * response expects this array to be in the exact same order as the
+ * certs are found in the list.  It would be harder to change that
+ * order than preserve it, but since the requirement is not obvious,
+ * it deserves to be mentioned.
+ *
+ * Any problem causes a null return and error set:
+ *      SEC_ERROR_UNKNOWN_ISSUER
+ * Other errors are low-level problems (no memory, bad database, etc.).
+ */
+static ocspSingleRequest **
+ocsp_CreateSingleRequestList(PLArenaPool *arena, CERTCertList *certList,
+                             PRTime time, PRBool includeLocator)
+{
+    ocspSingleRequest **requestList = NULL;
+    CERTCertListNode *node = NULL;
+    int i, count;
+    void *mark = PORT_ArenaMark(arena);
+ 
+    node = CERT_LIST_HEAD(certList);
+    for (count = 0; !CERT_LIST_END(node, certList); count++) {
+        node = CERT_LIST_NEXT(node);
+    }
+
+    if (count == 0)
+	goto loser;
+
+    requestList = PORT_ArenaNewArray(arena, ocspSingleRequest *, count + 1);
+    if (requestList == NULL)
+	goto loser;
+
+    node = CERT_LIST_HEAD(certList);
+    for (i = 0; !CERT_LIST_END(node, certList); i++) {
+        requestList[i] = PORT_ArenaZNew(arena, ocspSingleRequest);
+        if (requestList[i] == NULL)
+            goto loser;
+
+        OCSP_TRACE(("OCSP CERT_CreateOCSPRequest %s\n", node->cert->subjectName));
+        requestList[i]->arena = arena;
+        requestList[i]->reqCert = ocsp_CreateCertID(arena, node->cert, time);
+        if (requestList[i]->reqCert == NULL)
+            goto loser;
+
+        if (includeLocator == PR_TRUE) {
+            SECStatus rv;
+
+            rv = ocsp_AddServiceLocatorExtension(requestList[i], node->cert);
+            if (rv != SECSuccess)
+                goto loser;
+        }
+
+        node = CERT_LIST_NEXT(node);
+    }
+
+    PORT_Assert(i == count);
+
+    PORT_ArenaUnmark(arena, mark);
+    requestList[i] = NULL;
+    return requestList;
+
+loser:
+    PORT_ArenaRelease(arena, mark);
+    return NULL;
+}
+
+static ocspSingleRequest **
+ocsp_CreateRequestFromCert(PLArenaPool *arena,
+                           CERTOCSPCertID *certID, 
+                           CERTCertificate *singleCert,
+                           PRTime time,
+                           PRBool includeLocator)
+{
+    ocspSingleRequest **requestList = NULL;
+    void *mark = PORT_ArenaMark(arena);
+    PORT_Assert(certID != NULL && singleCert != NULL);
+
+    /* meaning of value 2: one entry + one end marker */
+    requestList = PORT_ArenaNewArray(arena, ocspSingleRequest *, 2);
+    if (requestList == NULL)
+        goto loser;
+    requestList[0] = PORT_ArenaZNew(arena, ocspSingleRequest);
+    if (requestList[0] == NULL)
+        goto loser;
+    requestList[0]->arena = arena;
+    /* certID will live longer than the request */
+    requestList[0]->reqCert = certID; 
+
+    if (includeLocator == PR_TRUE) {
+        SECStatus rv;
+        rv = ocsp_AddServiceLocatorExtension(requestList[0], singleCert);
+        if (rv != SECSuccess)
+            goto loser;
+    }
+
+    PORT_ArenaUnmark(arena, mark);
+    requestList[1] = NULL;
+    return requestList;
+
+loser:
+    PORT_ArenaRelease(arena, mark);
+    return NULL;
+}
+
+static CERTOCSPRequest *
+ocsp_prepareEmptyOCSPRequest(void)
+{
+    PLArenaPool *arena = NULL;
+    CERTOCSPRequest *request = NULL;
+    ocspTBSRequest *tbsRequest = NULL;
+
+    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    if (arena == NULL) {
+        goto loser;
+    }
+    request = PORT_ArenaZNew(arena, CERTOCSPRequest);
+    if (request == NULL) {
+        goto loser;
+    }
+    request->arena = arena;
+
+    tbsRequest = PORT_ArenaZNew(arena, ocspTBSRequest);
+    if (tbsRequest == NULL) {
+        goto loser;
+    }
+    request->tbsRequest = tbsRequest;
+    /* version 1 is the default, so we need not fill in a version number */
+    return request;
+
+loser:
+    if (arena != NULL) {
+        PORT_FreeArena(arena, PR_FALSE);
+    }
+    return NULL;
+}
+
+CERTOCSPRequest *
+cert_CreateSingleCertOCSPRequest(CERTOCSPCertID *certID, 
+                                 CERTCertificate *singleCert, 
+                                 PRTime time,
+                                 PRBool addServiceLocator,
+                                 CERTCertificate *signerCert)
+{
+    CERTOCSPRequest *request;
+    OCSP_TRACE(("OCSP cert_CreateSingleCertOCSPRequest %s\n", singleCert->subjectName));
+
+    /* XXX Support for signerCert may be implemented later,
+     * see also the comment in CERT_CreateOCSPRequest.
+     */
+    if (signerCert != NULL) {
+        PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+        return NULL;
+    }
+
+    request = ocsp_prepareEmptyOCSPRequest();
+    if (!request)
+        return NULL;
+    /*
+     * Version 1 is the default, so we need not fill in a version number.
+     * Now create the list of single requests, one for each cert.
+     */
+    request->tbsRequest->requestList = 
+        ocsp_CreateRequestFromCert(request->arena, 
+                                   certID,
+                                   singleCert,
+                                   time,
+                                   addServiceLocator);
+    if (request->tbsRequest->requestList == NULL) {
+        PORT_FreeArena(request->arena, PR_FALSE);
+        return NULL;
+    }
+    return request;
+}
+
+/*
+ * FUNCTION: CERT_CreateOCSPRequest
+ *   Creates a CERTOCSPRequest, requesting the status of the certs in 
+ *   the given list.
+ * INPUTS:
+ *   CERTCertList *certList
+ *     A list of certs for which status will be requested.
+ *     Note that all of these certificates should have the same issuer,
+ *     or it's expected the response will be signed by a trusted responder.
+ *     If the certs need to be broken up into multiple requests, that
+ *     must be handled by the caller (and thus by having multiple calls
+ *     to this routine), who knows about where the request(s) are being
+ *     sent and whether there are any trusted responders in place.
+ *   PRTime time
+ *     Indicates the time for which the certificate status is to be 
+ *     determined -- this may be used in the search for the cert's issuer
+ *     but has no effect on the request itself.
+ *   PRBool addServiceLocator
+ *     If true, the Service Locator extension should be added to the
+ *     single request(s) for each cert.
+ *   CERTCertificate *signerCert
+ *     If non-NULL, means sign the request using this cert.  Otherwise,
+ *     do not sign.
+ *     XXX note that request signing is not yet supported; see comment in code
+ * RETURN:
+ *   A pointer to a CERTOCSPRequest structure containing an OCSP request
+ *   for the cert list.  On error, null is returned, with an error set
+ *   indicating the reason.  This is likely SEC_ERROR_UNKNOWN_ISSUER.
+ *   (The issuer is needed to create a request for the certificate.)
+ *   Other errors are low-level problems (no memory, bad database, etc.).
+ */
+CERTOCSPRequest *
+CERT_CreateOCSPRequest(CERTCertList *certList, PRTime time,
+		       PRBool addServiceLocator,
+		       CERTCertificate *signerCert)
+{
+    CERTOCSPRequest *request = NULL;
+
+    if (!certList) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return NULL;
+    }
+    /*
+     * XXX When we are prepared to put signing of requests back in, 
+     * we will need to allocate a signature
+     * structure for the request, fill in the "derCerts" field in it,
+     * save the signerCert there, as well as fill in the "requestorName"
+     * field of the tbsRequest.
+     */
+    if (signerCert != NULL) {
+        PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
+        return NULL;
+    }
+    request = ocsp_prepareEmptyOCSPRequest();
+    if (!request)
+        return NULL;
+    /*
+     * Now create the list of single requests, one for each cert.
+     */
+    request->tbsRequest->requestList = 
+        ocsp_CreateSingleRequestList(request->arena, 
+                                     certList,
+                                     time,
+                                     addServiceLocator);
+    if (request->tbsRequest->requestList == NULL) {
+        PORT_FreeArena(request->arena, PR_FALSE);
+        return NULL;
+    }
+    return request;
+}
+
+/*
+ * FUNCTION: CERT_AddOCSPAcceptableResponses
+ *   Add the AcceptableResponses extension to an OCSP Request.
+ * INPUTS:
+ *   CERTOCSPRequest *request
+ *     The request to which the extension should be added.
+ *   ...
+ *     A list (of one or more) of SECOidTag -- each of the response types
+ *     to be added.  The last OID *must* be SEC_OID_PKIX_OCSP_BASIC_RESPONSE.
+ *     (This marks the end of the list, and it must be specified because a
+ *     client conforming to the OCSP standard is required to handle the basic
+ *     response type.)  The OIDs are not checked in any way.
+ * RETURN:
+ *   SECSuccess if the extension is added; SECFailure if anything goes wrong.
+ *   All errors are internal or low-level problems (e.g. no memory).
+ */
+
+void SetRequestExts(void *object, CERTCertExtension **exts)
+{
+  CERTOCSPRequest *request = (CERTOCSPRequest *)object;
+
+  request->tbsRequest->requestExtensions = exts;
+}
+
+SECStatus
+CERT_AddOCSPAcceptableResponses(CERTOCSPRequest *request,
+				SECOidTag responseType0, ...)
+{
+    void *extHandle;
+    va_list ap;
+    int i, count;
+    SECOidTag responseType;
+    SECOidData *responseOid;
+    SECItem **acceptableResponses = NULL;
+    SECStatus rv = SECFailure;
+
+    extHandle = request->tbsRequest->extensionHandle;
+    if (extHandle == NULL) {
+	extHandle = cert_StartExtensions(request, request->arena, SetRequestExts);
+	if (extHandle == NULL)
+	    goto loser;
+    }
+
+    /* Count number of OIDS going into the extension value. */
+    count = 1;
+    if (responseType0 != SEC_OID_PKIX_OCSP_BASIC_RESPONSE) {
+	va_start(ap, responseType0);
+	do {
+	    count++;
+	    responseType = va_arg(ap, SECOidTag);
+	} while (responseType != SEC_OID_PKIX_OCSP_BASIC_RESPONSE);
+	va_end(ap);
+    }
+
+    acceptableResponses = PORT_NewArray(SECItem *, count + 1);
+    if (acceptableResponses == NULL)
+	goto loser;
+
+    i = 0;
+    responseOid = SECOID_FindOIDByTag(responseType0);
+    acceptableResponses[i++] = &(responseOid->oid);
+    if (count > 1) {
+	va_start(ap, responseType0);
+	for ( ; i < count; i++) {
+	    responseType = va_arg(ap, SECOidTag);
+	    responseOid = SECOID_FindOIDByTag(responseType);
+	    acceptableResponses[i] = &(responseOid->oid);
+	}
+	va_end(ap);
+    }
+    acceptableResponses[i] = NULL;
+
+    rv = CERT_EncodeAndAddExtension(extHandle, SEC_OID_PKIX_OCSP_RESPONSE,
+                                &acceptableResponses, PR_FALSE,
+                                SEC_ASN1_GET(SEC_SequenceOfObjectIDTemplate));
+    if (rv != SECSuccess)
+	goto loser;
+
+    PORT_Free(acceptableResponses);
+    if (request->tbsRequest->extensionHandle == NULL)
+	request->tbsRequest->extensionHandle = extHandle;
+    return SECSuccess;
+
+loser:
+    if (acceptableResponses != NULL)
+	PORT_Free(acceptableResponses);
+    if (extHandle != NULL)
+	(void) CERT_FinishExtensions(extHandle);
+    return rv;
+}
+
+
+/*
+ * FUNCTION: CERT_DestroyOCSPRequest
+ *   Frees an OCSP Request structure.
+ * INPUTS:
+ *   CERTOCSPRequest *request
+ *     Pointer to CERTOCSPRequest to be freed.
+ * RETURN:
+ *   No return value; no errors.
+ */
+void
+CERT_DestroyOCSPRequest(CERTOCSPRequest *request)
+{
+    if (request == NULL)
+	return;
+
+    if (request->tbsRequest != NULL) {
+	if (request->tbsRequest->requestorName != NULL)
+	    CERT_DestroyGeneralNameList(request->tbsRequest->requestorName);
+	if (request->tbsRequest->extensionHandle != NULL)
+	    (void) CERT_FinishExtensions(request->tbsRequest->extensionHandle);
+    }
+
+    if (request->optionalSignature != NULL) {
+	if (request->optionalSignature->cert != NULL)
+	    CERT_DestroyCertificate(request->optionalSignature->cert);
+
+	/*
+	 * XXX Need to free derCerts?  Or do they come out of arena?
+	 * (Currently we never fill in derCerts, which is why the
+	 * answer is not obvious.  Once we do, add any necessary code
+	 * here and remove this comment.)
+	 */
+    }
+
+    /*
+     * We should actually never have a request without an arena,
+     * but check just in case.  (If there isn't one, there is not
+     * much we can do about it...)
+     */
+    PORT_Assert(request->arena != NULL);
+    if (request->arena != NULL)
+	PORT_FreeArena(request->arena, PR_FALSE);
+}
+
+
+/*
+ * RESPONSE SUPPORT FUNCTIONS (encode/create/decode/destroy):
+ */
+
+/*
+ * Helper function for encoding or decoding a ResponderID -- based on the
+ * given type, return the associated template for that choice.
+ */
+static const SEC_ASN1Template *
+ocsp_ResponderIDTemplateByType(CERTOCSPResponderIDType responderIDType)
+{
+    const SEC_ASN1Template *responderIDTemplate;
+
+    switch (responderIDType) {
+	case ocspResponderID_byName:
+	    responderIDTemplate = ocsp_ResponderIDByNameTemplate;
+	    break;
+	case ocspResponderID_byKey:
+	    responderIDTemplate = ocsp_ResponderIDByKeyTemplate;
+	    break;
+	case ocspResponderID_other:
+	default:
+	    PORT_Assert(responderIDType == ocspResponderID_other);
+	    responderIDTemplate = ocsp_ResponderIDOtherTemplate;
+	    break;
+    }
+
+    return responderIDTemplate;
+}
+
+/*
+ * Helper function for encoding or decoding a CertStatus -- based on the
+ * given type, return the associated template for that choice.
+ */
+static const SEC_ASN1Template *
+ocsp_CertStatusTemplateByType(ocspCertStatusType certStatusType)
+{
+    const SEC_ASN1Template *certStatusTemplate;
+
+    switch (certStatusType) {
+	case ocspCertStatus_good:
+	    certStatusTemplate = ocsp_CertStatusGoodTemplate;
+	    break;
+	case ocspCertStatus_revoked:
+	    certStatusTemplate = ocsp_CertStatusRevokedTemplate;
+	    break;
+	case ocspCertStatus_unknown:
+	    certStatusTemplate = ocsp_CertStatusUnknownTemplate;
+	    break;
+	case ocspCertStatus_other:
+	default:
+	    PORT_Assert(certStatusType == ocspCertStatus_other);
+	    certStatusTemplate = ocsp_CertStatusOtherTemplate;
+	    break;
+    }
+
+    return certStatusTemplate;
+}
+
+/*
+ * Helper function for decoding a certStatus -- turn the actual DER tag
+ * into our local translation.
+ */
+static ocspCertStatusType
+ocsp_CertStatusTypeByTag(int derTag)
+{
+    ocspCertStatusType certStatusType;
+
+    switch (derTag) {
+	case 0:
+	    certStatusType = ocspCertStatus_good;
+	    break;
+	case 1:
+	    certStatusType = ocspCertStatus_revoked;
+	    break;
+	case 2:
+	    certStatusType = ocspCertStatus_unknown;
+	    break;
+	default:
+	    certStatusType = ocspCertStatus_other;
+	    break;
+    }
+
+    return certStatusType;
+}
+
+/*
+ * Helper function for decoding SingleResponses -- they each contain
+ * a status which is encoded as CHOICE, which needs to be decoded "by hand".
+ *
+ * Note -- on error, this routine does not release the memory it may
+ * have allocated; it expects its caller to do that.
+ */
+static SECStatus
+ocsp_FinishDecodingSingleResponses(PLArenaPool *reqArena,
+				   CERTOCSPSingleResponse **responses)
+{
+    ocspCertStatus *certStatus;
+    ocspCertStatusType certStatusType;
+    const SEC_ASN1Template *certStatusTemplate;
+    int derTag;
+    int i;
+    SECStatus rv = SECFailure;
+
+    if (!reqArena) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+
+    if (responses == NULL)			/* nothing to do */
+	return SECSuccess;
+
+    for (i = 0; responses[i] != NULL; i++) {
+        SECItem* newStatus;
+	/*
+	 * The following assert points out internal errors (problems in
+	 * the template definitions or in the ASN.1 decoder itself, etc.).
+	 */
+	PORT_Assert(responses[i]->derCertStatus.data != NULL);
+
+	derTag = responses[i]->derCertStatus.data[0] & SEC_ASN1_TAGNUM_MASK;
+	certStatusType = ocsp_CertStatusTypeByTag(derTag);
+	certStatusTemplate = ocsp_CertStatusTemplateByType(certStatusType);
+
+	certStatus = PORT_ArenaZAlloc(reqArena, sizeof(ocspCertStatus));
+	if (certStatus == NULL) {
+	    goto loser;
+	}
+        newStatus = SECITEM_ArenaDupItem(reqArena, &responses[i]->derCertStatus);
+        if (!newStatus) {
+            goto loser;
+        }
+	rv = SEC_QuickDERDecodeItem(reqArena, certStatus, certStatusTemplate,
+				newStatus);
+	if (rv != SECSuccess) {
+	    if (PORT_GetError() == SEC_ERROR_BAD_DER)
+		PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+	    goto loser;
+	}
+
+	certStatus->certStatusType = certStatusType;
+	responses[i]->certStatus = certStatus;
+    }
+
+    return SECSuccess;
+
+loser:
+    return rv;
+}
+
+/*
+ * Helper function for decoding a responderID -- turn the actual DER tag
+ * into our local translation.
+ */
+static CERTOCSPResponderIDType
+ocsp_ResponderIDTypeByTag(int derTag)
+{
+    CERTOCSPResponderIDType responderIDType;
+
+    switch (derTag) {
+	case 1:
+	    responderIDType = ocspResponderID_byName;
+	    break;
+	case 2:
+	    responderIDType = ocspResponderID_byKey;
+	    break;
+	default:
+	    responderIDType = ocspResponderID_other;
+	    break;
+    }
+
+    return responderIDType;
+}
+
+/*
+ * Decode "src" as a BasicOCSPResponse, returning the result.
+ */
+static ocspBasicOCSPResponse *
+ocsp_DecodeBasicOCSPResponse(PLArenaPool *arena, SECItem *src)
+{
+    void *mark;
+    ocspBasicOCSPResponse *basicResponse;
+    ocspResponseData *responseData;
+    ocspResponderID *responderID;
+    CERTOCSPResponderIDType responderIDType;
+    const SEC_ASN1Template *responderIDTemplate;
+    int derTag;
+    SECStatus rv;
+    SECItem newsrc;
+
+    mark = PORT_ArenaMark(arena);
+
+    basicResponse = PORT_ArenaZAlloc(arena, sizeof(ocspBasicOCSPResponse));
+    if (basicResponse == NULL) {
+	goto loser;
+    }
+
+    /* copy the DER into the arena, since Quick DER returns data that points
+       into the DER input, which may get freed by the caller */
+    rv = SECITEM_CopyItem(arena, &newsrc, src);
+    if ( rv != SECSuccess ) {
+	goto loser;
+    }
+
+    rv = SEC_QuickDERDecodeItem(arena, basicResponse,
+			    ocsp_BasicOCSPResponseTemplate, &newsrc);
+    if (rv != SECSuccess) {
+	if (PORT_GetError() == SEC_ERROR_BAD_DER)
+	    PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+	goto loser;
+    }
+
+    responseData = basicResponse->tbsResponseData;
+
+    /*
+     * The following asserts point out internal errors (problems in
+     * the template definitions or in the ASN.1 decoder itself, etc.).
+     */
+    PORT_Assert(responseData != NULL);
+    PORT_Assert(responseData->derResponderID.data != NULL);
+
+    /*
+     * XXX Because responderID is a CHOICE, which is not currently handled
+     * by our ASN.1 decoder, we have to decode it "by hand".
+     */
+    derTag = responseData->derResponderID.data[0] & SEC_ASN1_TAGNUM_MASK;
+    responderIDType = ocsp_ResponderIDTypeByTag(derTag);
+    responderIDTemplate = ocsp_ResponderIDTemplateByType(responderIDType);
+
+    responderID = PORT_ArenaZAlloc(arena, sizeof(ocspResponderID));
+    if (responderID == NULL) {
+	goto loser;
+    }
+
+    rv = SEC_QuickDERDecodeItem(arena, responderID, responderIDTemplate,
+			    &responseData->derResponderID);
+    if (rv != SECSuccess) {
+	if (PORT_GetError() == SEC_ERROR_BAD_DER)
+	    PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+	goto loser;
+    }
+
+    responderID->responderIDType = responderIDType;
+    responseData->responderID = responderID;
+
+    /*
+     * XXX Each SingleResponse also contains a CHOICE, which has to be
+     * fixed up by hand.
+     */
+    rv = ocsp_FinishDecodingSingleResponses(arena, responseData->responses);
+    if (rv != SECSuccess) {
+	goto loser;
+    }
+
+    PORT_ArenaUnmark(arena, mark);
+    return basicResponse;
+
+loser:
+    PORT_ArenaRelease(arena, mark);
+    return NULL;
+}
+
+
+/*
+ * Decode the responseBytes based on the responseType found in "rbytes",
+ * leaving the resulting translated/decoded information in there as well.
+ */
+static SECStatus
+ocsp_DecodeResponseBytes(PLArenaPool *arena, ocspResponseBytes *rbytes)
+{
+    if (rbytes == NULL) {
+	PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE);
+	return SECFailure;
+    }
+
+    rbytes->responseTypeTag = SECOID_FindOIDTag(&rbytes->responseType);
+    switch (rbytes->responseTypeTag) {
+	case SEC_OID_PKIX_OCSP_BASIC_RESPONSE:
+	    {
+		ocspBasicOCSPResponse *basicResponse;
+
+		basicResponse = ocsp_DecodeBasicOCSPResponse(arena,
+							     &rbytes->response);
+		if (basicResponse == NULL)
+		    return SECFailure;
+
+		rbytes->decodedResponse.basic = basicResponse;
+	    }
+	    break;
+
+	/*
+	 * Add new/future response types here.
+	 */
+
+	default:
+	    PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE);
+	    return SECFailure;
+    }
+
+    return SECSuccess;
+}
+
+
+/*
+ * FUNCTION: CERT_DecodeOCSPResponse
+ *   Decode a DER encoded OCSP Response.
+ * INPUTS:
+ *   SECItem *src
+ *     Pointer to a SECItem holding DER encoded OCSP Response.
+ * RETURN:
+ *   Returns a pointer to a CERTOCSPResponse (the decoded OCSP Response);
+ *   the caller is responsible for destroying it.  Or NULL if error (either
+ *   response could not be decoded (SEC_ERROR_OCSP_MALFORMED_RESPONSE),
+ *   it was of an unexpected type (SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE),
+ *   or a low-level or internal error occurred).
+ */
+CERTOCSPResponse *
+CERT_DecodeOCSPResponse(const SECItem *src)
+{
+    PLArenaPool *arena = NULL;
+    CERTOCSPResponse *response = NULL;
+    SECStatus rv = SECFailure;
+    ocspResponseStatus sv;
+    SECItem newSrc;
+
+    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    if (arena == NULL) {
+	goto loser;
+    }
+    response = (CERTOCSPResponse *) PORT_ArenaZAlloc(arena,
+						     sizeof(CERTOCSPResponse));
+    if (response == NULL) {
+	goto loser;
+    }
+    response->arena = arena;
+
+    /* copy the DER into the arena, since Quick DER returns data that points
+       into the DER input, which may get freed by the caller */
+    rv = SECITEM_CopyItem(arena, &newSrc, src);
+    if ( rv != SECSuccess ) {
+	goto loser;
+    }
+
+    rv = SEC_QuickDERDecodeItem(arena, response, ocsp_OCSPResponseTemplate, &newSrc);
+    if (rv != SECSuccess) {
+	if (PORT_GetError() == SEC_ERROR_BAD_DER)
+	    PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+	goto loser;
+    }
+
+    sv = (ocspResponseStatus) DER_GetInteger(&response->responseStatus);
+    response->statusValue = sv;
+    if (sv != ocspResponse_successful) {
+	/*
+	 * If the response status is anything but successful, then we
+	 * are all done with decoding; the status is all there is.
+	 */
+	return response;
+    }
+
+    /*
+     * A successful response contains much more information, still encoded.
+     * Now we need to decode that.
+     */
+    rv = ocsp_DecodeResponseBytes(arena, response->responseBytes);
+    if (rv != SECSuccess) {
+	goto loser;
+    }
+
+    return response;
+
+loser:
+    if (arena != NULL) {
+	PORT_FreeArena(arena, PR_FALSE);
+    }
+    return NULL;
+}
+
+/*
+ * The way an OCSPResponse is defined, there are many levels to descend
+ * before getting to the actual response information.  And along the way
+ * we need to check that the response *type* is recognizable, which for
+ * now means that it is a BasicOCSPResponse, because that is the only
+ * type currently defined.  Rather than force all routines to perform
+ * a bunch of sanity checking every time they want to work on a response,
+ * this function isolates that and gives back the interesting part.
+ * Note that no copying is done, this just returns a pointer into the
+ * substructure of the response which is passed in.
+ *
+ * XXX This routine only works when a valid response structure is passed
+ * into it; this is checked with many assertions.  Assuming the response
+ * was creating by decoding, it wouldn't make it this far without being
+ * okay.  That is a sufficient assumption since the entire OCSP interface
+ * is only used internally.  When this interface is officially exported,
+ * each assertion below will need to be followed-up with setting an error
+ * and returning (null).
+ *
+ * FUNCTION: ocsp_GetResponseData
+ *   Returns ocspResponseData structure and a pointer to tbs response
+ *   data DER from a valid ocsp response. 
+ * INPUTS:
+ *   CERTOCSPResponse *response
+ *     structure of a valid ocsp response
+ * RETURN:
+ *   Returns a pointer to ocspResponseData structure: decoded OCSP response
+ *   data, and a pointer(tbsResponseDataDER) to its undecoded data DER.
+ */
+ocspResponseData *
+ocsp_GetResponseData(CERTOCSPResponse *response, SECItem **tbsResponseDataDER)
+{
+    ocspBasicOCSPResponse *basic;
+    ocspResponseData *responseData;
+
+    PORT_Assert(response != NULL);
+
+    PORT_Assert(response->responseBytes != NULL);
+
+    PORT_Assert(response->responseBytes->responseTypeTag
+		== SEC_OID_PKIX_OCSP_BASIC_RESPONSE);
+
+    basic = response->responseBytes->decodedResponse.basic;
+    PORT_Assert(basic != NULL);
+
+    responseData = basic->tbsResponseData;
+    PORT_Assert(responseData != NULL);
+
+    if (tbsResponseDataDER) {
+        *tbsResponseDataDER = &basic->tbsResponseDataDER;
+
+        PORT_Assert((*tbsResponseDataDER)->data != NULL);
+        PORT_Assert((*tbsResponseDataDER)->len != 0);
+    }
+
+    return responseData;
+}
+
+/*
+ * Much like the routine above, except it returns the response signature.
+ * Again, no copy is done.
+ */
+ocspSignature *
+ocsp_GetResponseSignature(CERTOCSPResponse *response)
+{
+    ocspBasicOCSPResponse *basic;
+
+    PORT_Assert(response != NULL);
+    if (NULL == response->responseBytes) {
+        return NULL;
+    }
+    if (response->responseBytes->responseTypeTag
+        != SEC_OID_PKIX_OCSP_BASIC_RESPONSE) {
+        return NULL;
+    }
+    basic = response->responseBytes->decodedResponse.basic;
+    PORT_Assert(basic != NULL);
+
+    return &(basic->responseSignature);
+}
+
+
+/*
+ * FUNCTION: CERT_DestroyOCSPResponse
+ *   Frees an OCSP Response structure.
+ * INPUTS:
+ *   CERTOCSPResponse *request
+ *     Pointer to CERTOCSPResponse to be freed.
+ * RETURN:
+ *   No return value; no errors.
+ */
+void
+CERT_DestroyOCSPResponse(CERTOCSPResponse *response)
+{
+    if (response != NULL) {
+	ocspSignature *signature = ocsp_GetResponseSignature(response);
+	if (signature && signature->cert != NULL)
+	    CERT_DestroyCertificate(signature->cert);
+
+	/*
+	 * We should actually never have a response without an arena,
+	 * but check just in case.  (If there isn't one, there is not
+	 * much we can do about it...)
+	 */
+	PORT_Assert(response->arena != NULL);
+	if (response->arena != NULL) {
+	    PORT_FreeArena(response->arena, PR_FALSE);
+	}
+    }
+}
+
+
+/*
+ * OVERALL OCSP CLIENT SUPPORT (make and send a request, verify a response):
+ */
+
+
+/*
+ * Pick apart a URL, saving the important things in the passed-in pointers.
+ *
+ * We expect to find "http://<hostname>[:<port>]/[path]", though we will
+ * tolerate that final slash character missing, as well as beginning and
+ * trailing whitespace, and any-case-characters for "http".  All of that
+ * tolerance is what complicates this routine.  What we want is just to
+ * pick out the hostname, the port, and the path.
+ *
+ * On a successful return, the caller will need to free the output pieces
+ * of hostname and path, which are copies of the values found in the url.
+ */
+static SECStatus
+ocsp_ParseURL(const char *url, char **pHostname, PRUint16 *pPort, char **pPath)
+{
+    unsigned short port = 80;		/* default, in case not in url */
+    char *hostname = NULL;
+    char *path = NULL;
+    const char *save;
+    char c;
+    int len;
+
+    if (url == NULL)
+	goto loser;
+
+    /*
+     * Skip beginning whitespace.
+     */
+    c = *url;
+    while ((c == ' ' || c == '\t') && c != '\0') {
+	url++;
+	c = *url;
+    }
+    if (c == '\0')
+	goto loser;
+
+    /*
+     * Confirm, then skip, protocol.  (Since we only know how to do http,
+     * that is all we will accept).
+     */
+    if (PORT_Strncasecmp(url, "http://", 7) != 0)
+	goto loser;
+    url += 7;
+
+    /*
+     * Whatever comes next is the hostname (or host IP address).  We just
+     * save it aside and then search for its end so we can determine its
+     * length and copy it.
+     *
+     * XXX Note that because we treat a ':' as a terminator character
+     * (and below, we expect that to mean there is a port specification
+     * immediately following), we will not handle IPv6 addresses.  That is
+     * apparently an acceptable limitation, for the time being.  Some day,
+     * when there is a clear way to specify a URL with an IPv6 address that
+     * can be parsed unambiguously, this code should be made to do that.
+     */
+    save = url;
+    c = *url;
+    while (c != '/' && c != ':' && c != '\0' && c != ' ' && c != '\t') {
+	url++;
+	c = *url;
+    }
+    len = url - save;
+    hostname = PORT_Alloc(len + 1);
+    if (hostname == NULL)
+	goto loser;
+    PORT_Memcpy(hostname, save, len);
+    hostname[len] = '\0';
+
+    /*
+     * Now we figure out if there was a port specified or not.
+     * If so, we need to parse it (as a number) and skip it.
+     */
+    if (c == ':') {
+	url++;
+	port = (unsigned short) PORT_Atoi(url);
+	c = *url;
+	while (c != '/' && c != '\0' && c != ' ' && c != '\t') {
+	    if (c < '0' || c > '9')
+		goto loser;
+	    url++;
+	    c = *url;
+	}
+    }
+
+    /*
+     * Last thing to find is a path.  There *should* be a slash,
+     * if nothing else -- but if there is not we provide one.
+     */
+    if (c == '/') {
+	save = url;
+	while (c != '\0' && c != ' ' && c != '\t') {
+	    url++;
+	    c = *url;
+	}
+	len = url - save;
+	path = PORT_Alloc(len + 1);
+	if (path == NULL)
+	    goto loser;
+	PORT_Memcpy(path, save, len);
+	path[len] = '\0';
+    } else {
+	path = PORT_Strdup("/");
+	if (path == NULL)
+	    goto loser;
+    }
+
+    *pHostname = hostname;
+    *pPort = port;
+    *pPath = path;
+    return SECSuccess;
+
+loser:
+    if (hostname != NULL)
+	PORT_Free(hostname);
+    PORT_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION);
+    return SECFailure;
+}
+
+/*
+ * Open a socket to the specified host on the specified port, and return it.
+ * The host is either a hostname or an IP address.
+ */
+static PRFileDesc *
+ocsp_ConnectToHost(const char *host, PRUint16 port)
+{
+    PRFileDesc *sock = NULL;
+    PRIntervalTime timeout;
+    PRNetAddr addr;
+    char *netdbbuf = NULL;
+
+    sock = PR_NewTCPSocket();
+    if (sock == NULL)
+	goto loser;
+
+    /* XXX Some day need a way to set (and get?) the following value */
+    timeout = PR_SecondsToInterval(30);
+
+    /*
+     * If the following converts an IP address string in "dot notation"
+     * into a PRNetAddr.  If it fails, we assume that is because we do not
+     * have such an address, but instead a host *name*.  In that case we
+     * then lookup the host by name.  Using the NSPR function this way
+     * means we do not have to have our own logic for distinguishing a
+     * valid numerical IP address from a hostname.
+     */
+    if (PR_StringToNetAddr(host, &addr) != PR_SUCCESS) {
+	PRIntn hostIndex;
+	PRHostEnt hostEntry;
+
+	netdbbuf = PORT_Alloc(PR_NETDB_BUF_SIZE);
+	if (netdbbuf == NULL)
+	    goto loser;
+
+	if (PR_GetHostByName(host, netdbbuf, PR_NETDB_BUF_SIZE,
+			     &hostEntry) != PR_SUCCESS)
+	    goto loser;
+
+	hostIndex = 0;
+	do {
+	    hostIndex = PR_EnumerateHostEnt(hostIndex, &hostEntry, port, &addr);
+	    if (hostIndex <= 0)
+		goto loser;
+	} while (PR_Connect(sock, &addr, timeout) != PR_SUCCESS);
+
+	PORT_Free(netdbbuf);
+    } else {
+	/*
+	 * First put the port into the address, then connect.
+	 */
+	if (PR_InitializeNetAddr(PR_IpAddrNull, port, &addr) != PR_SUCCESS)
+	    goto loser;
+	if (PR_Connect(sock, &addr, timeout) != PR_SUCCESS)
+	    goto loser;
+    }
+
+    return sock;
+
+loser:
+    if (sock != NULL)
+	PR_Close(sock);
+    if (netdbbuf != NULL)
+	PORT_Free(netdbbuf);
+    return NULL;
+}
+
+/*
+ * Sends an encoded OCSP request to the server identified by "location",
+ * and returns the socket on which it was sent (so can listen for the reply).
+ * "location" is expected to be a valid URL -- an error parsing it produces
+ * SEC_ERROR_CERT_BAD_ACCESS_LOCATION.  Other errors are likely problems
+ * connecting to it, or writing to it, or allocating memory, and the low-level
+ * errors appropriate to the problem will be set.
+ * if (encodedRequest == NULL)
+ *   then location MUST already include the full request,
+ *        including base64 and urlencode,
+ *        and the request will be sent with GET
+ * if (encodedRequest != NULL)
+ *   then the request will be sent with POST
+ */
+static PRFileDesc *
+ocsp_SendEncodedRequest(const char *location, const SECItem *encodedRequest)
+{
+    char *hostname = NULL;
+    char *path = NULL;
+    PRUint16 port;
+    SECStatus rv;
+    PRFileDesc *sock = NULL;
+    PRFileDesc *returnSock = NULL;
+    char *header = NULL;
+    char portstr[16];
+
+    /*
+     * Take apart the location, getting the hostname, port, and path.
+     */
+    rv = ocsp_ParseURL(location, &hostname, &port, &path);
+    if (rv != SECSuccess)
+	goto loser;
+
+    PORT_Assert(hostname != NULL);
+    PORT_Assert(path != NULL);
+
+    sock = ocsp_ConnectToHost(hostname, port);
+    if (sock == NULL)
+	goto loser;
+
+    portstr[0] = '\0';
+    if (port != 80) {
+        PR_snprintf(portstr, sizeof(portstr), ":%d", port);
+    }
+
+    if (!encodedRequest) {
+      header = PR_smprintf("GET %s HTTP/1.0\r\n"
+                          "Host: %s%s\r\n\r\n",
+                          path, hostname, portstr);
+      if (header == NULL)
+          goto loser;
+
+      /*
+      * The NSPR documentation promises that if it can, it will write the full
+      * amount; this will not return a partial value expecting us to loop.
+      */
+      if (PR_Write(sock, header, (PRInt32) PORT_Strlen(header)) < 0)
+          goto loser;
+    }
+    else {
+      header = PR_smprintf("POST %s HTTP/1.0\r\n"
+                          "Host: %s%s\r\n"
+                          "Content-Type: application/ocsp-request\r\n"
+                          "Content-Length: %u\r\n\r\n",
+                          path, hostname, portstr, encodedRequest->len);
+      if (header == NULL)
+          goto loser;
+
+      /*
+      * The NSPR documentation promises that if it can, it will write the full
+      * amount; this will not return a partial value expecting us to loop.
+      */
+      if (PR_Write(sock, header, (PRInt32) PORT_Strlen(header)) < 0)
+          goto loser;
+
+      if (PR_Write(sock, encodedRequest->data,
+                  (PRInt32) encodedRequest->len) < 0)
+          goto loser;
+    }
+
+    returnSock = sock;
+    sock = NULL;
+
+loser:
+    if (header != NULL)
+	PORT_Free(header);
+    if (sock != NULL)
+	PR_Close(sock);
+    if (path != NULL)
+	PORT_Free(path);
+    if (hostname != NULL)
+	PORT_Free(hostname);
+
+    return returnSock;
+}
+
+/*
+ * Read from "fd" into "buf" -- expect/attempt to read a given number of bytes
+ * Obviously, stop if hit end-of-stream. Timeout is passed in.
+ */
+
+static int
+ocsp_read(PRFileDesc *fd, char *buf, int toread, PRIntervalTime timeout)
+{
+    int total = 0;
+
+    while (total < toread)
+    {
+        PRInt32 got;
+
+        got = PR_Recv(fd, buf + total, (PRInt32) (toread - total), 0, timeout);
+        if (got < 0)
+        {
+            if (0 == total)
+            {
+                total = -1; /* report the error if we didn't read anything yet */
+            }
+            break;
+        }
+        else
+        if (got == 0)
+        {			/* EOS */
+            break;
+        }
+
+        total += got;
+    }
+
+    return total;
+}
+
+#define OCSP_BUFSIZE 1024
+
+#define AbortHttpDecode(error) \
+{ \
+        if (inBuffer) \
+            PORT_Free(inBuffer); \
+        PORT_SetError(error); \
+        return NULL; \
+}
+
+
+/*
+ * Reads on the given socket and returns an encoded response when received.
+ * Properly formatted HTTP/1.0 response headers are expected to be read
+ * from the socket, preceding a binary-encoded OCSP response.  Problems
+ * with parsing cause the error SEC_ERROR_OCSP_BAD_HTTP_RESPONSE to be
+ * set; any other problems are likely low-level i/o or memory allocation
+ * errors.
+ */
+static SECItem *
+ocsp_GetEncodedResponse(PLArenaPool *arena, PRFileDesc *sock)
+{
+    /* first read HTTP status line and headers */
+
+    char* inBuffer = NULL;
+    PRInt32 offset = 0;
+    PRInt32 inBufsize = 0;
+    const PRInt32 bufSizeIncrement = OCSP_BUFSIZE; /* 1 KB at a time */
+    const PRInt32 maxBufSize = 8 * bufSizeIncrement ; /* 8 KB max */
+    const char* CRLF = "\r\n";
+    const PRInt32 CRLFlen = strlen(CRLF);
+    const char* headerEndMark = "\r\n\r\n";
+    const PRInt32 markLen = strlen(headerEndMark);
+    const PRIntervalTime ocsptimeout =
+        PR_SecondsToInterval(30); /* hardcoded to 30s for now */
+    char* headerEnd = NULL;
+    PRBool EOS = PR_FALSE;
+    const char* httpprotocol = "HTTP/";
+    const PRInt32 httplen = strlen(httpprotocol);
+    const char* httpcode = NULL;
+    const char* contenttype = NULL;
+    PRInt32 contentlength = 0;
+    PRInt32 bytesRead = 0;
+    char* statusLineEnd = NULL;
+    char* space = NULL;
+    char* nextHeader = NULL;
+    SECItem* result = NULL;
+
+    /* read up to at least the end of the HTTP headers */
+    do
+    {
+        inBufsize += bufSizeIncrement;
+        inBuffer = PORT_Realloc(inBuffer, inBufsize+1);
+        if (NULL == inBuffer)
+        {
+            AbortHttpDecode(SEC_ERROR_NO_MEMORY);
+        }
+        bytesRead = ocsp_read(sock, inBuffer + offset, bufSizeIncrement,
+            ocsptimeout);
+        if (bytesRead > 0)
+        {
+            PRInt32 searchOffset = (offset - markLen) >0 ? offset-markLen : 0;
+            offset += bytesRead;
+            *(inBuffer + offset) = '\0'; /* NULL termination */
+            headerEnd = strstr((const char*)inBuffer + searchOffset, headerEndMark);
+            if (bytesRead < bufSizeIncrement)
+            {
+                /* we read less data than requested, therefore we are at
+                   EOS or there was a read error */
+                EOS = PR_TRUE;
+            }
+        }
+        else
+        {
+            /* recv error or EOS */
+            EOS = PR_TRUE;
+        }
+    } while ( (!headerEnd) && (PR_FALSE == EOS) &&
+              (inBufsize < maxBufSize) );
+
+    if (!headerEnd)
+    {
+        AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+    }
+
+    /* parse the HTTP status line  */
+    statusLineEnd = strstr((const char*)inBuffer, CRLF);
+    if (!statusLineEnd)
+    {
+        AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+    }
+    *statusLineEnd = '\0';
+
+    /* check for HTTP/ response */
+    space = strchr((const char*)inBuffer, ' ');
+    if (!space || PORT_Strncasecmp((const char*)inBuffer, httpprotocol, httplen) != 0 )
+    {
+        AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+    }
+
+    /* check the HTTP status code of 200 */
+    httpcode = space +1;
+    space = strchr(httpcode, ' ');
+    if (!space)
+    {
+        AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+    }
+    *space = 0;
+    if (0 != strcmp(httpcode, "200"))
+    {
+        AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+    }
+
+    /* parse the HTTP headers in the buffer . We only care about
+       content-type and content-length
+    */
+
+    nextHeader = statusLineEnd + CRLFlen;
+    *headerEnd = '\0'; /* terminate */
+    do
+    {
+        char* thisHeaderEnd = NULL;
+        char* value = NULL;
+        char* colon = strchr(nextHeader, ':');
+        
+        if (!colon)
+        {
+            AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+        }
+
+        *colon = '\0';
+        value = colon + 1;
+
+        /* jpierre - note : the following code will only handle the basic form
+           of HTTP/1.0 response headers, of the form "name: value" . Headers
+           split among multiple lines are not supported. This is not common
+           and should not be an issue, but it could become one in the
+           future */
+
+        if (*value != ' ')
+        {
+            AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+        }
+
+        value++;
+        thisHeaderEnd  = strstr(value, CRLF);
+        if (thisHeaderEnd )
+        {
+            *thisHeaderEnd  = '\0';
+        }
+
+        if (0 == PORT_Strcasecmp(nextHeader, "content-type"))
+        {
+            contenttype = value;
+        }
+        else
+        if (0 == PORT_Strcasecmp(nextHeader, "content-length"))
+        {
+            contentlength = atoi(value);
+        }
+
+        if (thisHeaderEnd )
+        {
+            nextHeader = thisHeaderEnd + CRLFlen;
+        }
+        else
+        {
+            nextHeader = NULL;
+        }
+
+    } while (nextHeader && (nextHeader < (headerEnd + CRLFlen) ) );
+
+    /* check content-type */
+    if (!contenttype ||
+        (0 != PORT_Strcasecmp(contenttype, "application/ocsp-response")) )
+    {
+        AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+    }
+
+    /* read the body of the OCSP response */
+    offset = offset - (PRInt32) (headerEnd - (const char*)inBuffer) - markLen;
+    if (offset)
+    {
+        /* move all data to the beginning of the buffer */
+        PORT_Memmove(inBuffer, headerEnd + markLen, offset);
+    }
+
+    /* resize buffer to only what's needed to hold the current response */
+    inBufsize = (1 + (offset-1) / bufSizeIncrement ) * bufSizeIncrement ;
+
+    while ( (PR_FALSE == EOS) &&
+            ( (contentlength == 0) || (offset < contentlength) ) &&
+            (inBufsize < maxBufSize)
+            )
+    {
+        /* we still need to receive more body data */
+        inBufsize += bufSizeIncrement;
+        inBuffer = PORT_Realloc(inBuffer, inBufsize+1);
+        if (NULL == inBuffer)
+        {
+            AbortHttpDecode(SEC_ERROR_NO_MEMORY);
+        }
+        bytesRead = ocsp_read(sock, inBuffer + offset, bufSizeIncrement,
+                              ocsptimeout);
+        if (bytesRead > 0)
+        {
+            offset += bytesRead;
+            if (bytesRead < bufSizeIncrement)
+            {
+                /* we read less data than requested, therefore we are at
+                   EOS or there was a read error */
+                EOS = PR_TRUE;
+            }
+        }
+        else
+        {
+            /* recv error or EOS */
+            EOS = PR_TRUE;
+        }
+    }
+
+    if (0 == offset)
+    {
+        AbortHttpDecode(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+    }
+
+    /*
+     * Now allocate the item to hold the data.
+     */
+    result = SECITEM_AllocItem(arena, NULL, offset);
+    if (NULL == result)
+    {
+        AbortHttpDecode(SEC_ERROR_NO_MEMORY);
+    }
+
+    /*
+     * And copy the data left in the buffer.
+    */
+    PORT_Memcpy(result->data, inBuffer, offset);
+
+    /* and free the temporary buffer */
+    PORT_Free(inBuffer);
+    return result;
+}
+
+SECStatus
+CERT_ParseURL(const char *url, char **pHostname, PRUint16 *pPort, char **pPath)
+{
+    return ocsp_ParseURL(url, pHostname, pPort, pPath);
+}
+
+/*
+ * Limit the size of http responses we are willing to accept.
+ */
+#define MAX_WANTED_OCSP_RESPONSE_LEN 64*1024
+
+/* if (encodedRequest == NULL)
+ *   then location MUST already include the full request,
+ *        including base64 and urlencode,
+ *        and the request will be sent with GET
+ * if (encodedRequest != NULL)
+ *   then the request will be sent with POST
+ */
+static SECItem *
+fetchOcspHttpClientV1(PLArenaPool *arena, 
+                      const SEC_HttpClientFcnV1 *hcv1, 
+                      const char *location, 
+                      const SECItem *encodedRequest)
+{
+    char *hostname = NULL;
+    char *path = NULL;
+    PRUint16 port;
+    SECItem *encodedResponse = NULL;
+    SEC_HTTP_SERVER_SESSION pServerSession = NULL;
+    SEC_HTTP_REQUEST_SESSION pRequestSession = NULL;
+    PRUint16 myHttpResponseCode;
+    const char *myHttpResponseData;
+    PRUint32 myHttpResponseDataLen;
+
+    if (ocsp_ParseURL(location, &hostname, &port, &path) == SECFailure) {
+        PORT_SetError(SEC_ERROR_OCSP_MALFORMED_REQUEST);
+        goto loser;
+    }
+    
+    PORT_Assert(hostname != NULL);
+    PORT_Assert(path != NULL);
+
+    if ((*hcv1->createSessionFcn)(
+            hostname, 
+            port, 
+            &pServerSession) != SECSuccess) {
+        PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR);
+        goto loser;
+    }
+
+    /* We use a non-zero timeout, which means:
+       - the client will use blocking I/O
+       - TryFcn will not return WOULD_BLOCK nor a poll descriptor
+       - it's sufficient to call TryFcn once
+       No lock for accessing OCSP_Global.timeoutSeconds, bug 406120
+    */
+
+    if ((*hcv1->createFcn)(
+            pServerSession,
+            "http",
+            path,
+            encodedRequest ? "POST" : "GET",
+            PR_TicksPerSecond() * OCSP_Global.timeoutSeconds,
+            &pRequestSession) != SECSuccess) {
+        PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR);
+        goto loser;
+    }
+
+    if (encodedRequest &&
+        (*hcv1->setPostDataFcn)(
+            pRequestSession, 
+            (char*)encodedRequest->data,
+            encodedRequest->len,
+            "application/ocsp-request") != SECSuccess) {
+        PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR);
+        goto loser;
+    }
+
+    /* we don't want result objects larger than this: */
+    myHttpResponseDataLen = MAX_WANTED_OCSP_RESPONSE_LEN;
+
+    OCSP_TRACE(("OCSP trySendAndReceive %s\n", location));
+
+    if ((*hcv1->trySendAndReceiveFcn)(
+            pRequestSession, 
+            NULL,
+            &myHttpResponseCode,
+            NULL,
+            NULL,
+            &myHttpResponseData,
+            &myHttpResponseDataLen) != SECSuccess) {
+        PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR);
+        goto loser;
+    }
+
+    OCSP_TRACE(("OCSP trySendAndReceive result http %d\n", myHttpResponseCode));
+
+    if (myHttpResponseCode != 200) {
+        PORT_SetError(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
+        goto loser;
+    }
+
+    encodedResponse = SECITEM_AllocItem(arena, NULL, myHttpResponseDataLen);
+
+    if (!encodedResponse) {
+        PORT_SetError(SEC_ERROR_NO_MEMORY);
+        goto loser;
+    }
+
+    PORT_Memcpy(encodedResponse->data, myHttpResponseData, myHttpResponseDataLen);
+
+loser:
+    if (pRequestSession != NULL) 
+        (*hcv1->freeFcn)(pRequestSession);
+    if (pServerSession != NULL)
+        (*hcv1->freeSessionFcn)(pServerSession);
+    if (path != NULL)
+	PORT_Free(path);
+    if (hostname != NULL)
+	PORT_Free(hostname);
+    
+    return encodedResponse;
+}
+
+/*
+ * FUNCTION: CERT_GetEncodedOCSPResponseByMethod
+ *   Creates and sends a request to an OCSP responder, then reads and
+ *   returns the (encoded) response.
+ * INPUTS:
+ *   PLArenaPool *arena
+ *     Pointer to arena from which return value will be allocated.
+ *     If NULL, result will be allocated from the heap (and thus should
+ *     be freed via SECITEM_FreeItem).
+ *   CERTCertList *certList
+ *     A list of certs for which status will be requested.
+ *     Note that all of these certificates should have the same issuer,
+ *     or it's expected the response will be signed by a trusted responder.
+ *     If the certs need to be broken up into multiple requests, that
+ *     must be handled by the caller (and thus by having multiple calls
+ *     to this routine), who knows about where the request(s) are being
+ *     sent and whether there are any trusted responders in place.
+ *   const char *location
+ *     The location of the OCSP responder (a URL).
+ *   const char *method
+ *     The protocol method used when retrieving the OCSP response.
+ *     Currently support: "GET" (http GET) and "POST" (http POST).
+ *     Additionals methods for http or other protocols might be added
+ *     in the future.
+ *   PRTime time
+ *     Indicates the time for which the certificate status is to be 
+ *     determined -- this may be used in the search for the cert's issuer
+ *     but has no other bearing on the operation.
+ *   PRBool addServiceLocator
+ *     If true, the Service Locator extension should be added to the
+ *     single request(s) for each cert.
+ *   CERTCertificate *signerCert
+ *     If non-NULL, means sign the request using this cert.  Otherwise,
+ *     do not sign.
+ *   void *pwArg
+ *     Pointer to argument for password prompting, if needed.  (Definitely
+ *     not needed if not signing.)
+ * OUTPUTS:
+ *   CERTOCSPRequest **pRequest
+ *     Pointer in which to store the OCSP request created for the given
+ *     list of certificates.  It is only filled in if the entire operation
+ *     is successful and the pointer is not null -- and in that case the
+ *     caller is then reponsible for destroying it.
+ * RETURN:
+ *   Returns a pointer to the SECItem holding the response.
+ *   On error, returns null with error set describing the reason:
+ *	SEC_ERROR_UNKNOWN_ISSUER
+ *	SEC_ERROR_CERT_BAD_ACCESS_LOCATION
+ *	SEC_ERROR_OCSP_BAD_HTTP_RESPONSE
+ *   Other errors are low-level problems (no memory, bad database, etc.).
+ */
+SECItem *
+CERT_GetEncodedOCSPResponseByMethod(PLArenaPool *arena, CERTCertList *certList,
+				    const char *location, const char *method,
+				    PRTime time, PRBool addServiceLocator,
+				    CERTCertificate *signerCert, void *pwArg,
+				    CERTOCSPRequest **pRequest)
+{
+    CERTOCSPRequest *request;
+    request = CERT_CreateOCSPRequest(certList, time, addServiceLocator,
+                                     signerCert);
+    if (!request)
+        return NULL;
+    return ocsp_GetEncodedOCSPResponseFromRequest(arena, request, location,
+                                                  method, time, addServiceLocator,
+                                                  pwArg, pRequest);
+}
+
+/*
+ * FUNCTION: CERT_GetEncodedOCSPResponse
+ *   Creates and sends a request to an OCSP responder, then reads and
+ *   returns the (encoded) response.
+ *
+ * This is a legacy API that behaves identically to
+ * CERT_GetEncodedOCSPResponseByMethod using the "POST" method.
+ */
+SECItem *
+CERT_GetEncodedOCSPResponse(PLArenaPool *arena, CERTCertList *certList,
+			    const char *location, PRTime time,
+			    PRBool addServiceLocator,
+			    CERTCertificate *signerCert, void *pwArg,
+			    CERTOCSPRequest **pRequest)
+{
+    return CERT_GetEncodedOCSPResponseByMethod(arena, certList, location,
+					       "POST", time, addServiceLocator,
+					       signerCert, pwArg, pRequest);
+}
+
+/* URL encode a buffer that consists of base64-characters, only,
+ * which means we can use a simple encoding logic.
+ * 
+ * No output buffer size checking is performed.
+ * You should call the function twice, to calculate the required buffer size.
+ * 
+ * If the outpufBuf parameter is NULL, the function will calculate the 
+ * required size, including the trailing zero termination char.
+ * 
+ * The function returns the number of bytes calculated or produced.
+ */
+size_t
+ocsp_UrlEncodeBase64Buf(const char *base64Buf, char *outputBuf)
+{
+    const char *walkInput = NULL;
+    char *walkOutput = outputBuf;
+    size_t count = 0;
+    
+    for (walkInput=base64Buf; *walkInput; ++walkInput) {
+	char c = *walkInput;
+	if (isspace(c))
+	    continue;
+	switch (c) {
+	  case '+':
+	    if (outputBuf) {
+		strcpy(walkOutput, "%2B");
+		walkOutput += 3;
+	    }
+	    count += 3;
+	    break;
+	  case '/':
+	    if (outputBuf) {
+		strcpy(walkOutput, "%2F");
+		walkOutput += 3;
+	    }
+	    count += 3;
+	    break;
+	  case '=':
+	    if (outputBuf) {
+		strcpy(walkOutput, "%3D");
+		walkOutput += 3;
+	    }
+	    count += 3;
+	    break;
+	  default:
+	    if (outputBuf) {
+		*walkOutput = *walkInput;
+		++walkOutput;
+	    }
+	    ++count;
+	    break;
+	}
+    }
+    if (outputBuf) {
+	*walkOutput = 0;
+    }
+    ++count;
+    return count;
+}
+
+enum { max_get_request_size = 255 }; /* defined by RFC2560 */
+
+static SECItem *
+cert_GetOCSPResponse(PLArenaPool *arena, const char *location, 
+                     const SECItem *encodedRequest);
+
+static SECItem *
+ocsp_GetEncodedOCSPResponseFromRequest(PLArenaPool *arena,
+                                       CERTOCSPRequest *request,
+                                       const char *location,
+				       const char *method,
+				       PRTime time,
+                                       PRBool addServiceLocator,
+                                       void *pwArg,
+                                       CERTOCSPRequest **pRequest)
+{
+    SECItem *encodedRequest = NULL;
+    SECItem *encodedResponse = NULL;
+    SECStatus rv;
+
+    if (!location || !*location) /* location should be at least one byte */
+        goto loser;
+
+    rv = CERT_AddOCSPAcceptableResponses(request,
+					 SEC_OID_PKIX_OCSP_BASIC_RESPONSE);
+    if (rv != SECSuccess)
+	goto loser;
+
+    encodedRequest = CERT_EncodeOCSPRequest(NULL, request, pwArg);
+    if (encodedRequest == NULL)
+	goto loser;
+
+    if (!strcmp(method, "GET")) {
+        encodedResponse = cert_GetOCSPResponse(arena, location, encodedRequest);
+    }
+    else if (!strcmp(method, "POST")) {
+        encodedResponse = CERT_PostOCSPRequest(arena, location, encodedRequest);
+    }
+    else {
+	goto loser;
+    }
+
+    if (encodedResponse != NULL && pRequest != NULL) {
+	*pRequest = request;
+	request = NULL;			/* avoid destroying below */
+    }
+
+loser:
+    if (request != NULL)
+	CERT_DestroyOCSPRequest(request);
+    if (encodedRequest != NULL)
+	SECITEM_FreeItem(encodedRequest, PR_TRUE);
+    return encodedResponse;
+}
+
+static SECItem *
+cert_FetchOCSPResponse(PLArenaPool *arena,  const char *location, 
+                       const SECItem *encodedRequest);
+
+/* using HTTP GET method */
+static SECItem *
+cert_GetOCSPResponse(PLArenaPool *arena, const char *location, 
+                     const SECItem *encodedRequest)
+{
+    char *walkOutput = NULL;
+    char *fullGetPath = NULL;
+    size_t pathLength;
+    PRInt32 urlEncodedBufLength;
+    size_t base64size;
+    char b64ReqBuf[max_get_request_size+1];
+    size_t slashLengthIfNeeded = 0;
+    size_t getURLLength;
+    SECItem *item;
+
+    if (!location || !*location) {
+	return NULL;
+    }
+    
+    pathLength = strlen(location);
+    if (location[pathLength-1] != '/') {
+	slashLengthIfNeeded = 1;
+    }
+    
+    /* Calculation as documented by PL_Base64Encode function.
+     * Use integer conversion to avoid having to use function ceil().
+     */
+    base64size = (((encodedRequest->len +2)/3) * 4);
+    if (base64size > max_get_request_size) {
+	return NULL;
+    }
+    memset(b64ReqBuf, 0, sizeof(b64ReqBuf));
+    PL_Base64Encode((const char*)encodedRequest->data, encodedRequest->len,
+		    b64ReqBuf);
+
+    urlEncodedBufLength = ocsp_UrlEncodeBase64Buf(b64ReqBuf, NULL);
+    getURLLength = pathLength + urlEncodedBufLength + slashLengthIfNeeded;
+    
+    /* urlEncodedBufLength already contains room for the zero terminator.
+     * Add another if we must add the '/' char.
+     */
+    if (arena) {
+        fullGetPath = (char*)PORT_ArenaAlloc(arena, getURLLength);
+    } else {
+        fullGetPath = (char*)PORT_Alloc(getURLLength);
+    }
+    if (!fullGetPath) {
+	return NULL;
+    }
+ 
+    strcpy(fullGetPath, location);
+    walkOutput = fullGetPath + pathLength;
+    
+    if (walkOutput > fullGetPath && slashLengthIfNeeded) {
+        strcpy(walkOutput, "/");
+        ++walkOutput;
+    }
+    ocsp_UrlEncodeBase64Buf(b64ReqBuf, walkOutput);
+
+    item = cert_FetchOCSPResponse(arena, fullGetPath, NULL);
+    if (!arena) {
+	PORT_Free(fullGetPath);
+    }
+    return item;
+}
+
+SECItem *
+CERT_PostOCSPRequest(PLArenaPool *arena,  const char *location, 
+                     const SECItem *encodedRequest)
+{
+    return cert_FetchOCSPResponse(arena, location, encodedRequest);
+}
+
+SECItem *
+cert_FetchOCSPResponse(PLArenaPool *arena,  const char *location, 
+                       const SECItem *encodedRequest)
+{
+    const SEC_HttpClientFcn *registeredHttpClient;
+    SECItem *encodedResponse = NULL;
+
+    registeredHttpClient = SEC_GetRegisteredHttpClient();
+
+    if (registeredHttpClient && registeredHttpClient->version == 1) {
+        encodedResponse = fetchOcspHttpClientV1(
+                              arena,
+                              &registeredHttpClient->fcnTable.ftable1,
+                              location,
+                              encodedRequest);
+    } else {
+        /* use internal http client */
+        PRFileDesc *sock = ocsp_SendEncodedRequest(location, encodedRequest);
+        if (sock) {
+            encodedResponse = ocsp_GetEncodedResponse(arena, sock);
+            PR_Close(sock);
+        }
+    }
+
+    return encodedResponse;
+}
+
+static SECItem *
+ocsp_GetEncodedOCSPResponseForSingleCert(PLArenaPool *arena, 
+                                         CERTOCSPCertID *certID, 
+                                         CERTCertificate *singleCert, 
+                                         const char *location,
+					 const char *method,
+					 PRTime time,
+                                         PRBool addServiceLocator,
+                                         void *pwArg,
+                                         CERTOCSPRequest **pRequest)
+{
+    CERTOCSPRequest *request;
+    request = cert_CreateSingleCertOCSPRequest(certID, singleCert, time, 
+                                               addServiceLocator, NULL);
+    if (!request)
+        return NULL;
+    return ocsp_GetEncodedOCSPResponseFromRequest(arena, request, location,
+                                                  method, time, addServiceLocator,
+                                                  pwArg, pRequest);
+}
+
+/* Checks a certificate for the key usage extension of OCSP signer. */
+static PRBool
+ocsp_CertIsOCSPDesignatedResponder(CERTCertificate *cert)
+{
+    SECStatus rv;
+    SECItem extItem;
+    SECItem **oids;
+    SECItem *oid;
+    SECOidTag oidTag;
+    PRBool retval;
+    CERTOidSequence *oidSeq = NULL;
+
+
+    extItem.data = NULL;
+    rv = CERT_FindCertExtension(cert, SEC_OID_X509_EXT_KEY_USAGE, &extItem);
+    if ( rv != SECSuccess ) {
+	goto loser;
+    }
+
+    oidSeq = CERT_DecodeOidSequence(&extItem);
+    if ( oidSeq == NULL ) {
+	goto loser;
+    }
+
+    oids = oidSeq->oids;
+    while ( *oids != NULL ) {
+	oid = *oids;
+	
+	oidTag = SECOID_FindOIDTag(oid);
+	
+	if ( oidTag == SEC_OID_OCSP_RESPONDER ) {
+	    goto success;
+	}
+	
+	oids++;
+    }
+
+loser:
+    retval = PR_FALSE;
+    PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT);
+    goto done;
+success:
+    retval = PR_TRUE;
+done:
+    if ( extItem.data != NULL ) {
+	PORT_Free(extItem.data);
+    }
+    if ( oidSeq != NULL ) {
+	CERT_DestroyOidSequence(oidSeq);
+    }
+    
+    return(retval);
+}
+
+
+#ifdef LATER	/*
+		 * XXX This function is not currently used, but will
+		 * be needed later when we do revocation checking of
+		 * the responder certificate.  Of course, it may need
+		 * revising then, if the cert extension interface has
+		 * changed.  (Hopefully it will!)
+		 */
+
+/* Checks a certificate to see if it has the OCSP no check extension. */
+static PRBool
+ocsp_CertHasNoCheckExtension(CERTCertificate *cert)
+{
+    SECStatus rv;
+    
+    rv = CERT_FindCertExtension(cert, SEC_OID_PKIX_OCSP_NO_CHECK, 
+				NULL);
+    if (rv == SECSuccess) {
+	return PR_TRUE;
+    }
+    return PR_FALSE;
+}
+#endif	/* LATER */
+
+static PRBool
+ocsp_matchcert(SECItem *certIndex,CERTCertificate *testCert)
+{
+    SECItem item;
+    unsigned char buf[HASH_LENGTH_MAX];
+
+    item.data = buf;
+    item.len = SHA1_LENGTH;
+
+    if (CERT_GetSubjectPublicKeyDigest(NULL,testCert,SEC_OID_SHA1,
+				       &item) == NULL) {
+	return PR_FALSE;
+    }
+    if  (SECITEM_ItemsAreEqual(certIndex,&item)) {
+	return PR_TRUE;
+    }
+    if (CERT_GetSubjectPublicKeyDigest(NULL,testCert,SEC_OID_MD5,
+				       &item) == NULL) {
+	return PR_FALSE;
+    }
+    if  (SECITEM_ItemsAreEqual(certIndex,&item)) {
+	return PR_TRUE;
+    }
+    if (CERT_GetSubjectPublicKeyDigest(NULL,testCert,SEC_OID_MD2,
+				       &item) == NULL) {
+	return PR_FALSE;
+    }
+    if  (SECITEM_ItemsAreEqual(certIndex,&item)) {
+	return PR_TRUE;
+    }
+
+    return PR_FALSE;
+}
+
+static CERTCertificate *
+ocsp_CertGetDefaultResponder(CERTCertDBHandle *handle,CERTOCSPCertID *certID);
+
+CERTCertificate *
+ocsp_GetSignerCertificate(CERTCertDBHandle *handle, ocspResponseData *tbsData,
+                          ocspSignature *signature, CERTCertificate *issuer)
+{
+    CERTCertificate **certs = NULL;
+    CERTCertificate *signerCert = NULL;
+    SECStatus rv = SECFailure;
+    PRBool lookupByName = PR_TRUE;
+    void *certIndex = NULL;
+    int certCount = 0;
+
+    PORT_Assert(tbsData->responderID != NULL);
+    switch (tbsData->responderID->responderIDType) {
+    case ocspResponderID_byName:
+	lookupByName = PR_TRUE;
+	certIndex = &tbsData->derResponderID;
+	break;
+    case ocspResponderID_byKey:
+	lookupByName = PR_FALSE;
+	certIndex = &tbsData->responderID->responderIDValue.keyHash;
+	break;
+    case ocspResponderID_other:
+    default:
+	PORT_Assert(0);
+	PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+	return NULL;
+    }
+
+    /*
+     * If the signature contains some certificates as well, temporarily
+     * import them in case they are needed for verification.
+     *
+     * Note that the result of this is that each cert in "certs" needs
+     * to be destroyed.
+     */
+    if (signature->derCerts != NULL) {
+	for (; signature->derCerts[certCount] != NULL; certCount++) {
+	    /* just counting */
+	}
+	rv = CERT_ImportCerts(handle, certUsageStatusResponder, certCount,
+	                      signature->derCerts, &certs,
+	                      PR_FALSE, PR_FALSE, NULL);
+	if (rv != SECSuccess)
+	     goto finish;
+    }
+
+    /*
+     * Now look up the certificate that did the signing.
+     * The signer can be specified either by name or by key hash.
+     */
+    if (lookupByName) {
+	SECItem *crIndex = (SECItem*)certIndex;
+	SECItem encodedName;
+	PLArenaPool *arena;
+
+	arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+	if (arena != NULL) {
+
+	    rv = SEC_QuickDERDecodeItem(arena, &encodedName,
+	                                ocsp_ResponderIDDerNameTemplate,
+	                                crIndex);
+	    if (rv != SECSuccess) {
+	        if (PORT_GetError() == SEC_ERROR_BAD_DER)
+	            PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+	    } else {
+	            signerCert = CERT_FindCertByName(handle, &encodedName);
+	    }
+	    PORT_FreeArena(arena, PR_FALSE);
+	}
+    } else {
+	/*
+	 * The signer is either 1) a known issuer CA we passed in,
+	 * 2) the default OCSP responder, or 3) an intermediate CA
+	 * passed in the cert list to use. Figure out which it is.
+	 */
+	int i;
+	CERTCertificate *responder = 
+            ocsp_CertGetDefaultResponder(handle, NULL);
+	if (responder && ocsp_matchcert(certIndex,responder)) {
+	    signerCert = CERT_DupCertificate(responder);
+	} else if (issuer && ocsp_matchcert(certIndex,issuer)) {
+	    signerCert = CERT_DupCertificate(issuer);
+	} 
+	for (i=0; (signerCert == NULL) && (i < certCount); i++) {
+	    if (ocsp_matchcert(certIndex,certs[i])) {
+		signerCert = CERT_DupCertificate(certs[i]);
+	    }
+	}
+	if (signerCert == NULL) {
+	    PORT_SetError(SEC_ERROR_UNKNOWN_CERT);
+	}
+    }
+
+finish:
+    if (certs != NULL) {
+	CERT_DestroyCertArray(certs, certCount);
+    }
+
+    return signerCert;
+}
+
+SECStatus
+ocsp_VerifyResponseSignature(CERTCertificate *signerCert,
+                             ocspSignature *signature,
+                             SECItem *tbsResponseDataDER,
+                             void *pwArg)
+{
+    SECKEYPublicKey *signerKey = NULL;
+    SECStatus rv = SECFailure;
+    CERTSignedData signedData;
+
+    /*
+     * Now get the public key from the signer's certificate; we need
+     * it to perform the verification.
+     */
+    signerKey = CERT_ExtractPublicKey(signerCert);
+    if (signerKey == NULL) {
+        return SECFailure;
+    }
+
+    /*
+     * We copy the signature data *pointer* and length, so that we can
+     * modify the length without damaging the original copy.  This is a
+     * simple copy, not a dup, so no destroy/free is necessary.
+     */
+    signedData.signature = signature->signature;
+    signedData.signatureAlgorithm = signature->signatureAlgorithm;
+    signedData.data = *tbsResponseDataDER;
+
+    rv = CERT_VerifySignedDataWithPublicKey(&signedData, signerKey, pwArg);
+    if (rv != SECSuccess &&
+        (PORT_GetError() == SEC_ERROR_BAD_SIGNATURE || 
+         PORT_GetError() == SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED)) {
+        PORT_SetError(SEC_ERROR_OCSP_BAD_SIGNATURE);
+    }
+
+    if (signerKey != NULL) {
+        SECKEY_DestroyPublicKey(signerKey);
+    }
+
+    return rv;
+}
+
+
+/*
+ * FUNCTION: CERT_VerifyOCSPResponseSignature
+ *   Check the signature on an OCSP Response.  Will also perform a
+ *   verification of the signer's certificate.  Note, however, that a
+ *   successful verification does not make any statement about the
+ *   signer's *authority* to provide status for the certificate(s),
+ *   that must be checked individually for each certificate.
+ * INPUTS:
+ *   CERTOCSPResponse *response
+ *     Pointer to response structure with signature to be checked.
+ *   CERTCertDBHandle *handle
+ *     Pointer to CERTCertDBHandle for certificate DB to use for verification.
+ *   void *pwArg
+ *     Pointer to argument for password prompting, if needed.
+ * OUTPUTS:
+ *   CERTCertificate **pSignerCert
+ *     Pointer in which to store signer's certificate; only filled-in if
+ *     non-null.
+ * RETURN:
+ *   Returns SECSuccess when signature is valid, anything else means invalid.
+ *   Possible errors set:
+ *	SEC_ERROR_OCSP_MALFORMED_RESPONSE - unknown type of ResponderID
+ *	SEC_ERROR_INVALID_TIME - bad format of "ProducedAt" time
+ *	SEC_ERROR_UNKNOWN_SIGNER - signer's cert could not be found
+ *	SEC_ERROR_BAD_SIGNATURE - the signature did not verify
+ *   Other errors are any of the many possible failures in cert verification
+ *   (e.g. SEC_ERROR_REVOKED_CERTIFICATE, SEC_ERROR_UNTRUSTED_ISSUER) when
+ *   verifying the signer's cert, or low-level problems (no memory, etc.)
+ */
+SECStatus
+CERT_VerifyOCSPResponseSignature(CERTOCSPResponse *response,	
+				 CERTCertDBHandle *handle, void *pwArg,
+				 CERTCertificate **pSignerCert,
+				 CERTCertificate *issuer)
+{
+    SECItem *tbsResponseDataDER;
+    CERTCertificate *signerCert = NULL;
+    SECStatus rv = SECFailure;
+    PRTime producedAt;
+
+    /* ocsp_DecodeBasicOCSPResponse will fail if asn1 decoder is unable
+     * to properly decode tbsData (see the function and
+     * ocsp_BasicOCSPResponseTemplate). Thus, tbsData can not be
+     * equal to null */
+    ocspResponseData *tbsData = ocsp_GetResponseData(response,
+                                                     &tbsResponseDataDER);
+    ocspSignature *signature = ocsp_GetResponseSignature(response);
+
+    if (!signature) {
+        PORT_SetError(SEC_ERROR_OCSP_BAD_SIGNATURE);
+        return SECFailure;
+    }
+
+    /*
+     * If this signature has already gone through verification, just
+     * return the cached result.
+     */
+    if (signature->wasChecked) {
+	if (signature->status == SECSuccess) {
+	    if (pSignerCert != NULL)
+		*pSignerCert = CERT_DupCertificate(signature->cert);
+	} else {
+	    PORT_SetError(signature->failureReason);
+	}
+	return signature->status;
+    }
+
+    signerCert = ocsp_GetSignerCertificate(handle, tbsData,
+                                           signature, issuer);
+    if (signerCert == NULL) {
+	rv = SECFailure;
+	if (PORT_GetError() == SEC_ERROR_UNKNOWN_CERT) {
+	    /* Make the error a little more specific. */
+	    PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT);
+	}
+	goto finish;
+    }
+
+    /*
+     * We could mark this true at the top of this function, or always
+     * below at "finish", but if the problem was just that we could not
+     * find the signer's cert, leave that as if the signature hasn't
+     * been checked in case a subsequent call might have better luck.
+     */
+    signature->wasChecked = PR_TRUE;
+
+    /*
+     * The function will also verify the signer certificate; we
+     * need to tell it *when* that certificate must be valid -- for our
+     * purposes we expect it to be valid when the response was signed.
+     * The value of "producedAt" is the signing time.
+     */
+    rv = DER_GeneralizedTimeToTime(&producedAt, &tbsData->producedAt);
+    if (rv != SECSuccess)
+        goto finish;
+
+    /*
+     * Just because we have a cert does not mean it is any good; check
+     * it for validity, trust and usage.
+     */
+    if (ocsp_CertIsOCSPDefaultResponder(handle, signerCert)) {
+        rv = SECSuccess;
+    } else {
+        SECCertUsage certUsage;
+        if (CERT_IsCACert(signerCert, NULL)) {
+            certUsage = certUsageAnyCA;
+        } else {
+            certUsage = certUsageStatusResponder;
+        }
+        rv = cert_VerifyCertWithFlags(handle, signerCert, PR_TRUE, certUsage,
+                                      producedAt, CERT_VERIFYCERT_SKIP_OCSP,
+                                      pwArg, NULL);
+        if (rv != SECSuccess) {
+            PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT);
+            goto finish;
+        }
+    }
+
+    rv = ocsp_VerifyResponseSignature(signerCert, signature,
+                                      tbsResponseDataDER,
+                                      pwArg);
+
+finish:
+    if (signature->wasChecked)
+	signature->status = rv;
+
+    if (rv != SECSuccess) {
+	signature->failureReason = PORT_GetError();
+	if (signerCert != NULL)
+	    CERT_DestroyCertificate(signerCert);
+    } else {
+	/*
+	 * Save signer's certificate in signature.
+	 */
+	signature->cert = signerCert;
+	if (pSignerCert != NULL) {
+	    /*
+	     * Pass pointer to signer's certificate back to our caller,
+	     * who is also now responsible for destroying it.
+	     */
+	    *pSignerCert = CERT_DupCertificate(signerCert);
+	}
+    }
+
+    return rv;
+}
+
+/*
+ * See if the request's certID and the single response's certID match.
+ * This can be easy or difficult, depending on whether the same hash
+ * algorithm was used.
+ */
+static PRBool
+ocsp_CertIDsMatch(CERTOCSPCertID *requestCertID,
+		  CERTOCSPCertID *responseCertID)
+{
+    PRBool match = PR_FALSE;
+    SECOidTag hashAlg;
+    SECItem *keyHash = NULL;
+    SECItem *nameHash = NULL;
+
+    /*
+     * In order to match, they must have the same issuer and the same
+     * serial number.
+     *
+     * We just compare the easier things first.
+     */
+    if (SECITEM_CompareItem(&requestCertID->serialNumber,
+			    &responseCertID->serialNumber) != SECEqual) {
+	goto done;
+    }
+
+    /*
+     * Make sure the "parameters" are not too bogus.  Since we encoded
+     * requestCertID->hashAlgorithm, we don't need to check it.
+     */
+    if (responseCertID->hashAlgorithm.parameters.len > 2) {
+	goto done;
+    }
+    if (SECITEM_CompareItem(&requestCertID->hashAlgorithm.algorithm,
+		&responseCertID->hashAlgorithm.algorithm) == SECEqual) {
+	/*
+	 * If the hash algorithms match then we can do a simple compare
+	 * of the hash values themselves.
+	 */
+	if ((SECITEM_CompareItem(&requestCertID->issuerNameHash,
+				&responseCertID->issuerNameHash) == SECEqual)
+	    && (SECITEM_CompareItem(&requestCertID->issuerKeyHash,
+				&responseCertID->issuerKeyHash) == SECEqual)) {
+	    match = PR_TRUE;
+	}
+	goto done;
+    }
+
+    hashAlg = SECOID_FindOIDTag(&responseCertID->hashAlgorithm.algorithm);
+    switch (hashAlg) {
+    case SEC_OID_SHA1:
+	keyHash = &requestCertID->issuerSHA1KeyHash;
+	nameHash = &requestCertID->issuerSHA1NameHash;
+	break;
+    case SEC_OID_MD5:
+	keyHash = &requestCertID->issuerMD5KeyHash;
+	nameHash = &requestCertID->issuerMD5NameHash;
+	break;
+    case SEC_OID_MD2:
+	keyHash = &requestCertID->issuerMD2KeyHash;
+	nameHash = &requestCertID->issuerMD2NameHash;
+	break;
+    default:
+	PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ 	return PR_FALSE;
+    }
+
+    if ((keyHash != NULL)
+	&& (SECITEM_CompareItem(nameHash,
+				&responseCertID->issuerNameHash) == SECEqual)
+	&& (SECITEM_CompareItem(keyHash,
+				&responseCertID->issuerKeyHash) == SECEqual)) {
+	match = PR_TRUE;
+    }
+
+done:
+    return match;
+}
+
+/*
+ * Find the single response for the cert specified by certID.
+ * No copying is done; this just returns a pointer to the appropriate
+ * response within responses, if it is found (and null otherwise).
+ * This is fine, of course, since this function is internal-use only.
+ */
+static CERTOCSPSingleResponse *
+ocsp_GetSingleResponseForCertID(CERTOCSPSingleResponse **responses,
+				CERTCertDBHandle *handle,
+				CERTOCSPCertID *certID)
+{
+    CERTOCSPSingleResponse *single;
+    int i;
+
+    if (responses == NULL)
+	return NULL;
+
+    for (i = 0; responses[i] != NULL; i++) {
+	single = responses[i];
+	if (ocsp_CertIDsMatch(certID, single->certID)) {
+	    return single;
+	}
+    }
+
+    /*
+     * The OCSP server should have included a response even if it knew
+     * nothing about the certificate in question.  Since it did not,
+     * this will make it look as if it had.
+     * 
+     * XXX Should we make this a separate error to notice the server's
+     * bad behavior?
+     */
+    PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT);
+    return NULL;
+}
+
+static ocspCheckingContext *
+ocsp_GetCheckingContext(CERTCertDBHandle *handle)
+{
+    CERTStatusConfig *statusConfig;
+    ocspCheckingContext *ocspcx = NULL;
+
+    statusConfig = CERT_GetStatusConfig(handle);
+    if (statusConfig != NULL) {
+	ocspcx = statusConfig->statusContext;
+
+	/*
+	 * This is actually an internal error, because we should never
+	 * have a good statusConfig without a good statusContext, too.
+	 * For lack of anything better, though, we just assert and use
+	 * the same error as if there were no statusConfig (set below).
+	 */
+	PORT_Assert(ocspcx != NULL);
+    }
+
+    if (ocspcx == NULL)
+	PORT_SetError(SEC_ERROR_OCSP_NOT_ENABLED);
+
+    return ocspcx;
+}
+
+/*
+ * Return cert reference if the given signerCert is the default responder for
+ * the given certID.  If not, or if any error, return NULL.
+ */
+static CERTCertificate *
+ocsp_CertGetDefaultResponder(CERTCertDBHandle *handle, CERTOCSPCertID *certID)
+{
+    ocspCheckingContext *ocspcx;
+
+    ocspcx = ocsp_GetCheckingContext(handle);
+    if (ocspcx == NULL)
+	goto loser;
+
+   /*
+    * Right now we have only one default responder.  It applies to
+    * all certs when it is used, so the check is simple and certID
+    * has no bearing on the answer.  Someday in the future we may
+    * allow configuration of different responders for different
+    * issuers, and then we would have to use the issuer specified
+    * in certID to determine if signerCert is the right one.
+    */
+    if (ocspcx->useDefaultResponder) {
+	PORT_Assert(ocspcx->defaultResponderCert != NULL);
+	return ocspcx->defaultResponderCert;
+    }
+
+loser:
+    return NULL;
+}
+
+/*
+ * Return true if the cert is one of the default responders configured for
+ * ocsp context. If not, or if any error, return false.
+ */
+PRBool
+ocsp_CertIsOCSPDefaultResponder(CERTCertDBHandle *handle, CERTCertificate *cert)
+{
+    ocspCheckingContext *ocspcx;
+
+    ocspcx = ocsp_GetCheckingContext(handle);
+    if (ocspcx == NULL)
+	return PR_FALSE;
+
+   /*
+    * Right now we have only one default responder.  It applies to
+    * all certs when it is used, so the check is simple and certID
+    * has no bearing on the answer.  Someday in the future we may
+    * allow configuration of different responders for different
+    * issuers, and then we would have to use the issuer specified
+    * in certID to determine if signerCert is the right one.
+    */
+    if (ocspcx->useDefaultResponder &&
+        CERT_CompareCerts(ocspcx->defaultResponderCert, cert)) {
+	return PR_TRUE;
+    }
+
+    return PR_FALSE;
+}
+
+/*
+ * Check that the given signer certificate is authorized to sign status
+ * information for the given certID.  Return true if it is, false if not
+ * (or if there is any error along the way).  If false is returned because
+ * the signer is not authorized, the following error will be set:
+ *	SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE
+ * Other errors are low-level problems (no memory, bad database, etc.).
+ *
+ * There are three ways to be authorized.  In the order in which we check,
+ * using the terms used in the OCSP spec, the signer must be one of:
+ *  1.  A "trusted responder" -- it matches a local configuration
+ *      of OCSP signing authority for the certificate in question.
+ *  2.  The CA who issued the certificate in question.
+ *  3.  A "CA designated responder", aka an "authorized responder" -- it
+ *      must be represented by a special cert issued by the CA who issued
+ *      the certificate in question.
+ */
+static PRBool
+ocsp_AuthorizedResponderForCertID(CERTCertDBHandle *handle,
+				  CERTCertificate *signerCert,
+				  CERTOCSPCertID *certID,
+				  PRTime thisUpdate)
+{
+    CERTCertificate *issuerCert = NULL, *defRespCert;
+    SECItem *keyHash = NULL;
+    SECItem *nameHash = NULL;
+    SECOidTag hashAlg;
+    PRBool keyHashEQ = PR_FALSE, nameHashEQ = PR_FALSE;
+
+    /*
+     * Check first for a trusted responder, which overrides everything else.
+     */
+    if ((defRespCert = ocsp_CertGetDefaultResponder(handle, certID)) &&
+        CERT_CompareCerts(defRespCert, signerCert)) {
+        return PR_TRUE;
+    }
+
+    /*
+     * In the other two cases, we need to do an issuer comparison.
+     * How we do it depends on whether the signer certificate has the
+     * special extension (for a designated responder) or not.
+     *
+     * First, lets check if signer of the response is the actual issuer
+     * of the cert. For that we will use signer cert key hash and cert subj
+     * name hash and will compare them with already calculated issuer key
+     * hash and issuer name hash. The hash algorithm is picked from response
+     * certID hash to avoid second hash calculation.
+     */
+
+    hashAlg = SECOID_FindOIDTag(&certID->hashAlgorithm.algorithm);
+
+    keyHash = CERT_GetSubjectPublicKeyDigest(NULL, signerCert, hashAlg, NULL);
+    if (keyHash != NULL) {
+
+        keyHashEQ =
+            (SECITEM_CompareItem(keyHash,
+                                 &certID->issuerKeyHash) == SECEqual);
+        SECITEM_FreeItem(keyHash, PR_TRUE);
+    }
+    if (keyHashEQ &&
+        (nameHash = CERT_GetSubjectNameDigest(NULL, signerCert,
+                                              hashAlg, NULL))) {
+        nameHashEQ =
+            (SECITEM_CompareItem(nameHash,
+                                 &certID->issuerNameHash) == SECEqual);
+            
+        SECITEM_FreeItem(nameHash, PR_TRUE);
+        if (nameHashEQ) {
+            /* The issuer of the cert is the the signer of the response */
+            return PR_TRUE;
+        }
+    }
+
+
+    keyHashEQ = PR_FALSE;
+    nameHashEQ = PR_FALSE;
+
+    if (!ocsp_CertIsOCSPDesignatedResponder(signerCert)) {
+        PORT_SetError(SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE);
+        return PR_FALSE;
+    }
+
+    /*
+     * The signer is a designated responder.  Its issuer must match
+     * the issuer of the cert being checked.
+     */
+    issuerCert = CERT_FindCertIssuer(signerCert, thisUpdate,
+                                     certUsageAnyCA);
+    if (issuerCert == NULL) {
+        /*
+         * We could leave the SEC_ERROR_UNKNOWN_ISSUER error alone,
+         * but the following will give slightly more information.
+         * Once we have an error stack, things will be much better.
+         */
+        PORT_SetError(SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE);
+        return PR_FALSE;
+    }
+
+    keyHash = CERT_GetSubjectPublicKeyDigest(NULL, issuerCert, hashAlg, NULL);
+    nameHash = CERT_GetSubjectNameDigest(NULL, issuerCert, hashAlg, NULL);
+
+    CERT_DestroyCertificate(issuerCert);
+
+    if (keyHash != NULL && nameHash != NULL) {
+        keyHashEQ = 
+            (SECITEM_CompareItem(keyHash,
+                                 &certID->issuerKeyHash) == SECEqual);
+
+        nameHashEQ =
+            (SECITEM_CompareItem(nameHash,
+                                 &certID->issuerNameHash) == SECEqual);
+    }
+
+    if (keyHash) {
+        SECITEM_FreeItem(keyHash, PR_TRUE);
+    }
+    if (nameHash) {
+        SECITEM_FreeItem(nameHash, PR_TRUE);
+    }
+
+    if (keyHashEQ && nameHashEQ) {
+        return PR_TRUE;
+    }
+
+    PORT_SetError(SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE);
+    return PR_FALSE;
+}
+
+/*
+ * We need to check that a responder gives us "recent" information.
+ * Since a responder can pre-package responses, we need to pick an amount
+ * of time that is acceptable to us, and reject any response that is
+ * older than that.
+ *
+ * XXX This *should* be based on some configuration parameter, so that
+ * different usages could specify exactly what constitutes "sufficiently
+ * recent".  But that is not going to happen right away.  For now, we
+ * want something from within the last 24 hours.  This macro defines that
+ * number in seconds.
+ */
+#define OCSP_ALLOWABLE_LAPSE_SECONDS	(24L * 60L * 60L)
+
+static PRBool
+ocsp_TimeIsRecent(PRTime checkTime)
+{
+    PRTime now = PR_Now();
+    PRTime lapse, tmp;
+
+    LL_I2L(lapse, OCSP_ALLOWABLE_LAPSE_SECONDS);
+    LL_I2L(tmp, PR_USEC_PER_SEC);
+    LL_MUL(lapse, lapse, tmp);		/* allowable lapse in microseconds */
+
+    LL_ADD(checkTime, checkTime, lapse);
+    if (LL_CMP(now, >, checkTime))
+	return PR_FALSE;
+
+    return PR_TRUE;
+}
+
+#define OCSP_SLOP (5L*60L) /* OCSP responses are allowed to be 5 minutes
+                              in the future by default */
+
+static PRUint32 ocspsloptime = OCSP_SLOP;	/* seconds */
+
+/*
+ * If an old response contains the revoked certificate status, we want
+ * to return SECSuccess so the response will be used.
+ */
+static SECStatus
+ocsp_HandleOldSingleResponse(CERTOCSPSingleResponse *single, PRTime time)
+{
+    SECStatus rv;
+    ocspCertStatus *status = single->certStatus;
+    if (status->certStatusType == ocspCertStatus_revoked) {
+        rv = ocsp_CertRevokedAfter(status->certStatusInfo.revokedInfo, time);
+        if (rv != SECSuccess &&
+            PORT_GetError() == SEC_ERROR_REVOKED_CERTIFICATE) {
+            /*
+             * Return SECSuccess now.  The subsequent ocsp_CertRevokedAfter
+             * call in ocsp_CertHasGoodStatus will cause
+             * ocsp_CertHasGoodStatus to fail with
+             * SEC_ERROR_REVOKED_CERTIFICATE.
+             */
+            return SECSuccess;
+        }
+
+    }
+    PORT_SetError(SEC_ERROR_OCSP_OLD_RESPONSE);
+    return SECFailure;
+}
+
+/*
+ * Check that this single response is okay.  A return of SECSuccess means:
+ *   1. The signer (represented by "signerCert") is authorized to give status
+ *	for the cert represented by the individual response in "single".
+ *   2. The value of thisUpdate is earlier than now.
+ *   3. The value of producedAt is later than or the same as thisUpdate.
+ *   4. If nextUpdate is given:
+ *	- The value of nextUpdate is later than now.
+ *	- The value of producedAt is earlier than nextUpdate.
+ *	Else if no nextUpdate:
+ *	- The value of thisUpdate is fairly recent.
+ *	- The value of producedAt is fairly recent.
+ *	However we do not need to perform an explicit check for this last
+ *	constraint because it is already guaranteed by checking that
+ *	producedAt is later than thisUpdate and thisUpdate is recent.
+ * Oh, and any responder is "authorized" to say that a cert is unknown to it.
+ *
+ * If any of those checks fail, SECFailure is returned and an error is set:
+ *	SEC_ERROR_OCSP_FUTURE_RESPONSE
+ *	SEC_ERROR_OCSP_OLD_RESPONSE
+ *	SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE
+ * Other errors are low-level problems (no memory, bad database, etc.).
+ */ 
+static SECStatus
+ocsp_VerifySingleResponse(CERTOCSPSingleResponse *single,
+			  CERTCertDBHandle *handle,
+			  CERTCertificate *signerCert,
+			  PRTime producedAt)
+{
+    CERTOCSPCertID *certID = single->certID;
+    PRTime now, thisUpdate, nextUpdate, tmstamp, tmp;
+    SECStatus rv;
+
+    OCSP_TRACE(("OCSP ocsp_VerifySingleResponse, nextUpdate: %d\n", 
+               ((single->nextUpdate) != 0)));
+    /*
+     * If all the responder said was that the given cert was unknown to it,
+     * that is a valid response.  Not very interesting to us, of course,
+     * but all this function is concerned with is validity of the response,
+     * not the status of the cert.
+     */
+    PORT_Assert(single->certStatus != NULL);
+    if (single->certStatus->certStatusType == ocspCertStatus_unknown)
+	return SECSuccess;
+
+    /*
+     * We need to extract "thisUpdate" for use below and to pass along
+     * to AuthorizedResponderForCertID in case it needs it for doing an
+     * issuer look-up.
+     */
+    rv = DER_GeneralizedTimeToTime(&thisUpdate, &single->thisUpdate);
+    if (rv != SECSuccess)
+	return rv;
+
+    /*
+     * First confirm that signerCert is authorized to give this status.
+     */
+    if (ocsp_AuthorizedResponderForCertID(handle, signerCert, certID,
+					  thisUpdate) != PR_TRUE)
+	return SECFailure;
+
+    /*
+     * Now check the time stuff, as described above.
+     */
+    now = PR_Now();
+    /* allow slop time for future response */
+    LL_UI2L(tmstamp, ocspsloptime); /* get slop time in seconds */
+    LL_UI2L(tmp, PR_USEC_PER_SEC);
+    LL_MUL(tmp, tmstamp, tmp); /* convert the slop time to PRTime */
+    LL_ADD(tmstamp, tmp, now); /* add current time to it */
+
+    if (LL_CMP(thisUpdate, >, tmstamp) || LL_CMP(producedAt, <, thisUpdate)) {
+	PORT_SetError(SEC_ERROR_OCSP_FUTURE_RESPONSE);
+	return SECFailure;
+    }
+    if (single->nextUpdate != NULL) {
+	rv = DER_GeneralizedTimeToTime(&nextUpdate, single->nextUpdate);
+	if (rv != SECSuccess)
+	    return rv;
+
+	LL_ADD(tmp, tmp, nextUpdate);
+	if (LL_CMP(tmp, <, now) || LL_CMP(producedAt, >, nextUpdate))
+	    return ocsp_HandleOldSingleResponse(single, now);
+    } else if (ocsp_TimeIsRecent(thisUpdate) != PR_TRUE) {
+	return ocsp_HandleOldSingleResponse(single, now);
+    }
+
+    return SECSuccess;
+}
+
+
+/*
+ * FUNCTION: CERT_GetOCSPAuthorityInfoAccessLocation
+ *   Get the value of the URI of the OCSP responder for the given cert.
+ *   This is found in the (optional) Authority Information Access extension
+ *   in the cert.
+ * INPUTS:
+ *   CERTCertificate *cert
+ *     The certificate being examined.
+ * RETURN:
+ *   char *
+ *     A copy of the URI for the OCSP method, if found.  If either the
+ *     extension is not present or it does not contain an entry for OCSP,
+ *     SEC_ERROR_CERT_BAD_ACCESS_LOCATION will be set and a NULL returned.
+ *     Any other error will also result in a NULL being returned.
+ *     
+ *     This result should be freed (via PORT_Free) when no longer in use.
+ */
+char *
+CERT_GetOCSPAuthorityInfoAccessLocation(const CERTCertificate *cert)
+{
+    CERTGeneralName *locname = NULL;
+    SECItem *location = NULL;
+    SECItem *encodedAuthInfoAccess = NULL;
+    CERTAuthInfoAccess **authInfoAccess = NULL;
+    char *locURI = NULL;
+    PLArenaPool *arena = NULL;
+    SECStatus rv;
+    int i;
+
+    /*
+     * Allocate this one from the heap because it will get filled in
+     * by CERT_FindCertExtension which will also allocate from the heap,
+     * and we can free the entire thing on our way out.
+     */
+    encodedAuthInfoAccess = SECITEM_AllocItem(NULL, NULL, 0);
+    if (encodedAuthInfoAccess == NULL)
+	goto loser;
+
+    rv = CERT_FindCertExtension(cert, SEC_OID_X509_AUTH_INFO_ACCESS,
+				encodedAuthInfoAccess);
+    if (rv == SECFailure) {
+	PORT_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION);
+	goto loser;
+    }
+
+    /*
+     * The rest of the things allocated in the routine will come out of
+     * this arena, which is temporary just for us to decode and get at the
+     * AIA extension.  The whole thing will be destroyed on our way out,
+     * after we have copied the location string (url) itself (if found).
+     */
+    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+    if (arena == NULL)
+	goto loser;
+
+    authInfoAccess = CERT_DecodeAuthInfoAccessExtension(arena,
+							encodedAuthInfoAccess);
+    if (authInfoAccess == NULL)
+	goto loser;
+
+    for (i = 0; authInfoAccess[i] != NULL; i++) {
+	if (SECOID_FindOIDTag(&authInfoAccess[i]->method) == SEC_OID_PKIX_OCSP)
+	    locname = authInfoAccess[i]->location;
+    }
+
+    /*
+     * If we found an AIA extension, but it did not include an OCSP method,
+     * that should look to our caller as if we did not find the extension
+     * at all, because it is only an OCSP method that we care about.
+     * So set the same error that would be set if the AIA extension was
+     * not there at all.
+     */
+    if (locname == NULL) {
+	PORT_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION);
+	goto loser;
+    }
+
+    /*
+     * The following is just a pointer back into locname (i.e. not a copy);
+     * thus it should not be freed.
+     */
+    location = CERT_GetGeneralNameByType(locname, certURI, PR_FALSE);
+    if (location == NULL) {
+	/*
+	 * XXX Appears that CERT_GetGeneralNameByType does not set an
+	 * error if there is no name by that type.  For lack of anything
+	 * better, act as if the extension was not found.  In the future
+	 * this should probably be something more like the extension was
+	 * badly formed.
+	 */
+	PORT_SetError(SEC_ERROR_CERT_BAD_ACCESS_LOCATION);
+	goto loser;
+    }
+
+    /*
+     * That location is really a string, but it has a specified length
+     * without a null-terminator.  We need a real string that does have
+     * a null-terminator, and we need a copy of it anyway to return to
+     * our caller -- so allocate and copy.
+     */
+    locURI = PORT_Alloc(location->len + 1);
+    if (locURI == NULL) {
+	goto loser;
+    }
+    PORT_Memcpy(locURI, location->data, location->len);
+    locURI[location->len] = '\0';
+
+loser:
+    if (arena != NULL)
+	PORT_FreeArena(arena, PR_FALSE);
+
+    if (encodedAuthInfoAccess != NULL)
+	SECITEM_FreeItem(encodedAuthInfoAccess, PR_TRUE);
+
+    return locURI;
+}
+
+
+/*
+ * Figure out where we should go to find out the status of the given cert
+ * via OCSP.  If allowed to use a default responder uri and a default
+ * responder is set up, then that is our answer.
+ * If not, see if the certificate has an Authority Information Access (AIA)
+ * extension for OCSP, and return the value of that.  Otherwise return NULL.
+ * We also let our caller know whether or not the responder chosen was
+ * a default responder or not through the output variable isDefault;
+ * its value has no meaning unless a good (non-null) value is returned
+ * for the location.
+ *
+ * The result needs to be freed (PORT_Free) when no longer in use.
+ */
+char *
+ocsp_GetResponderLocation(CERTCertDBHandle *handle, CERTCertificate *cert,
+			  PRBool canUseDefault, PRBool *isDefault)
+{
+    ocspCheckingContext *ocspcx = NULL;
+    char *ocspUrl = NULL;
+
+    if (canUseDefault) {
+        ocspcx = ocsp_GetCheckingContext(handle);
+    }
+    if (ocspcx != NULL && ocspcx->useDefaultResponder) {
+	/*
+	 * A default responder wins out, if specified.
+	 * XXX Someday this may be a more complicated determination based
+	 * on the cert's issuer.  (That is, we could have different default
+	 * responders configured for different issuers.)
+	 */
+	PORT_Assert(ocspcx->defaultResponderURI != NULL);
+	*isDefault = PR_TRUE;
+	return (PORT_Strdup(ocspcx->defaultResponderURI));
+    }
+
+    /*
+     * No default responder set up, so go see if we can find an AIA
+     * extension that has a value for OCSP, and get the url from that.
+     */
+    *isDefault = PR_FALSE;
+    ocspUrl = CERT_GetOCSPAuthorityInfoAccessLocation(cert);
+    if (!ocspUrl) {
+	CERT_StringFromCertFcn altFcn;
+
+	PR_EnterMonitor(OCSP_Global.monitor);
+	altFcn = OCSP_Global.alternateOCSPAIAFcn;
+	PR_ExitMonitor(OCSP_Global.monitor);
+	if (altFcn) {
+	    ocspUrl = (*altFcn)(cert);
+	    if (ocspUrl)
+		*isDefault = PR_TRUE;
+    	}
+    }
+    return ocspUrl;
+}
+
+/*
+ * Return SECSuccess if the cert was revoked *after* "time",
+ * SECFailure otherwise.
+ */
+static SECStatus
+ocsp_CertRevokedAfter(ocspRevokedInfo *revokedInfo, PRTime time)
+{
+    PRTime revokedTime;
+    SECStatus rv;
+
+    rv = DER_GeneralizedTimeToTime(&revokedTime, &revokedInfo->revocationTime);
+    if (rv != SECSuccess)
+	return rv;
+
+    /*
+     * Set the error even if we will return success; someone might care.
+     */
+    PORT_SetError(SEC_ERROR_REVOKED_CERTIFICATE);
+
+    if (LL_CMP(revokedTime, >, time))
+	return SECSuccess;
+
+    return SECFailure;
+}
+
+/*
+ * See if the cert represented in the single response had a good status
+ * at the specified time.
+ */
+SECStatus
+ocsp_CertHasGoodStatus(ocspCertStatus *status, PRTime time)
+{
+    SECStatus rv;
+    switch (status->certStatusType) {
+    case ocspCertStatus_good:
+        rv = SECSuccess;
+        break;
+    case ocspCertStatus_revoked:
+        rv = ocsp_CertRevokedAfter(status->certStatusInfo.revokedInfo, time);
+        break;
+    case ocspCertStatus_unknown:
+        PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT);
+        rv = SECFailure;
+        break;
+    case ocspCertStatus_other:
+    default:
+        PORT_Assert(0);
+        PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
+        rv = SECFailure;
+        break;
+    }
+    return rv;
+}
+
+static SECStatus
+ocsp_SingleResponseCertHasGoodStatus(CERTOCSPSingleResponse *single, 
+                                     PRTime time)
+{
+    return ocsp_CertHasGoodStatus(single->certStatus, time);
+}
+
+/* SECFailure means the arguments were invalid.
+ * On SECSuccess, the out parameters contain the OCSP status.
+ * rvOcsp contains the overall result of the OCSP operation.
+ * Depending on input parameter ignoreGlobalOcspFailureSetting,
+ * a soft failure might be converted into *rvOcsp=SECSuccess.
+ * If the cached attempt to obtain OCSP information had resulted
+ * in a failure, missingResponseError shows the error code of
+ * that failure.
+ * cacheFreshness is ocspMissing if no entry was found,
+ *                   ocspFresh if a fresh entry was found, or
+ *                   ocspStale if a stale entry was found.
+ */
+SECStatus
+ocsp_GetCachedOCSPResponseStatus(CERTOCSPCertID *certID,
+                                 PRTime time,
+                                 PRBool ignoreGlobalOcspFailureSetting,
+                                 SECStatus *rvOcsp,
+                                 SECErrorCodes *missingResponseError,
+                                 OCSPFreshness *cacheFreshness)
+{
+    OCSPCacheItem *cacheItem = NULL;
+  
+    if (!certID || !missingResponseError || !rvOcsp || !cacheFreshness) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+    *rvOcsp = SECFailure;
+    *missingResponseError = 0;
+    *cacheFreshness = ocspMissing;
+  
+    PR_EnterMonitor(OCSP_Global.monitor);
+    cacheItem = ocsp_FindCacheEntry(&OCSP_Global.cache, certID);
+    if (cacheItem) {
+        *cacheFreshness = ocsp_IsCacheItemFresh(cacheItem) ? ocspFresh
+                                                           : ocspStale;
+        /* having an arena means, we have a cached certStatus */
+        if (cacheItem->certStatusArena) {
+            *rvOcsp = ocsp_CertHasGoodStatus(&cacheItem->certStatus, time);
+            if (*rvOcsp != SECSuccess) {
+                *missingResponseError = PORT_GetError();
+            }
+        } else {
+            /*
+             * No status cached, the previous attempt failed.
+             * If OCSP is required, we never decide based on a failed attempt 
+             * However, if OCSP is optional, a recent OCSP failure is
+             * an allowed good state.
+             */
+            if (*cacheFreshness == ocspFresh &&
+                !ignoreGlobalOcspFailureSetting &&
+                OCSP_Global.ocspFailureMode == 
+                    ocspMode_FailureIsNotAVerificationFailure) {
+                *rvOcsp = SECSuccess;
+            }
+            *missingResponseError = cacheItem->missingResponseError;
+        }
+    }
+    PR_ExitMonitor(OCSP_Global.monitor);
+    return SECSuccess;
+}
+
+PRBool
+ocsp_FetchingFailureIsVerificationFailure(void)
+{
+    PRBool isFailure;
+
+    PR_EnterMonitor(OCSP_Global.monitor);
+    isFailure =
+        OCSP_Global.ocspFailureMode == ocspMode_FailureIsVerificationFailure;
+    PR_ExitMonitor(OCSP_Global.monitor);
+    return isFailure;
+}
+
+/*
+ * FUNCTION: CERT_CheckOCSPStatus
+ *   Checks the status of a certificate via OCSP.  Will only check status for
+ *   a certificate that has an AIA (Authority Information Access) extension
+ *   for OCSP *or* when a "default responder" is specified and enabled.
+ *   (If no AIA extension for OCSP and no default responder in place, the
+ *   cert is considered to have a good status and SECSuccess is returned.)
+ * INPUTS:
+ *   CERTCertDBHandle *handle
+ *     certificate DB of the cert that is being checked
+ *   CERTCertificate *cert
+ *     the certificate being checked
+ *   XXX in the long term also need a boolean parameter that specifies
+ *	whether to check the cert chain, as well; for now we check only
+ *	the leaf (the specified certificate)
+ *   PRTime time
+ *     time for which status is to be determined
+ *   void *pwArg
+ *     argument for password prompting, if needed
+ * RETURN:
+ *   Returns SECSuccess if an approved OCSP responder "knows" the cert
+ *   *and* returns a non-revoked status for it; SECFailure otherwise,
+ *   with an error set describing the reason:
+ *
+ *	SEC_ERROR_OCSP_BAD_HTTP_RESPONSE
+ *	SEC_ERROR_OCSP_FUTURE_RESPONSE
+ *	SEC_ERROR_OCSP_MALFORMED_REQUEST
+ *	SEC_ERROR_OCSP_MALFORMED_RESPONSE
+ *	SEC_ERROR_OCSP_OLD_RESPONSE
+ *	SEC_ERROR_OCSP_REQUEST_NEEDS_SIG
+ *	SEC_ERROR_OCSP_SERVER_ERROR
+ *	SEC_ERROR_OCSP_TRY_SERVER_LATER
+ *	SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST
+ *	SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE
+ *	SEC_ERROR_OCSP_UNKNOWN_CERT
+ *	SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS
+ *	SEC_ERROR_OCSP_UNKNOWN_RESPONSE_TYPE
+ *
+ *	SEC_ERROR_BAD_SIGNATURE
+ *	SEC_ERROR_CERT_BAD_ACCESS_LOCATION
+ *	SEC_ERROR_INVALID_TIME
+ *	SEC_ERROR_REVOKED_CERTIFICATE
+ *	SEC_ERROR_UNKNOWN_ISSUER
+ *	SEC_ERROR_UNKNOWN_SIGNER
+ *
+ *   Other errors are any of the many possible failures in cert verification
+ *   (e.g. SEC_ERROR_REVOKED_CERTIFICATE, SEC_ERROR_UNTRUSTED_ISSUER) when
+ *   verifying the signer's cert, or low-level problems (error allocating
+ *   memory, error performing ASN.1 decoding, etc.).
+ */    
+SECStatus 
+CERT_CheckOCSPStatus(CERTCertDBHandle *handle, CERTCertificate *cert,
+		     PRTime time, void *pwArg)
+{
+    CERTOCSPCertID *certID;
+    PRBool certIDWasConsumed = PR_FALSE;
+    SECStatus rv;
+    SECStatus rvOcsp;
+    SECErrorCodes cachedErrorCode;
+    OCSPFreshness cachedResponseFreshness;
+  
+    OCSP_TRACE_CERT(cert);
+    OCSP_TRACE_TIME("## requested validity time:", time);
+  
+    certID = CERT_CreateOCSPCertID(cert, time);
+    if (!certID)
+        return SECFailure;
+    rv = ocsp_GetCachedOCSPResponseStatus(
+        certID, time, PR_FALSE, /* ignoreGlobalOcspFailureSetting */
+        &rvOcsp, &cachedErrorCode, &cachedResponseFreshness);
+    if (rv != SECSuccess) {
+        CERT_DestroyOCSPCertID(certID);
+        return SECFailure;
+    }
+    if (cachedResponseFreshness == ocspFresh) {
+        CERT_DestroyOCSPCertID(certID);
+        if (rvOcsp != SECSuccess) {
+            PORT_SetError(cachedErrorCode);
+        }
+        return rvOcsp;
+    }
+
+    rv = ocsp_GetOCSPStatusFromNetwork(handle, certID, cert, time, pwArg,
+                                       &certIDWasConsumed, 
+                                       &rvOcsp);
+    if (rv != SECSuccess) {
+        PRErrorCode err = PORT_GetError();
+        if (ocsp_FetchingFailureIsVerificationFailure()) {
+            PORT_SetError(err);
+            rvOcsp = SECFailure;
+        } else if (cachedResponseFreshness == ocspStale &&
+                   (cachedErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT ||
+                    cachedErrorCode == SEC_ERROR_REVOKED_CERTIFICATE)) {
+            /* If we couldn't get a response for a certificate that the OCSP
+             * responder previously told us was bad, then assume it is still
+             * bad until we hear otherwise, as it is very unlikely that the
+             * certificate status has changed from "revoked" to "good" and it
+             * is also unlikely that the certificate status has changed from
+             * "unknown" to "good", except for some buggy OCSP responders.
+             */
+            PORT_SetError(cachedErrorCode);
+            rvOcsp = SECFailure;
+        } else {
+            rvOcsp = SECSuccess;
+        }
+    }
+    if (!certIDWasConsumed) {
+        CERT_DestroyOCSPCertID(certID);
+    }
+    return rvOcsp;
+}
+
+/*
+ * FUNCTION: CERT_CacheOCSPResponseFromSideChannel
+ *   First, this function checks the OCSP cache to see if a good response
+ *   for the given certificate already exists. If it does, then the function
+ *   returns successfully.
+ *
+ *   If not, then it validates that the given OCSP response is a valid,
+ *   good response for the given certificate and inserts it into the
+ *   cache.
+ *
+ *   This function is intended for use when OCSP responses are provided via a
+ *   side-channel, i.e. TLS OCSP stapling (a.k.a. the status_request extension).
+ *
+ * INPUTS:
+ *   CERTCertDBHandle *handle
+ *     certificate DB of the cert that is being checked
+ *   CERTCertificate *cert
+ *     the certificate being checked
+ *   PRTime time
+ *     time for which status is to be determined
+ *   SECItem *encodedResponse
+ *     the DER encoded bytes of the OCSP response
+ *   void *pwArg
+ *     argument for password prompting, if needed
+ * RETURN:
+ *   SECSuccess if the cert was found in the cache, or if the OCSP response was
+ *   found to be valid and inserted into the cache. SECFailure otherwise.
+ */
+SECStatus
+CERT_CacheOCSPResponseFromSideChannel(CERTCertDBHandle *handle,
+				      CERTCertificate *cert,
+				      PRTime time,
+				      const SECItem *encodedResponse,
+				      void *pwArg)
+{
+    CERTOCSPCertID *certID = NULL;
+    PRBool certIDWasConsumed = PR_FALSE;
+    SECStatus rv = SECFailure;
+    SECStatus rvOcsp = SECFailure;
+    SECErrorCodes dummy_error_code; /* we ignore this */
+    CERTOCSPResponse *decodedResponse = NULL;
+    CERTOCSPSingleResponse *singleResponse = NULL;
+    OCSPFreshness freshness;
+
+    /* The OCSP cache can be in three states regarding this certificate:
+     *    + Good (cached, timely, 'good' response, or revoked in the future)
+     *    + Revoked (cached, timely, but doesn't fit in the last category)
+     *    + Miss (no knowledge)
+     *
+     * Likewise, the side-channel information can be
+     *    + Good (timely, 'good' response, or revoked in the future)
+     *    + Revoked (timely, but doesn't fit in the last category)
+     *    + Invalid (bad syntax, bad signature, not timely etc)
+     *
+     * The common case is that the cache result is Good and so is the
+     * side-channel information. We want to save processing time in this case
+     * so we say that any time we see a Good result from the cache we return
+     * early.
+     *
+     *                       Cache result
+     *      | Good             Revoked               Miss
+     *   ---+--------------------------------------------
+     *    G |  noop           Cache more           Cache it
+     * S    |                 recent result
+     * i    |
+     * d    |
+     * e    |
+     *    R |  noop           Cache more           Cache it
+     * C    |                 recent result
+     * h    |
+     * a    |
+     * n    |
+     * n  I |  noop           Noop                  Noop
+     * e    |
+     * l    |
+     *
+     * When we fetch from the network we might choose to cache a negative
+     * result when the response is invalid. This saves us hammering, uselessly,
+     * at a broken responder. However, side channels are commonly attacker
+     * controlled and so we must not cache a negative result for an Invalid
+     * side channel.
+     */
+
+    if (!cert || !encodedResponse) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+    certID = CERT_CreateOCSPCertID(cert, time);
+    if (!certID)
+        return SECFailure;
+
+    /* We pass PR_TRUE for ignoreGlobalOcspFailureSetting so that a cached
+     * error entry is not interpreted as being a 'Good' entry here.
+     */
+    rv = ocsp_GetCachedOCSPResponseStatus(
+        certID, time, PR_TRUE, /* ignoreGlobalOcspFailureSetting */
+        &rvOcsp, &dummy_error_code, &freshness);
+    if (rv == SECSuccess && rvOcsp == SECSuccess && freshness == ocspFresh) {
+        /* The cached value is good. We don't want to waste time validating
+         * this OCSP response. This is the first column in the table above. */
+        CERT_DestroyOCSPCertID(certID);
+        return rv;
+    }
+
+    /* The logic for caching the more recent response is handled in
+     * ocsp_CacheSingleResponse. */
+
+    rv = ocsp_GetDecodedVerifiedSingleResponseForID(handle, certID, cert,
+						    time, pwArg,
+						    encodedResponse,
+						    &decodedResponse,
+						    &singleResponse);
+    if (rv == SECSuccess) {
+	rvOcsp = ocsp_SingleResponseCertHasGoodStatus(singleResponse, time);
+	/* Cache any valid singleResponse, regardless of status. */
+	ocsp_CacheSingleResponse(certID, singleResponse, &certIDWasConsumed);
+    }
+    if (decodedResponse) {
+	CERT_DestroyOCSPResponse(decodedResponse);
+    }
+    if (!certIDWasConsumed) {
+        CERT_DestroyOCSPCertID(certID);
+    }
+    return rv == SECSuccess ? rvOcsp : rv;
+}
+
+/*
+ * Status in *certIDWasConsumed will always be correct, regardless of 
+ * return value.
+ */
+static SECStatus
+ocsp_GetOCSPStatusFromNetwork(CERTCertDBHandle *handle, 
+                              CERTOCSPCertID *certID, 
+                              CERTCertificate *cert, 
+                              PRTime time,
+                              void *pwArg,
+                              PRBool *certIDWasConsumed,
+                              SECStatus *rv_ocsp)
+{
+    char *location = NULL;
+    PRBool locationIsDefault;
+    SECItem *encodedResponse = NULL;
+    CERTOCSPRequest *request = NULL;
+    SECStatus rv = SECFailure;
+
+    CERTOCSPResponse *decodedResponse = NULL;
+    CERTOCSPSingleResponse *singleResponse = NULL;
+    enum { stageGET, stagePOST } currentStage;
+    PRBool retry = PR_FALSE;
+
+    if (!certIDWasConsumed || !rv_ocsp) {
+        PORT_SetError(SEC_ERROR_INVALID_ARGS);
+        return SECFailure;
+    }
+    *certIDWasConsumed = PR_FALSE;
+    *rv_ocsp = SECFailure;
+
+    if (!OCSP_Global.monitor) {
+        PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+        return SECFailure;
+    }
+    PR_EnterMonitor(OCSP_Global.monitor);
+    if (OCSP_Global.forcePost) {
+        currentStage = stagePOST;
+    } else {
+        currentStage = stageGET;
+    }
+    PR_ExitMonitor(OCSP_Global.monitor);
+
+    /*
+     * The first thing we need to do is find the location of the responder.
+     * This will be the value of the default responder (if enabled), else
+     * it will come out of the AIA extension in the cert (if present).
+     * If we have no such location, then this cert does not "deserve" to
+     * be checked -- that is, we consider it a success and just return.
+     * The way we tell that is by looking at the error number to see if
+     * the problem was no AIA extension was found; any other error was
+     * a true failure that we unfortunately have to treat as an overall
+     * failure here.
+     */
+    location = ocsp_GetResponderLocation(handle, cert, PR_TRUE,
+                                         &locationIsDefault);
+    if (location == NULL) {
+       int err = PORT_GetError();
+       if (err == SEC_ERROR_EXTENSION_NOT_FOUND ||
+           err == SEC_ERROR_CERT_BAD_ACCESS_LOCATION) {
+           PORT_SetError(0);
+           *rv_ocsp = SECSuccess;
+           return SECSuccess;
+       }
+       return SECFailure;
+    }
+
+    /*
+     * XXX In the fullness of time, we will want/need to handle a
+     * certificate chain.  This will be done either when a new parameter
+     * tells us to, or some configuration variable tells us to.  In any
+     * case, handling it is complicated because we may need to send as
+     * many requests (and receive as many responses) as we have certs
+     * in the chain.  If we are going to talk to a default responder,
+     * and we only support one default responder, we can put all of the
+     * certs together into one request.  Otherwise, we must break them up
+     * into multiple requests.  (Even if all of the requests will go to
+     * the same location, the signature on each response will be different,
+     * because each issuer is different.  Carefully read the OCSP spec
+     * if you do not understand this.)
+     */
+
+    /*
+     * XXX If/when signing of requests is supported, that second NULL
+     * should be changed to be the signer certificate.  Not sure if that
+     * should be passed into this function or retrieved via some operation
+     * on the handle/context.
+     */
+
+    do {
+	const char *method;
+	PRBool validResponseWithAccurateInfo = PR_FALSE;
+	retry = PR_FALSE;
+	*rv_ocsp = SECFailure;
+
+	if (currentStage == stageGET) {
+	    method = "GET";
+	} else {
+	    PORT_Assert(currentStage == stagePOST);
+	    method = "POST";
+	}
+
+	encodedResponse = 
+	    ocsp_GetEncodedOCSPResponseForSingleCert(NULL, certID, cert,
+						     location, method,
+						     time, locationIsDefault,
+						     pwArg, &request);
+
+	if (encodedResponse) {
+	    rv = ocsp_GetDecodedVerifiedSingleResponseForID(handle, certID, cert,
+							    time, pwArg,
+							    encodedResponse,
+							    &decodedResponse,
+							    &singleResponse);
+	    if (rv == SECSuccess) {
+		switch (singleResponse->certStatus->certStatusType) {
+		    case ocspCertStatus_good:
+		    case ocspCertStatus_revoked:
+			validResponseWithAccurateInfo = PR_TRUE;
+			break;
+		    default:
+			break;
+		}
+		*rv_ocsp = ocsp_SingleResponseCertHasGoodStatus(singleResponse, time);
+	    }
+	}
+
+	if (currentStage == stageGET) {
+	    /* only accept GET response if good or revoked */
+	    if (validResponseWithAccurateInfo) {
+		ocsp_CacheSingleResponse(certID, singleResponse, 
+					 certIDWasConsumed);
+	    } else {
+		retry = PR_TRUE;
+		currentStage = stagePOST;
+	    }
+	} else {
+	    /* cache the POST respone, regardless of status */
+	    if (!singleResponse) {
+		cert_RememberOCSPProcessingFailure(certID, certIDWasConsumed);
+	    } else {
+		ocsp_CacheSingleResponse(certID, singleResponse, 
+					 certIDWasConsumed);
+	    }
+	}
+
+	if (encodedResponse) {
+	    SECITEM_FreeItem(encodedResponse, PR_TRUE);
+	    encodedResponse = NULL;
+	}
+	if (request) {
+	    CERT_DestroyOCSPRequest(request);
+	    request = NULL;
+	}
+	if (decodedResponse) {
+	    CERT_DestroyOCSPResponse(decodedResponse);
+	    decodedResponse = NULL;
+	}
+	singleResponse = NULL;
+
+    } while (retry);
+
+    PORT_Free(location);
+    return rv;
+}
+
+/*
+ * FUNCTION: ocsp_GetDecodedVerifiedSingleResponseForID
+ *   This function decodes an OCSP response and checks for a valid response
+ *   concerning the given certificate.
+ *
+ *   Note: a 'valid' response is one that parses successfully, is not an OCSP
+ *   exception (see RFC 2560 Section 2.3), is correctly signed and is current.
+ *   A 'good' response is a valid response that attests that the certificate
+ *   is not currently revoked (see RFC 2560 Section 2.2).
+ *
+ * INPUTS:
+ *   CERTCertDBHandle *handle
+ *     certificate DB of the cert that is being checked
+ *   CERTOCSPCertID *certID
+ *     the cert ID corresponding to |cert|
+ *   CERTCertificate *cert
+ *     the certificate being checked
+ *   PRTime time
+ *     time for which status is to be determined
+ *   void *pwArg
+ *     the opaque argument to the password prompting function.
+ *   SECItem *encodedResponse
+ *     the DER encoded bytes of the OCSP response
+ *   CERTOCSPResponse **pDecodedResponse
+ *     (output) The caller must ALWAYS check for this output parameter,
+ *     and if it's non-null, must destroy it using CERT_DestroyOCSPResponse.
+ *   CERTOCSPSingleResponse **pSingle
+ *     (output) on success, this points to the single response that corresponds
+ *     to the certID parameter. Points to the inside of pDecodedResponse.
+ *     It isn't a copy, don't free it.
+ * RETURN:
+ *   SECSuccess iff the response is valid.
+ */
+static SECStatus
+ocsp_GetDecodedVerifiedSingleResponseForID(CERTCertDBHandle *handle,
+					   CERTOCSPCertID *certID,
+					   CERTCertificate *cert,
+					   PRTime time,
+					   void *pwArg,
+					   const SECItem *encodedResponse,
+					   CERTOCSPResponse **pDecodedResponse,
+					   CERTOCSPSingleResponse **pSingle)
+{
+    CERTCertificate *signerCert = NULL;
+    CERTCertificate *issuerCert = NULL;
+    SECStatus rv = SECFailure;
+
+    if (!pSingle || !pDecodedResponse) {
+	return SECFailure;
+    }
+    *pSingle = NULL;
+    *pDecodedResponse = CERT_DecodeOCSPResponse(encodedResponse);
+    if (!*pDecodedResponse) {
+	return SECFailure;
+    }
+
+    /*
+     * Okay, we at least have a response that *looks* like a response!
+     * Now see if the overall response status value is good or not.
+     * If not, we set an error and give up.  (It means that either the
+     * server had a problem, or it didn't like something about our
+     * request.  Either way there is nothing to do but give up.)
+     * Otherwise, we continue to find the actual per-cert status
+     * in the response.
+     */
+    if (CERT_GetOCSPResponseStatus(*pDecodedResponse) != SECSuccess) {
+	goto loser;
+    }
+
+    /*
+     * If we've made it this far, we expect a response with a good signature.
+     * So, check for that.
+     */
+    issuerCert = CERT_FindCertIssuer(cert, time, certUsageAnyCA);
+    rv = CERT_VerifyOCSPResponseSignature(*pDecodedResponse, handle, pwArg,
+                                          &signerCert, issuerCert);
+    if (rv != SECSuccess) {
+	goto loser;
+    }
+
+    PORT_Assert(signerCert != NULL);	/* internal consistency check */
+    /* XXX probably should set error, return failure if signerCert is null */
+
+    /*
+     * Again, we are only doing one request for one cert.
+     * XXX When we handle cert chains, the following code will obviously
+     * have to be modified, in coordation with the code above that will
+     * have to determine how to make multiple requests, etc. 
+     */
+    rv = ocsp_GetVerifiedSingleResponseForCertID(handle, *pDecodedResponse, certID, 
+                                                 signerCert, time, pSingle);
+loser:
+    if (issuerCert != NULL)
+	CERT_DestroyCertificate(issuerCert);
+    if (signerCert != NULL)
+	CERT_DestroyCertificate(signerCert);
+    return rv;
+}
+
+/*
+ * FUNCTION: ocsp_CacheSingleResponse
+ *   This function requires that the caller has checked that the response
+ *   is valid and verified. 
+ *   The (positive or negative) valid response will be used to update the cache.
+ * INPUTS:
+ *   CERTOCSPCertID *certID
+ *     the cert ID corresponding to |cert|
+ *   PRBool *certIDWasConsumed
+ *     (output) on return, this is true iff |certID| was consumed by this
+ *     function.
+ */
+void
+ocsp_CacheSingleResponse(CERTOCSPCertID *certID,
+			 CERTOCSPSingleResponse *single,
+			 PRBool *certIDWasConsumed)
+{
+    if (single != NULL) {
+	PR_EnterMonitor(OCSP_Global.monitor);
+	if (OCSP_Global.maxCacheEntries >= 0) {
+	    ocsp_CreateOrUpdateCacheEntry(&OCSP_Global.cache, certID, single,
+					  certIDWasConsumed);
+	    /* ignore cache update failures */
+	}
+	PR_ExitMonitor(OCSP_Global.monitor);
+    }
+}
+
+SECStatus
+ocsp_GetVerifiedSingleResponseForCertID(CERTCertDBHandle *handle, 
+                                        CERTOCSPResponse *response, 
+                                        CERTOCSPCertID   *certID,
+                                        CERTCertificate  *signerCert,
+                                        PRTime            time,
+                                        CERTOCSPSingleResponse 
+                                            **pSingleResponse)
+{
+    SECStatus rv;
+    ocspResponseData *responseData;
+    PRTime producedAt;
+    CERTOCSPSingleResponse *single;
+
+    /*
+     * The ResponseData part is the real guts of the response.
+     */
+    responseData = ocsp_GetResponseData(response, NULL);
+    if (responseData == NULL) {
+        rv = SECFailure;
+        goto loser;
+    }
+
+    /*
+     * There is one producedAt time for the entire response (and a separate
+     * thisUpdate time for each individual single response).  We need to
+     * compare them, so get the overall time to pass into the check of each
+     * single response.
+     */
+    rv = DER_GeneralizedTimeToTime(&producedAt, &responseData->producedAt);
+    if (rv != SECSuccess)
+        goto loser;
+
+    single = ocsp_GetSingleResponseForCertID(responseData->responses,
+                                             handle, certID);
+    if (single == NULL) {
+        rv = SECFailure;
+        goto loser;
+    }
+
+    rv = ocsp_VerifySingleResponse(single, handle, signerCert, producedAt);
+    if (rv != SECSuccess)
+        goto loser;
+    *pSingleResponse = single;
+
+loser:
+    return rv;
+}
+
+SECStatus
+CERT_GetOCSPStatusForCertID(CERTCertDBHandle *handle, 
+                            CERTOCSPResponse *response, 
+                            CERTOCSPCertID   *certID,
+                            CERTCertificate  *signerCert,
+                            PRTime            time)
+{
+    /*
+     * We do not update the cache, because:
+     *
+     * CERT_GetOCSPStatusForCertID is an old exported API that was introduced
+     * before the OCSP cache got implemented.
+     *
+     * The implementation of helper function cert_ProcessOCSPResponse
+     * requires the ability to transfer ownership of the the given certID to
+     * the cache. The external API doesn't allow us to prevent the caller from
+     * destroying the certID. We don't have the original certificate available,
+     * therefore we are unable to produce another certID object (that could 
+     * be stored in the cache).
+     *
+     * Should we ever implement code to produce a deep copy of certID,
+     * then this could be changed to allow updating the cache.
+     * The duplication would have to be done in 
+     * cert_ProcessOCSPResponse, if the out parameter to indicate
+     * a transfer of ownership is NULL.
+     */
+    return cert_ProcessOCSPResponse(handle, response, certID, 
+                                    signerCert, time, 
+                                    NULL, NULL);
+}
+
+/*
+ * The first 5 parameters match the definition of CERT_GetOCSPStatusForCertID.
+ */
+SECStatus
+cert_ProcessOCSPResponse(CERTCertDBHandle *handle, 
+                         CERTOCSPResponse *response, 
+                         CERTOCSPCertID   *certID,
+                         CERTCertificate  *signerCert,
+                         PRTime            time,
+                         PRBool           *certIDWasConsumed,
+                         SECStatus        *cacheUpdateStatus)
+{
+    SECStatus rv;
+    SECStatus rv_cache = SECSuccess;
+    CERTOCSPSingleResponse *single = NULL;
+
+    rv = ocsp_GetVerifiedSingleResponseForCertID(handle, response, certID, 
+                                                 signerCert, time, &single);
+    if (rv == SECSuccess) {
+        /*
+         * Check whether the status says revoked, and if so 
+         * how that compares to the time value passed into this routine.
+         */
+        rv = ocsp_SingleResponseCertHasGoodStatus(single, time);
+    }
+
+    if (certIDWasConsumed) {
+        /*
+         * We don't have copy-of-certid implemented. In order to update 
+         * the cache, the caller must supply an out variable 
+         * certIDWasConsumed, allowing us to return ownership status.
+         */
+  
+        PR_EnterMonitor(OCSP_Global.monitor);
+        if (OCSP_Global.maxCacheEntries >= 0) {
+            /* single == NULL means: remember response failure */
+            rv_cache = 
+                ocsp_CreateOrUpdateCacheEntry(&OCSP_Global.cache, certID,
+                                              single, certIDWasConsumed);
+        }
+        PR_ExitMonitor(OCSP_Global.monitor);
+        if (cacheUpdateStatus) {
+            *cacheUpdateStatus = rv_cache;
+        }
+    }
+
+    return rv;
+}
+
+SECStatus
+cert_RememberOCSPProcessingFailure(CERTOCSPCertID *certID,
+                                   PRBool         *certIDWasConsumed)
+{
+    SECStatus rv = SECSuccess;
+    PR_EnterMonitor(OCSP_Global.monitor);
+    if (OCSP_Global.maxCacheEntries >= 0) {
+        rv = ocsp_CreateOrUpdateCacheEntry(&OCSP_Global.cache, certID, NULL, 
+                                           certIDWasConsumed);
+    }
+    PR_ExitMonitor(OCSP_Global.monitor);
+    return rv;
+}
+
+/*
+ * Disable status checking and destroy related structures/data.
+ */
+static SECStatus
+ocsp_DestroyStatusChecking(CERTStatusConfig *statusConfig)
+{
+    ocspCheckingContext *statusContext;
+
+    /*
+     * Disable OCSP checking
+     */
+    statusConfig->statusChecker = NULL;
+
+    statusContext = statusConfig->statusContext;
+    PORT_Assert(statusContext != NULL);
+    if (statusContext == NULL)
+	return SECFailure;
+
+    if (statusContext->defaultResponderURI != NULL)
+	PORT_Free(statusContext->defaultResponderURI);
+    if (statusContext->defaultResponderNickname != NULL)
+	PORT_Free(statusContext->defaultResponderNickname);
+
+    PORT_Free(statusContext);
+    statusConfig->statusContext = NULL;
+
+    PORT_Free(statusConfig);
+
+    return SECSuccess;
+}
+
+
+/*
+ * FUNCTION: CERT_DisableOCSPChecking
+ *   Turns off OCSP checking for the given certificate database.
+ *   This routine disables OCSP checking.  Though it will return
+ *   SECFailure if OCSP checking is not enabled, it is "safe" to
+ *   call it that way and just ignore the return value, if it is
+ *   easier to just call it than to "remember" whether it is enabled.
+ * INPUTS:
+ *   CERTCertDBHandle *handle
+ *     Certificate database for which OCSP checking will be disabled.
+ * RETURN:
+ *   Returns SECFailure if an error occurred (usually means that OCSP
+ *   checking was not enabled or status contexts were not initialized --
+ *   error set will be SEC_ERROR_OCSP_NOT_ENABLED); SECSuccess otherwise.
+ */
+SECStatus
+CERT_DisableOCSPChecking(CERTCertDBHandle *handle)
+{
+    CERTStatusConfig *statusConfig;
+    ocspCheckingContext *statusContext;
+
+    if (handle == NULL) {
+	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+	return SECFailure;
+    }
+
+    statusConfig = CERT_GetStatusConfig(handle);
+    statusContext = ocsp_GetCheckingContext(handle);
+    if (statusContext == NULL)
+	return SECFailure;
+
+    if (statusConfig->statusChecker != CERT_CheckOCSPStatus) {
+	/*
+	 * Status configuration is present, but either not currently
+	 * enabled or not for OCSP.
+	 */
+	PORT_SetError(SEC_ERROR_OCSP_NOT_ENABLED);
+	return SECFailure;
+    }
+
+    /* cache no longer necessary */
+    CERT_ClearOCSPCache();
+
+    /*
+     * This is how we disable status checking.  Everything else remains
+     * in place in case we are enabled again.
+     */
+    statusConfig->statusChecker = NULL;
+
+    return SECSuccess;
+}
+
+/*
+ * Allocate and initialize the informational structures for status checking.
+ * This is done when some configuration of OCSP is being done or when OCSP
+ * checking is being turned on, whichever comes first.
+ */
+static SECStatus
+ocsp_InitStatusChecking(CERTCertDBHandle *handle)
+{
+    CERTStatusConfig *statusConfig = NULL;
+    ocspCheckingContext *statusContext = NULL;
+
+    PORT_Assert(CERT_GetStatusConfig(handle) == NULL);
+    if (CERT_GetStatusConfig(handle) != NULL) {
+	/* XXX or call statusConfig->statusDestroy and continue? */
+	return SECFailure;
+    }
+
+    statusConfig = PORT_ZNew(CERTStatusConfig);
+    if (statusConfig == NULL)
+	goto loser;
+
+    statusContext = PORT_ZNew(ocspCheckingContext);
+    if (statusContext == NULL)
+	goto loser;
+
+    statusConfig->statusDestroy = ocsp_DestroyStatusChecking;
+    statusConfig->statusContext = statusContext;
+
+    CERT_SetStatusConfig(handle, statusConfig);
+
+    return SECSuccess;
+
+loser:
+    if (statusConfig != NULL)
+	PORT_Free(statusConfig);
+    return SECFailure;
+}
+
+
+/*
+ * FUNCTION: CERT_EnableOCSPChecking
+ *   Turns on OCSP checking for the given certificate database.
+ * INPUTS:
+ *   CERTCertDBHandle *handle
+ *     Certificate database for which OCSP checking will be enabled.
+ * RETURN:
+ *   Returns SECFailure if an error occurred (likely only problem
+ *   allocating memory); SECSuccess otherwise.
+ */
+SECStatus
+CERT_EnableOCSPChecking(CERTCertDBHandle *handle)
+{
+    CERTStatusConfig *statusConfig;
+    
+    SECStatus rv;
+
+    if (handle == NULL) {
+	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+	return SECFailure;
+    }
+
+    statusConfig = CERT_GetStatusConfig(handle);
+    if (statusConfig == NULL) {
+	rv = ocsp_InitStatusChecking(handle);
+	if (rv != SECSuccess)
+	    return rv;
+
+	/* Get newly established value */
+	statusConfig = CERT_GetStatusConfig(handle);
+	PORT_Assert(statusConfig != NULL);
+    }
+
+    /*
+     * Setting the checker function is what really enables the checking
+     * when each cert verification is done.
+     */
+    statusConfig->statusChecker = CERT_CheckOCSPStatus;
+
+    return SECSuccess;
+}
+
+
+/*
+ * FUNCTION: CERT_SetOCSPDefaultResponder
+ *   Specify the location and cert of the default responder.
+ *   If OCSP checking is already enabled *and* use of a default responder
+ *   is also already enabled, all OCSP checking from now on will go directly
+ *   to the specified responder.  If OCSP checking is not enabled, or if
+ *   it is but use of a default responder is not enabled, the information
+ *   will be recorded and take effect whenever both are enabled.
+ * INPUTS:
+ *   CERTCertDBHandle *handle
+ *     Cert database on which OCSP checking should use the default responder.
+ *   char *url
+ *     The location of the default responder (e.g. "http://foo.com:80/ocsp")
+ *     Note that the location will not be tested until the first attempt
+ *     to send a request there.
+ *   char *name
+ *     The nickname of the cert to trust (expected) to sign the OCSP responses.
+ *     If the corresponding cert cannot be found, SECFailure is returned.
+ * RETURN:
+ *   Returns SECFailure if an error occurred; SECSuccess otherwise.
+ *   The most likely error is that the cert for "name" could not be found
+ *   (probably SEC_ERROR_UNKNOWN_CERT).  Other errors are low-level (no memory,
+ *   bad database, etc.).
+ */
+SECStatus
+CERT_SetOCSPDefaultResponder(CERTCertDBHandle *handle,
+			     const char *url, const char *name)
+{
+    CERTCertificate *cert;
+    ocspCheckingContext *statusContext;
+    char *url_copy = NULL;
+    char *name_copy = NULL;
+    SECStatus rv;
+
+    if (handle == NULL || url == NULL || name == NULL) {
+	/*
+	 * XXX When interface is exported, probably want better errors;
+	 * perhaps different one for each parameter.
+	 */
+	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+	return SECFailure;
+    }
+
+    /*
+     * Find the certificate for the specified nickname.  Do this first
+     * because it seems the most likely to fail.
+     *
+     * XXX Shouldn't need that cast if the FindCertByNickname interface
+     * used const to convey that it does not modify the name.  Maybe someday.
+     */
+    cert = CERT_FindCertByNickname(handle, (char *) name);
+    if (cert == NULL) {
+      /*
+       * look for the cert on an external token.
+       */
+      cert = PK11_FindCertFromNickname((char *)name, NULL);
+    }
+    if (cert == NULL)
+	return SECFailure;
+
+    /*
+     * Make a copy of the url and nickname.
+     */
+    url_copy = PORT_Strdup(url);
+    name_copy = PORT_Strdup(name);
+    if (url_copy == NULL || name_copy == NULL) {
+	rv = SECFailure;
+	goto loser;
+    }
+
+    statusContext = ocsp_GetCheckingContext(handle);
+
+    /*
+     * Allocate and init the context if it doesn't already exist.
+     */
+    if (statusContext == NULL) {
+	rv = ocsp_InitStatusChecking(handle);
+	if (rv != SECSuccess)
+	    goto loser;
+
+	statusContext = ocsp_GetCheckingContext(handle);
+	PORT_Assert(statusContext != NULL);	/* extreme paranoia */
+    }
+
+    /*
+     * Note -- we do not touch the status context until after all of
+     * the steps which could cause errors.  If something goes wrong,
+     * we want to leave things as they were.
+     */
+
+    /*
+     * Get rid of old url and name if there.
+     */
+    if (statusContext->defaultResponderNickname != NULL)
+	PORT_Free(statusContext->defaultResponderNickname);
+    if (statusContext->defaultResponderURI != NULL)
+	PORT_Free(statusContext->defaultResponderURI);
+
+    /*
+     * And replace them with the new ones.
+     */
+    statusContext->defaultResponderURI = url_copy;
+    statusContext->defaultResponderNickname = name_copy;
+
+    /*
+     * If there was already a cert in place, get rid of it and replace it.
+     * Otherwise, we are not currently enabled, so we don't want to save it;
+     * it will get re-found and set whenever use of a default responder is
+     * enabled.
+     */
+    if (statusContext->defaultResponderCert != NULL) {
+	CERT_DestroyCertificate(statusContext->defaultResponderCert);
+	statusContext->defaultResponderCert = cert;
+        /*OCSP enabled, switching responder: clear cache*/
+        CERT_ClearOCSPCache();
+    } else {
+	PORT_Assert(statusContext->useDefaultResponder == PR_FALSE);
+	CERT_DestroyCertificate(cert);
+        /*OCSP currently not enabled, no need to clear cache*/
+    }
+
+    return SECSuccess;
+
+loser:
+    CERT_DestroyCertificate(cert);
+    if (url_copy != NULL)
+	PORT_Free(url_copy);
+    if (name_copy != NULL)
+	PORT_Free(name_copy);
+    return rv;
+}
+
+
+/*
+ * FUNCTION: CERT_EnableOCSPDefaultResponder
+ *   Turns on use of a default responder when OCSP checking.
+ *   If OCSP checking is already enabled, this will make subsequent checks
+ *   go directly to the default responder.  (The location of the responder
+ *   and the nickname of the responder cert must already be specified.)
+ *   If OCSP checking is not enabled, this will be recorded and take effect
+ *   whenever it is enabled.
+ * INPUTS:
+ *   CERTCertDBHandle *handle
+ *     Cert database on which OCSP checking should use the default responder.
+ * RETURN:
+ *   Returns SECFailure if an error occurred; SECSuccess otherwise.
+ *   No errors are especially likely unless the caller did not previously
+ *   perform a successful call to SetOCSPDefaultResponder (in which case
+ *   the error set will be SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER).
+ */
+SECStatus
+CERT_EnableOCSPDefaultResponder(CERTCertDBHandle *handle)
+{
+    ocspCheckingContext *statusContext;
+    CERTCertificate *cert;
+    SECStatus rv;
+    SECCertificateUsage usage;
+
+    if (handle == NULL) {
+	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+	return SECFailure;
+    }
+
+    statusContext = ocsp_GetCheckingContext(handle);
+
+    if (statusContext == NULL) {
+	/*
+	 * Strictly speaking, the error already set is "correct",
+	 * but cover over it with one more helpful in this context.
+	 */
+	PORT_SetError(SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER);
+	return SECFailure;
+    }
+
+    if (statusContext->defaultResponderURI == NULL) {
+	PORT_SetError(SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER);
+	return SECFailure;
+    }
+
+    if (statusContext->defaultResponderNickname == NULL) {
+	PORT_SetError(SEC_ERROR_OCSP_NO_DEFAULT_RESPONDER);
+	return SECFailure;
+    }
+
+    /*
+     * Find the cert for the nickname.
+     */
+    cert = CERT_FindCertByNickname(handle,
+				   statusContext->defaultResponderNickname);
+    if (cert == NULL) {
+        cert = PK11_FindCertFromNickname(statusContext->defaultResponderNickname,
+                                         NULL);
+    }
+    /*
+     * We should never have trouble finding the cert, because its
+     * existence should have been proven by SetOCSPDefaultResponder.
+     */
+    PORT_Assert(cert != NULL);
+    if (cert == NULL)
+	return SECFailure;
+
+   /*
+    * Supplied cert should at least have  a signing capability in order for us
+    * to use it as a trusted responder cert. Ability to sign is guaranteed  if
+    * cert is validated to have any set of the usages below.
+    */
+    rv = CERT_VerifyCertificateNow(handle, cert, PR_TRUE,
+                                   certificateUsageCheckAllUsages,
+                                   NULL, &usage);
+    if (rv != SECSuccess || (usage & (certificateUsageSSLClient |
+                                      certificateUsageSSLServer |
+                                      certificateUsageSSLServerWithStepUp |
+                                      certificateUsageEmailSigner |
+                                      certificateUsageObjectSigner |
+                                      certificateUsageStatusResponder |
+                                      certificateUsageSSLCA)) == 0) {
+	PORT_SetError(SEC_ERROR_OCSP_RESPONDER_CERT_INVALID);
+	return SECFailure;
+    }
+
+    /*
+     * And hang onto it.
+     */
+    statusContext->defaultResponderCert = cert;
+
+    /* we don't allow a mix of cache entries from different responders */
+    CERT_ClearOCSPCache();
+
+    /*
+     * Finally, record the fact that we now have a default responder enabled.
+     */
+    statusContext->useDefaultResponder = PR_TRUE;
+    return SECSuccess;
+}
+
+
+/*
+ * FUNCTION: CERT_DisableOCSPDefaultResponder
+ *   Turns off use of a default responder when OCSP checking.
+ *   (Does nothing if use of a default responder is not enabled.)
+ * INPUTS:
+ *   CERTCertDBHandle *handle
+ *     Cert database on which OCSP checking should stop using a default
+ *     responder.
+ * RETURN:
+ *   Returns SECFailure if an error occurred; SECSuccess otherwise.
+ *   Errors very unlikely (like random memory corruption...).
+ */
+SECStatus
+CERT_DisableOCSPDefaultResponder(CERTCertDBHandle *handle)
+{
+    CERTStatusConfig *statusConfig;
+    ocspCheckingContext *statusContext;
+    CERTCertificate *tmpCert;
+
+    if (handle == NULL) {
+	PORT_SetError(SEC_ERROR_INVALID_ARGS);
+	return SECFailure;
+    }
+
+    statusConfig = CERT_GetStatusConfig(handle);
+    if (statusConfig == NULL)
+	return SECSuccess;
+
+    statusContext = ocsp_GetCheckingContext(handle);
+    PORT_Assert(statusContext != NULL);
+    if (statusContext == NULL)
+	return SECFailure;
+
+    tmpCert = statusContext->defaultResponderCert;
+    if (tmpCert) {
+	statusContext->defaultResponderCert = NULL;
+	CERT_DestroyCertificate(tmpCert);
+        /* we don't allow a mix of cache entries from different responders */
+        CERT_ClearOCSPCache();
+    }
+
+    /*
+     * Finally, record the fact.
+     */
+    statusContext->useDefaultResponder = PR_FALSE;
+    return SECSuccess;
+}
+
+SECStatus
+CERT_ForcePostMethodForOCSP(PRBool forcePost)
+{
+    if (!OCSP_Global.monitor) {
+        PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+        return SECFailure;
+    }
+
+    PR_EnterMonitor(OCSP_Global.monitor);
+    OCSP_Global.forcePost = forcePost;
+    PR_ExitMonitor(OCSP_Global.monitor);
+
+    return SECSuccess;
+}
+
+SECStatus
+CERT_GetOCSPResponseStatus(CERTOCSPResponse *response)
+{
+    PORT_Assert(response);
+    if (response->statusValue == ocspResponse_successful)
+	return SECSuccess;
+
+    switch (response->statusValue) {
+      case ocspResponse_malformedRequest:
+	PORT_SetError(SEC_ERROR_OCSP_MALFORMED_REQUEST);
+	break;
+      case ocspResponse_internalError:
+	PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR);
+	break;
+      case ocspResponse_tryLater:
+	PORT_SetError(SEC_ERROR_OCSP_TRY_SERVER_LATER);
+	break;
+      case ocspResponse_sigRequired:
+	/* XXX We *should* retry with a signature, if possible. */
+	PORT_SetError(SEC_ERROR_OCSP_REQUEST_NEEDS_SIG);
+	break;
+      case ocspResponse_unauthorized:
+	PORT_SetError(SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST);
+	break;
+      case ocspResponse_unused:
+      default:
+	PORT_SetError(SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS);
+	break;
+    }
+    return SECFailure;
+}
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)