Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/security/nss/lib/certhigh/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 198 kB image not shown  

Quelle  ocsp.c   Sprache: C

 
/* 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_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 = PR_GetEnvSecure("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.
     */

    OCSP_TRACE(("OCSP ocsp_RemoveCacheItem, THREADID %p\n", PR_GetCurrentThread()));
    PR_EnterMonitor(OCSP_Global.monitor);

    ocsp_RemoveCacheItemFromLinkedList(cache, item);
#ifdef DEBUG
    {
        PRBool couldRemoveFromHashTable = PL_HashTableRemove(cache->entries,
                                                             item->certID);
        PORT_Assert(couldRemoveFromHashTable);
    }
#else
    PL_HashTableRemove(cache->entries, item->certID);
#endif
    --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);
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=97 H=80 G=88

¤ Dauer der Verarbeitung: 0.10 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.