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

Quelle  pcertdb.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/. */


/*
 * Permanent Certificate database handling code
 */

#include "lowkeyti.h"
#include "pcert.h"
#include "mcom_db.h"
#include "pcert.h"
#include "secitem.h"
#include "secder.h"

#include "secerr.h"
#include "lgdb.h"

/* forward declaration */
NSSLOWCERTCertificate *
nsslowcert_FindCertByDERCertNoLocking(NSSLOWCERTCertDBHandle *handle, SECItem *derCert);
static SECStatus
nsslowcert_UpdateSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle,
                              char *emailAddr, SECItem *derSubject, SECItem *emailProfile,
                              SECItem *profileTime);
static SECStatus
nsslowcert_UpdatePermCert(NSSLOWCERTCertDBHandle *dbhandle,
                          NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust);
static SECStatus
nsslowcert_UpdateCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl,
                     SECItem *crlKey, char *url, PRBool isKRL);

static NSSLOWCERTCertificate *certListHead = NULL;
static NSSLOWCERTTrust *trustListHead = NULL;
static certDBEntryCert *entryListHead = NULL;
static int certListCount = 0;
static int trustListCount = 0;
static int entryListCount = 0;
#define MAX_CERT_LIST_COUNT 10
#define MAX_TRUST_LIST_COUNT 10
#define MAX_ENTRY_LIST_COUNT 10

/*
 * the following functions are wrappers for the db library that implement
 * a global lock to make the database thread safe.
 */

static PZLock *dbLock = NULL;
static PZLock *certRefCountLock = NULL;
static PZLock *certTrustLock = NULL;
static PZLock *freeListLock = NULL;

void
certdb_InitDBLock(NSSLOWCERTCertDBHandle *handle)
{
    if (dbLock == NULL) {
        dbLock = PZ_NewLock(nssILockCertDB);
        PORT_Assert(dbLock != NULL);
    }
}

SECStatus
nsslowcert_InitLocks(void)
{
    if (freeListLock == NULL) {
        freeListLock = PZ_NewLock(nssILockRefLock);
        if (freeListLock == NULL) {
            return SECFailure;
        }
    }
    if (certRefCountLock == NULL) {
        certRefCountLock = PZ_NewLock(nssILockRefLock);
        if (certRefCountLock == NULL) {
            return SECFailure;
        }
    }
    if (certTrustLock == NULL) {
        certTrustLock = PZ_NewLock(nssILockCertDB);
        if (certTrustLock == NULL) {
            return SECFailure;
        }
    }

    return SECSuccess;
}

/*
 * Acquire the global lock on the cert database.
 * This lock is currently used for the following operations:
 *  adding or deleting a cert to either the temp or perm databases
 *  converting a temp to perm or perm to temp
 *  changing (maybe just adding!?) the trust of a cert
 *      chaning the DB status checking Configuration
 */

static void
nsslowcert_LockDB(NSSLOWCERTCertDBHandle *handle)
{
    PZ_EnterMonitor(handle->dbMon);
    return;
}

/*
 * Free the global cert database lock.
 */

static void
nsslowcert_UnlockDB(NSSLOWCERTCertDBHandle *handle)
{
#ifdef DEBUG
    PRStatus prstat = PZ_ExitMonitor(handle->dbMon);
    PORT_Assert(prstat == PR_SUCCESS);
#else
    PZ_ExitMonitor(handle->dbMon);
#endif
}

/*
 * Acquire the cert reference count lock
 * There is currently one global lock for all certs, but I'm putting a cert
 * arg here so that it will be easy to make it per-cert in the future if
 * that turns out to be necessary.
 */

static void
nsslowcert_LockCertRefCount(NSSLOWCERTCertificate *cert)
{
    PORT_Assert(certRefCountLock != NULL);

    PZ_Lock(certRefCountLock);
    return;
}

/*
 * Free the cert reference count lock
 */

static void
nsslowcert_UnlockCertRefCount(NSSLOWCERTCertificate *cert)
{
    PORT_Assert(certRefCountLock != NULL);

#ifdef DEBUG
    {
        PRStatus prstat = PZ_Unlock(certRefCountLock);
        PORT_Assert(prstat == PR_SUCCESS);
    }
#else
    PZ_Unlock(certRefCountLock);
#endif
}

/*
 * Acquire the cert trust lock
 * There is currently one global lock for all certs, but I'm putting a cert
 * arg here so that it will be easy to make it per-cert in the future if
 * that turns out to be necessary.
 */

static void
nsslowcert_LockCertTrust(NSSLOWCERTCertificate *cert)
{
    PORT_Assert(certTrustLock != NULL);

    PZ_Lock(certTrustLock);
    return;
}

/*
 * Free the cert trust lock
 */

static void
nsslowcert_UnlockCertTrust(NSSLOWCERTCertificate *cert)
{
    PORT_Assert(certTrustLock != NULL);

#ifdef DEBUG
    {
        PRStatus prstat = PZ_Unlock(certTrustLock);
        PORT_Assert(prstat == PR_SUCCESS);
    }
#else
    PZ_Unlock(certTrustLock);
#endif
}

/*
 * Acquire the cert reference count lock
 * There is currently one global lock for all certs, but I'm putting a cert
 * arg here so that it will be easy to make it per-cert in the future if
 * that turns out to be necessary.
 */

static void
nsslowcert_LockFreeList(void)
{
    PORT_Assert(freeListLock != NULL);

    SKIP_AFTER_FORK(PZ_Lock(freeListLock));
    return;
}

/*
 * Free the cert reference count lock
 */

static void
nsslowcert_UnlockFreeList(void)
{
    PORT_Assert(freeListLock != NULL);

#ifdef DEBUG
    {
        PRStatus prstat = PR_SUCCESS;
        SKIP_AFTER_FORK(prstat = PZ_Unlock(freeListLock));
        PORT_Assert(prstat == PR_SUCCESS);
    }
#else
    SKIP_AFTER_FORK(PZ_Unlock(freeListLock));
#endif
}

NSSLOWCERTCertificate *
nsslowcert_DupCertificate(NSSLOWCERTCertificate *c)
{
    if (c) {
        nsslowcert_LockCertRefCount(c);
        ++c->referenceCount;
        nsslowcert_UnlockCertRefCount(c);
    }
    return c;
}

static int
certdb_Get(DB *db, DBT *key, DBT *data, unsigned int flags)
{
    int ret;

    PORT_Assert(dbLock != NULL);
    PZ_Lock(dbLock);

    ret = (*db->get)(db, key, data, flags);

    (void)PZ_Unlock(dbLock);

    return (ret);
}

static int
certdb_Put(DB *db, DBT *key, DBT *data, unsigned int flags)
{
    int ret = 0;

    PORT_Assert(dbLock != NULL);
    PZ_Lock(dbLock);

    ret = (*db->put)(db, key, data, flags);

    (void)PZ_Unlock(dbLock);

    return (ret);
}

static int
certdb_Sync(DB *db, unsigned int flags)
{
    int ret;

    PORT_Assert(dbLock != NULL);
    PZ_Lock(dbLock);

    ret = (*db->sync)(db, flags);

    (void)PZ_Unlock(dbLock);

    return (ret);
}

#define DB_NOT_FOUND -30991 /* from DBM 3.2 */
static int
certdb_Del(DB *db, DBT *key, unsigned int flags)
{
    int ret;

    PORT_Assert(dbLock != NULL);
    PZ_Lock(dbLock);

    ret = (*db->del)(db, key, flags);

    (void)PZ_Unlock(dbLock);

    /* don't fail if the record is already deleted */
    if (ret == DB_NOT_FOUND) {
        ret = 0;
    }

    return (ret);
}

static int
certdb_Seq(DB *db, DBT *key, DBT *data, unsigned int flags)
{
    int ret;

    PORT_Assert(dbLock != NULL);
    PZ_Lock(dbLock);

    ret = (*db->seq)(db, key, data, flags);

    (void)PZ_Unlock(dbLock);

    return (ret);
}

static void
certdb_Close(DB *db)
{
    PORT_Assert(dbLock != NULL);
    SKIP_AFTER_FORK(PZ_Lock(dbLock));

    (*db->close)(db);

    SKIP_AFTER_FORK(PZ_Unlock(dbLock));

    return;
}

void
pkcs11_freeNickname(char *nickname, char *space)
{
    if (nickname && nickname != space) {
        PORT_Free(nickname);
    }
}

char *
pkcs11_copyNickname(char *nickname, char *space, int spaceLen)
{
    int len;
    char *copy = NULL;

    len = PORT_Strlen(nickname) + 1;
    if (len <= spaceLen) {
        copy = space;
        PORT_Memcpy(copy, nickname, len);
    } else {
        copy = PORT_Strdup(nickname);
    }

    return copy;
}

void
pkcs11_freeStaticData(unsigned char *data, unsigned char *space)
{
    if (data && data != space) {
        PORT_Free(data);
    }
}

unsigned char *
pkcs11_allocStaticData(int len, unsigned char *space, int spaceLen)
{
    unsigned char *data = NULL;

    if (len <= spaceLen) {
        data = space;
    } else {
        data = (unsigned char *)PORT_Alloc(len);
    }

    return data;
}

unsigned char *
pkcs11_copyStaticData(unsigned char *data, int len,
                      unsigned char *space, int spaceLen)
{
    unsigned char *copy = pkcs11_allocStaticData(len, space, spaceLen);
    if (copy) {
        PORT_Memcpy(copy, data, len);
    }

    return copy;
}

/*
 * destroy a database entry
 */

static void
DestroyDBEntry(certDBEntry *entry)
{
    PLArenaPool *arena = entry->common.arena;

    /* must be one of our certDBEntry from the free list */
    if (arena == NULL) {
        certDBEntryCert *certEntry;
        if (entry->common.type != certDBEntryTypeCert) {
            return;
        }
        certEntry = (certDBEntryCert *)entry;

        pkcs11_freeStaticData(certEntry->derCert.data, certEntry->derCertSpace);
        pkcs11_freeNickname(certEntry->nickname, certEntry->nicknameSpace);

        nsslowcert_LockFreeList();
        if (entryListCount > MAX_ENTRY_LIST_COUNT) {
            PORT_Free(certEntry);
        } else {
            entryListCount++;
            PORT_Memset(certEntry, 0, sizeof(*certEntry));
            certEntry->next = entryListHead;
            entryListHead = certEntry;
        }
        nsslowcert_UnlockFreeList();
        return;
    }

    /* Zero out the entry struct, so that any further attempts to use it
     * will cause an exception (e.g. null pointer reference). */

    PORT_Memset(&entry->common, 0, sizeof entry->common);
    PORT_FreeArena(arena, PR_FALSE);

    return;
}

/* forward references */
static void nsslowcert_DestroyCertificateNoLocking(NSSLOWCERTCertificate *cert);

static SECStatus
DeleteDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryType type, SECItem *dbkey)
{
    DBT key;
    int ret;

    /* init the database key */
    key.data = dbkey->data;
    key.size = dbkey->len;

    dbkey->data[0] = (unsigned char)type;

    /* delete entry from database */
    ret = certdb_Del(handle->permCertDB, &key, 0);
    if (ret != 0) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        goto loser;
    }

    ret = certdb_Sync(handle->permCertDB, 0);
    if (ret) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        goto loser;
    }

    return (SECSuccess);

loser:
    return (SECFailure);
}

static SECStatus
ReadDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCommon *entry,
            SECItem *dbkey, SECItem *dbentry, PLArenaPool *arena)
{
    DBT data, key;
    int ret;
    unsigned char *buf;

    /* init the database key */
    key.data = dbkey->data;
    key.size = dbkey->len;

    dbkey->data[0] = (unsigned char)entry->type;

    /* read entry from database */
    ret = certdb_Get(handle->permCertDB, &key, &data, 0);
    if (ret != 0) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        goto loser;
    }

    /* validate the entry */
    if (data.size < SEC_DB_ENTRY_HEADER_LEN) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        goto loser;
    }
    buf = (unsigned char *)data.data;
    /* version 7 has the same schema, we may be using a v7 db if we openned
     * the databases readonly. */

    if (!((buf[0] == (unsigned char)CERT_DB_FILE_VERSION) ||
          (buf[0] == (unsigned char)CERT_DB_V7_FILE_VERSION))) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        goto loser;
    }
    if (buf[1] != (unsigned char)entry->type) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        goto loser;
    }

    /* copy out header information */
    entry->version = (unsigned int)buf[0];
    entry->type = (certDBEntryType)buf[1];
    entry->flags = (unsigned int)buf[2];

    /* format body of entry for return to caller */
    dbentry->len = data.size - SEC_DB_ENTRY_HEADER_LEN;
    if (dbentry->len) {
        if (arena) {
            dbentry->data = (unsigned char *)
                PORT_ArenaAlloc(arena, dbentry->len);
            if (dbentry->data == NULL) {
                PORT_SetError(SEC_ERROR_NO_MEMORY);
                goto loser;
            }

            PORT_Memcpy(dbentry->data, &buf[SEC_DB_ENTRY_HEADER_LEN],
                        dbentry->len);
        } else {
            dbentry->data = &buf[SEC_DB_ENTRY_HEADER_LEN];
        }
    } else {
        dbentry->data = NULL;
    }

    return (SECSuccess);

loser:
    return (SECFailure);
}

/**
 ** Implement low level database access
 **/

static SECStatus
WriteDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCommon *entry,
             SECItem *dbkey, SECItem *dbentry)
{
    int ret;
    DBT data, key;
    unsigned char *buf;

    data.data = dbentry->data;
    data.size = dbentry->len;

    buf = (unsigned char *)data.data;

    buf[0] = (unsigned char)entry->version;
    buf[1] = (unsigned char)entry->type;
    buf[2] = (unsigned char)entry->flags;

    key.data = dbkey->data;
    key.size = dbkey->len;

    dbkey->data[0] = (unsigned char)entry->type;

    /* put the record into the database now */
    ret = certdb_Put(handle->permCertDB, &key, &data, 0);

    if (ret != 0) {
        goto loser;
    }

    ret = certdb_Sync(handle->permCertDB, 0);

    if (ret) {
        goto loser;
    }

    return (SECSuccess);

loser:
    return (SECFailure);
}

/*
 * encode a database cert record
 */

static SECStatus
EncodeDBCertEntry(certDBEntryCert *entry, PLArenaPool *arena, SECItem *dbitem)
{
    unsigned int nnlen;
    unsigned char *buf;
    char *nn;
    char zbuf = 0;

    if (entry->nickname) {
        nn = entry->nickname;
    } else {
        nn = &zbuf;
    }
    nnlen = PORT_Strlen(nn) + 1;

    /* allocate space for encoded database record, including space
     * for low level header
     */

    dbitem->len = entry->derCert.len + nnlen + DB_CERT_ENTRY_HEADER_LEN +
                  SEC_DB_ENTRY_HEADER_LEN;

    dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
    if (dbitem->data == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }

    /* fill in database record */
    buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];

    buf[0] = (PRUint8)(entry->trust.sslFlags >> 8);
    buf[1] = (PRUint8)(entry->trust.sslFlags);
    buf[2] = (PRUint8)(entry->trust.emailFlags >> 8);
    buf[3] = (PRUint8)(entry->trust.emailFlags);
    buf[4] = (PRUint8)(entry->trust.objectSigningFlags >> 8);
    buf[5] = (PRUint8)(entry->trust.objectSigningFlags);
    buf[6] = (PRUint8)(entry->derCert.len >> 8);
    buf[7] = (PRUint8)(entry->derCert.len);
    buf[8] = (PRUint8)(nnlen >> 8);
    buf[9] = (PRUint8)(nnlen);

    PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN], entry->derCert.data,
                entry->derCert.len);

    PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN + entry->derCert.len],
                nn, nnlen);

    return (SECSuccess);

loser:
    return (SECFailure);
}

/*
 * encode a database key for a cert record
 */

static SECStatus
EncodeDBCertKey(const SECItem *certKey, PLArenaPool *arena, SECItem *dbkey)
{
    unsigned int len = certKey->len + SEC_DB_KEY_HEADER_LEN;
    if (len > NSS_MAX_LEGACY_DB_KEY_SIZE)
        goto loser;
    if (arena) {
        dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, len);
    } else {
        if (dbkey->len < len) {
            dbkey->data = (unsigned char *)PORT_Alloc(len);
        }
    }
    dbkey->len = len;
    if (dbkey->data == NULL) {
        goto loser;
    }
    PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],
                certKey->data, certKey->len);
    dbkey->data[0] = certDBEntryTypeCert;

    return (SECSuccess);
loser:
    return (SECFailure);
}

static SECStatus
EncodeDBGenericKey(const SECItem *certKey, PLArenaPool *arena, SECItem *dbkey,
                   certDBEntryType entryType)
{
    /*
     * we only allow _one_ KRL key!
     */

    if (entryType == certDBEntryTypeKeyRevocation) {
        dbkey->len = SEC_DB_KEY_HEADER_LEN;
        dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
        if (dbkey->data == NULL) {
            goto loser;
        }
        dbkey->data[0] = (unsigned char)entryType;
        return (SECSuccess);
    }

    dbkey->len = certKey->len + SEC_DB_KEY_HEADER_LEN;
    if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
        goto loser;
    dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
    if (dbkey->data == NULL) {
        goto loser;
    }
    PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],
                certKey->data, certKey->len);
    dbkey->data[0] = (unsigned char)entryType;

    return (SECSuccess);
loser:
    return (SECFailure);
}

static SECStatus
DecodeDBCertEntry(certDBEntryCert *entry, SECItem *dbentry)
{
    unsigned int nnlen;
    unsigned int headerlen;
    int lenoff;

    /* allow updates of old versions of the database */
    switch (entry->common.version) {
        case 5:
            headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
            lenoff = 3;
            break;
        case 6:
            /* should not get here */
            PORT_Assert(0);
            headerlen = DB_CERT_V6_ENTRY_HEADER_LEN;
            lenoff = 3;
            break;
        case 7:
        case 8:
            headerlen = DB_CERT_ENTRY_HEADER_LEN;
            lenoff = 6;
            break;
        default:
            /* better not get here */
            PORT_Assert(0);
            headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
            lenoff = 3;
            break;
    }

    /* is record long enough for header? */
    if (dbentry->len < headerlen) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        goto loser;
    }

    /* is database entry correct length? */
    entry->derCert.len = ((dbentry->data[lenoff] << 8) |
                          dbentry->data[lenoff + 1]);
    nnlen = ((dbentry->data[lenoff + 2] << 8) | dbentry->data[lenoff + 3]);
    lenoff = dbentry->len - (entry->derCert.len + nnlen + headerlen);
    if (lenoff) {
        if (lenoff < 0 || (lenoff & 0xffff) != 0) {
            PORT_SetError(SEC_ERROR_BAD_DATABASE);
            goto loser;
        }
        /* The cert size exceeded 64KB.  Reconstruct the correct length. */
        entry->derCert.len += lenoff;
    }

    /* Is data long enough? */
    if (dbentry->len < headerlen + entry->derCert.len) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        goto loser;
    }

    /* copy the dercert */
    entry->derCert.data = pkcs11_copyStaticData(&dbentry->data[headerlen],
                                                entry->derCert.len, entry->derCertSpace, sizeof(entry->derCertSpace));
    if (entry->derCert.data == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }

    /* copy the nickname */
    if (nnlen > 1) {
        /* Is data long enough? */
        if (dbentry->len < headerlen + entry->derCert.len + nnlen) {
            PORT_SetError(SEC_ERROR_BAD_DATABASE);
            goto loser;
        }
        entry->nickname = (char *)pkcs11_copyStaticData(
            &dbentry->data[headerlen + entry->derCert.len], nnlen,
            (unsigned char *)entry->nicknameSpace,
            sizeof(entry->nicknameSpace));
        if (entry->nickname == NULL) {
            PORT_SetError(SEC_ERROR_NO_MEMORY);
            goto loser;
        }
    } else {
        entry->nickname = NULL;
    }

    if (entry->common.version < 7) {
        /* allow updates of v5 db */
        entry->trust.sslFlags = dbentry->data[0];
        entry->trust.emailFlags = dbentry->data[1];
        entry->trust.objectSigningFlags = dbentry->data[2];
    } else {
        entry->trust.sslFlags = (dbentry->data[0] << 8) | dbentry->data[1];
        entry->trust.emailFlags = (dbentry->data[2] << 8) | dbentry->data[3];
        entry->trust.objectSigningFlags =
            (dbentry->data[4] << 8) | dbentry->data[5];
    }

    return (SECSuccess);
loser:
    return (SECFailure);
}

/*
 * Create a new certDBEntryCert from existing data
 */

static certDBEntryCert *
NewDBCertEntry(SECItem *derCert, char *nickname,
               NSSLOWCERTCertTrust *trust, int flags)
{
    certDBEntryCert *entry;
    PLArenaPool *arena = NULL;
    int nnlen;

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);

    if (!arena) {
        goto loser;
    }

    entry = PORT_ArenaZNew(arena, certDBEntryCert);
    if (entry == NULL) {
        goto loser;
    }

    /* fill in the dbCert */
    entry->common.arena = arena;
    entry->common.type = certDBEntryTypeCert;
    entry->common.version = CERT_DB_FILE_VERSION;
    entry->common.flags = flags;

    if (trust) {
        entry->trust = *trust;
    }

    entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, derCert->len);
    if (!entry->derCert.data) {
        goto loser;
    }
    entry->derCert.len = derCert->len;
    PORT_Memcpy(entry->derCert.data, derCert->data, derCert->len);

    nnlen = (nickname ? strlen(nickname) + 1 : 0);

    if (nnlen) {
        entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
        if (!entry->nickname) {
            goto loser;
        }
        PORT_Memcpy(entry->nickname, nickname, nnlen);

    } else {
        entry->nickname = 0;
    }

    return (entry);

loser:

    /* allocation error, free arena and return */
    if (arena) {
        PORT_FreeArena(arena, PR_FALSE);
    }

    PORT_SetError(SEC_ERROR_NO_MEMORY);
    return (0);
}

/*
 * Decode a version 4 DBCert from the byte stream database format
 * and construct a current database entry struct
 */

static certDBEntryCert *
DecodeV4DBCertEntry(unsigned char *buf, int len)
{
    certDBEntryCert *entry;
    int certlen;
    int nnlen;
    PLArenaPool *arena;

    /* make sure length is at least long enough for the header */
    if (len < DBCERT_V4_HEADER_LEN) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        return (0);
    }

    /* get other lengths */
    certlen = buf[3] << 8 | buf[4];
    nnlen = buf[5] << 8 | buf[6];

    /* make sure DB entry is the right size */
    if ((certlen + nnlen + DBCERT_V4_HEADER_LEN) != len) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        return (0);
    }

    /* allocate arena */
    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);

    if (!arena) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        return (0);
    }

    /* allocate structure and members */
    entry = (certDBEntryCert *)PORT_ArenaAlloc(arena, sizeof(certDBEntryCert));

    if (!entry) {
        goto loser;
    }

    entry->common.arena = arena;
    entry->common.version = CERT_DB_FILE_VERSION;
    entry->common.type = certDBEntryTypeCert;
    entry->common.flags = 0;
    entry->trust.sslFlags = buf[0];
    entry->trust.emailFlags = buf[1];
    entry->trust.objectSigningFlags = buf[2];

    entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, certlen);
    if (!entry->derCert.data) {
        goto loser;
    }
    entry->derCert.len = certlen;
    PORT_Memcpy(entry->derCert.data, &buf[DBCERT_V4_HEADER_LEN], certlen);

    if (nnlen) {
        entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
        if (!entry->nickname) {
            goto loser;
        }
        PORT_Memcpy(entry->nickname, &buf[DBCERT_V4_HEADER_LEN + certlen], nnlen);

        if (PORT_Strcmp(entry->nickname, "Server-Cert") == 0) {
            entry->trust.sslFlags |= CERTDB_USER;
        }
    } else {
        entry->nickname = 0;
    }

    return (entry);

loser:
    PORT_FreeArena(arena, PR_FALSE);
    PORT_SetError(SEC_ERROR_NO_MEMORY);
    return (0);
}

/*
 * Encode a Certificate database entry into byte stream suitable for
 * the database
 */

static SECStatus
WriteDBCertEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry)
{
    SECItem dbitem, dbkey;
    PLArenaPool *tmparena = NULL;
    SECItem tmpitem;
    SECStatus rv;

    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (tmparena == NULL) {
        goto loser;
    }

    rv = EncodeDBCertEntry(entry, tmparena, &dbitem);
    if (rv != SECSuccess) {
        goto loser;
    }

    /* get the database key and format it */
    rv = nsslowcert_KeyFromDERCert(tmparena, &entry->derCert, &tmpitem);
    if (rv == SECFailure) {
        goto loser;
    }

    rv = EncodeDBCertKey(&tmpitem, tmparena, &dbkey);
    if (rv == SECFailure) {
        goto loser;
    }

    /* now write it to the database */
    rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
    if (rv != SECSuccess) {
        goto loser;
    }

    PORT_FreeArena(tmparena, PR_FALSE);
    return (SECSuccess);

loser:
    if (tmparena) {
        PORT_FreeArena(tmparena, PR_FALSE);
    }
    return (SECFailure);
}

/*
 * delete a certificate entry
 */

static SECStatus
DeleteDBCertEntry(NSSLOWCERTCertDBHandle *handle, SECItem *certKey)
{
    SECItem dbkey;
    SECStatus rv;

    dbkey.data = NULL;
    dbkey.len = 0;

    rv = EncodeDBCertKey(certKey, NULL, &dbkey);
    if (rv != SECSuccess) {
        goto loser;
    }

    rv = DeleteDBEntry(handle, certDBEntryTypeCert, &dbkey);
    if (rv == SECFailure) {
        goto loser;
    }

    PORT_Free(dbkey.data);

    return (SECSuccess);

loser:
    if (dbkey.data) {
        PORT_Free(dbkey.data);
    }
    return (SECFailure);
}

static certDBEntryCert *
CreateCertEntry(void)
{
    certDBEntryCert *entry;

    nsslowcert_LockFreeList();
    entry = entryListHead;
    if (entry) {
        entryListCount--;
        entryListHead = entry->next;
    }
    PORT_Assert(entryListCount >= 0);
    nsslowcert_UnlockFreeList();
    if (entry) {
        return entry;
    }

    return PORT_ZNew(certDBEntryCert);
}

static void
DestroyCertEntryFreeList(void)
{
    certDBEntryCert *entry;

    nsslowcert_LockFreeList();
    while (NULL != (entry = entryListHead)) {
        entryListCount--;
        entryListHead = entry->next;
        PORT_Free(entry);
    }
    PORT_Assert(!entryListCount);
    entryListCount = 0;
    nsslowcert_UnlockFreeList();
}

/*
 * Read a certificate entry
 */

static certDBEntryCert *
ReadDBCertEntry(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
{
    certDBEntryCert *entry;
    SECItem dbkey;
    SECItem dbentry;
    SECStatus rv;
    unsigned char buf[512];

    dbkey.data = buf;
    dbkey.len = sizeof(buf);

    entry = CreateCertEntry();
    if (entry == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }
    entry->common.arena = NULL;
    entry->common.type = certDBEntryTypeCert;

    rv = EncodeDBCertKey(certKey, NULL, &dbkey);
    if (rv != SECSuccess) {
        goto loser;
    }

    rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, NULL);
    if (rv == SECFailure) {
        goto loser;
    }

    rv = DecodeDBCertEntry(entry, &dbentry);
    if (rv != SECSuccess) {
        goto loser;
    }

    pkcs11_freeStaticData(dbkey.data, buf);
    dbkey.data = NULL;
    return (entry);

loser:
    pkcs11_freeStaticData(dbkey.data, buf);
    dbkey.data = NULL;
    if (entry) {
        DestroyDBEntry((certDBEntry *)entry);
    }

    return (NULL);
}

/*
 * encode a database cert record
 */

static SECStatus
EncodeDBCrlEntry(certDBEntryRevocation *entry, PLArenaPool *arena, SECItem *dbitem)
{
    unsigned int nnlen = 0;
    unsigned char *buf;

    if (entry->url) {
        nnlen = PORT_Strlen(entry->url) + 1;
    }

    /* allocate space for encoded database record, including space
     * for low level header
     */

    dbitem->len = entry->derCrl.len + nnlen + SEC_DB_ENTRY_HEADER_LEN + DB_CRL_ENTRY_HEADER_LEN;

    dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
    if (dbitem->data == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }

    /* fill in database record */
    buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];

    buf[0] = (PRUint8)(entry->derCrl.len >> 8);
    buf[1] = (PRUint8)(entry->derCrl.len);
    buf[2] = (PRUint8)(nnlen >> 8);
    buf[3] = (PRUint8)(nnlen);

    PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN], entry->derCrl.data,
                entry->derCrl.len);

    if (nnlen != 0) {
        PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN + entry->derCrl.len],
                    entry->url, nnlen);
    }

    return (SECSuccess);

loser:
    return (SECFailure);
}

static SECStatus
DecodeDBCrlEntry(certDBEntryRevocation *entry, SECItem *dbentry)
{
    unsigned int urlLen;
    int lenDiff;

    /* is record long enough for header? */
    if (dbentry->len < DB_CRL_ENTRY_HEADER_LEN) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        goto loser;
    }

    /* is database entry correct length? */
    entry->derCrl.len = ((dbentry->data[0] << 8) | dbentry->data[1]);
    urlLen = ((dbentry->data[2] << 8) | dbentry->data[3]);
    lenDiff = dbentry->len -
              (entry->derCrl.len + urlLen + DB_CRL_ENTRY_HEADER_LEN);
    if (lenDiff) {
        if (lenDiff < 0 || (lenDiff & 0xffff) != 0) {
            PORT_SetError(SEC_ERROR_BAD_DATABASE);
            goto loser;
        }
        /* CRL entry is greater than 64 K. Hack to make this continue to work */
        entry->derCrl.len += lenDiff;
    }

    /* copy the der CRL */
    entry->derCrl.data = (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
                                                          entry->derCrl.len);
    if (entry->derCrl.data == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }
    PORT_Memcpy(entry->derCrl.data, &dbentry->data[DB_CRL_ENTRY_HEADER_LEN],
                entry->derCrl.len);

    /* copy the url */
    entry->url = NULL;
    if (urlLen != 0) {
        entry->url = (char *)PORT_ArenaAlloc(entry->common.arena, urlLen);
        if (entry->url == NULL) {
            PORT_SetError(SEC_ERROR_NO_MEMORY);
            goto loser;
        }
        PORT_Memcpy(entry->url,
                    &dbentry->data[DB_CRL_ENTRY_HEADER_LEN + entry->derCrl.len],
                    urlLen);
    }

    return (SECSuccess);
loser:
    return (SECFailure);
}

/*
 * Create a new certDBEntryRevocation from existing data
 */

static certDBEntryRevocation *
NewDBCrlEntry(SECItem *derCrl, char *url, certDBEntryType crlType, int flags)
{
    certDBEntryRevocation *entry;
    PLArenaPool *arena = NULL;
    int nnlen;

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);

    if (!arena) {
        goto loser;
    }

    entry = PORT_ArenaZNew(arena, certDBEntryRevocation);
    if (entry == NULL) {
        goto loser;
    }

    /* fill in the dbRevolcation */
    entry->common.arena = arena;
    entry->common.type = crlType;
    entry->common.version = CERT_DB_FILE_VERSION;
    entry->common.flags = flags;

    entry->derCrl.data = (unsigned char *)PORT_ArenaAlloc(arena, derCrl->len);
    if (!entry->derCrl.data) {
        goto loser;
    }

    if (url) {
        nnlen = PORT_Strlen(url) + 1;
        entry->url = (char *)PORT_ArenaAlloc(arena, nnlen);
        if (!entry->url) {
            goto loser;
        }
        PORT_Memcpy(entry->url, url, nnlen);
    } else {
        entry->url = NULL;
    }

    entry->derCrl.len = derCrl->len;
    PORT_Memcpy(entry->derCrl.data, derCrl->data, derCrl->len);

    return (entry);

loser:

    /* allocation error, free arena and return */
    if (arena) {
        PORT_FreeArena(arena, PR_FALSE);
    }

    PORT_SetError(SEC_ERROR_NO_MEMORY);
    return (0);
}

static SECStatus
WriteDBCrlEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryRevocation *entry,
                SECItem *crlKey)
{
    SECItem dbkey;
    PLArenaPool *tmparena = NULL;
    SECItem encodedEntry;
    SECStatus rv;

    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (tmparena == NULL) {
        goto loser;
    }

    rv = EncodeDBCrlEntry(entry, tmparena, &encodedEntry);
    if (rv == SECFailure) {
        goto loser;
    }

    rv = EncodeDBGenericKey(crlKey, tmparena, &dbkey, entry->common.type);
    if (rv == SECFailure) {
        goto loser;
    }

    /* now write it to the database */
    rv = WriteDBEntry(handle, &entry->common, &dbkey, &encodedEntry);
    if (rv != SECSuccess) {
        goto loser;
    }

    PORT_FreeArena(tmparena, PR_FALSE);
    return (SECSuccess);

loser:
    if (tmparena) {
        PORT_FreeArena(tmparena, PR_FALSE);
    }
    return (SECFailure);
}
/*
 * delete a crl entry
 */

static SECStatus
DeleteDBCrlEntry(NSSLOWCERTCertDBHandle *handle, const SECItem *crlKey,
                 certDBEntryType crlType)
{
    SECItem dbkey;
    PLArenaPool *arena = NULL;
    SECStatus rv;

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (arena == NULL) {
        goto loser;
    }

    rv = EncodeDBGenericKey(crlKey, arena, &dbkey, crlType);
    if (rv != SECSuccess) {
        goto loser;
    }

    rv = DeleteDBEntry(handle, crlType, &dbkey);
    if (rv == SECFailure) {
        goto loser;
    }

    PORT_FreeArena(arena, PR_FALSE);
    return (SECSuccess);

loser:
    if (arena) {
        PORT_FreeArena(arena, PR_FALSE);
    }

    return (SECFailure);
}

/*
 * Read a certificate entry
 */

static certDBEntryRevocation *
ReadDBCrlEntry(NSSLOWCERTCertDBHandle *handle, SECItem *certKey,
               certDBEntryType crlType)
{
    PLArenaPool *arena = NULL;
    PLArenaPool *tmparena = NULL;
    certDBEntryRevocation *entry;
    SECItem dbkey;
    SECItem dbentry;
    SECStatus rv;

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (arena == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }

    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (tmparena == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }

    entry = (certDBEntryRevocation *)
        PORT_ArenaAlloc(arena, sizeof(certDBEntryRevocation));
    if (entry == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }
    entry->common.arena = arena;
    entry->common.type = crlType;

    rv = EncodeDBGenericKey(certKey, tmparena, &dbkey, crlType);
    if (rv != SECSuccess) {
        goto loser;
    }

    rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, NULL);
    if (rv == SECFailure) {
        goto loser;
    }

    rv = DecodeDBCrlEntry(entry, &dbentry);
    if (rv != SECSuccess) {
        goto loser;
    }

    PORT_FreeArena(tmparena, PR_FALSE);
    return (entry);

loser:
    if (tmparena) {
        PORT_FreeArena(tmparena, PR_FALSE);
    }
    if (arena) {
        PORT_FreeArena(arena, PR_FALSE);
    }

    return (NULL);
}

void
nsslowcert_DestroyDBEntry(certDBEntry *entry)
{
    DestroyDBEntry(entry);
    return;
}

/*
 * Encode a database nickname record
 */

static SECStatus
EncodeDBNicknameEntry(certDBEntryNickname *entry, PLArenaPool *arena,
                      SECItem *dbitem)
{
    unsigned char *buf;

    /* allocate space for encoded database record, including space
     * for low level header
     */

    dbitem->len = entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN +
                  SEC_DB_ENTRY_HEADER_LEN;
    dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
    if (dbitem->data == NULL) {
        goto loser;
    }

    /* fill in database record */
    buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
    buf[0] = (PRUint8)(entry->subjectName.len >> 8);
    buf[1] = (PRUint8)(entry->subjectName.len);
    PORT_Memcpy(&buf[DB_NICKNAME_ENTRY_HEADER_LEN], entry->subjectName.data,
                entry->subjectName.len);

    return (SECSuccess);

loser:
    return (SECFailure);
}

/*
 * Encode a database key for a nickname record
 */

static SECStatus
EncodeDBNicknameKey(char *nickname, PLArenaPool *arena,
                    SECItem *dbkey)
{
    unsigned int nnlen;

    nnlen = PORT_Strlen(nickname) + 1; /* includes null */

    /* now get the database key and format it */
    dbkey->len = nnlen + SEC_DB_KEY_HEADER_LEN;
    if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
        goto loser;
    dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
    if (dbkey->data == NULL) {
        goto loser;
    }
    PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], nickname, nnlen);
    dbkey->data[0] = certDBEntryTypeNickname;

    return (SECSuccess);

loser:
    return (SECFailure);
}

static SECStatus
DecodeDBNicknameEntry(certDBEntryNickname *entry, SECItem *dbentry,
                      char *nickname)
{
    int lenDiff;

    /* is record long enough for header? */
    if (dbentry->len < DB_NICKNAME_ENTRY_HEADER_LEN) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        goto loser;
    }

    /* is database entry correct length? */
    entry->subjectName.len = ((dbentry->data[0] << 8) | dbentry->data[1]);
    lenDiff = dbentry->len -
              (entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN);
    if (lenDiff) {
        if (lenDiff < 0 || (lenDiff & 0xffff) != 0) {
            PORT_SetError(SEC_ERROR_BAD_DATABASE);
            goto loser;
        }
        /* The entry size exceeded 64KB.  Reconstruct the correct length. */
        entry->subjectName.len += lenDiff;
    }

    /* copy the certkey */
    entry->subjectName.data =
        (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
                                         entry->subjectName.len);
    if (entry->subjectName.data == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }
    PORT_Memcpy(entry->subjectName.data,
                &dbentry->data[DB_NICKNAME_ENTRY_HEADER_LEN],
                entry->subjectName.len);
    entry->subjectName.type = siBuffer;

    entry->nickname = (char *)PORT_ArenaAlloc(entry->common.arena,
                                              PORT_Strlen(nickname) + 1);
    if (entry->nickname) {
        PORT_Strcpy(entry->nickname, nickname);
    }

    return (SECSuccess);

loser:
    return (SECFailure);
}

/*
 * create a new nickname entry
 */

static certDBEntryNickname *
NewDBNicknameEntry(char *nickname, SECItem *subjectName, unsigned int flags)
{
    PLArenaPool *arena = NULL;
    certDBEntryNickname *entry;
    int nnlen;
    SECStatus rv;

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (arena == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }

    entry = (certDBEntryNickname *)PORT_ArenaAlloc(arena,
                                                   sizeof(certDBEntryNickname));
    if (entry == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }

    /* init common fields */
    entry->common.arena = arena;
    entry->common.type = certDBEntryTypeNickname;
    entry->common.version = CERT_DB_FILE_VERSION;
    entry->common.flags = flags;

    /* copy the nickname */
    nnlen = PORT_Strlen(nickname) + 1;

    entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
    if (entry->nickname == NULL) {
        goto loser;
    }

    PORT_Memcpy(entry->nickname, nickname, nnlen);

    rv = SECITEM_CopyItem(arena, &entry->subjectName, subjectName);
    if (rv != SECSuccess) {
        goto loser;
    }

    return (entry);
loser:
    if (arena) {
        PORT_FreeArena(arena, PR_FALSE);
    }

    return (NULL);
}

/*
 * delete a nickname entry
 */

static SECStatus
DeleteDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, char *nickname)
{
    PLArenaPool *arena = NULL;
    SECStatus rv;
    SECItem dbkey;

    if (nickname == NULL) {
        return (SECSuccess);
    }

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (arena == NULL) {
        goto loser;
    }

    rv = EncodeDBNicknameKey(nickname, arena, &dbkey);
    if (rv != SECSuccess) {
        goto loser;
    }

    rv = DeleteDBEntry(handle, certDBEntryTypeNickname, &dbkey);
    if (rv == SECFailure) {
        goto loser;
    }

    PORT_FreeArena(arena, PR_FALSE);
    return (SECSuccess);

loser:
    if (arena) {
        PORT_FreeArena(arena, PR_FALSE);
    }

    return (SECFailure);
}

/*
 * Read a nickname entry
 */

static certDBEntryNickname *
ReadDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, char *nickname)
{
    PLArenaPool *arena = NULL;
    PLArenaPool *tmparena = NULL;
    certDBEntryNickname *entry;
    SECItem dbkey;
    SECItem dbentry;
    SECStatus rv;

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (arena == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }

    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (tmparena == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }

    entry = (certDBEntryNickname *)PORT_ArenaAlloc(arena,
                                                   sizeof(certDBEntryNickname));
    if (entry == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }
    entry->common.arena = arena;
    entry->common.type = certDBEntryTypeNickname;

    rv = EncodeDBNicknameKey(nickname, tmparena, &dbkey);
    if (rv != SECSuccess) {
        goto loser;
    }

    rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
    if (rv == SECFailure) {
        goto loser;
    }

    /* is record long enough for header? */
    if (dbentry.len < DB_NICKNAME_ENTRY_HEADER_LEN) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        goto loser;
    }

    rv = DecodeDBNicknameEntry(entry, &dbentry, nickname);
    if (rv != SECSuccess) {
        goto loser;
    }

    PORT_FreeArena(tmparena, PR_FALSE);
    return (entry);

loser:
    if (tmparena) {
        PORT_FreeArena(tmparena, PR_FALSE);
    }
    if (arena) {
        PORT_FreeArena(arena, PR_FALSE);
    }

    return (NULL);
}

/*
 * Encode a nickname entry into byte stream suitable for
 * the database
 */

static SECStatus
WriteDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryNickname *entry)
{
    SECItem dbitem, dbkey;
    PLArenaPool *tmparena = NULL;
    SECStatus rv;

    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (tmparena == NULL) {
        goto loser;
    }

    rv = EncodeDBNicknameEntry(entry, tmparena, &dbitem);
    if (rv != SECSuccess) {
        goto loser;
    }

    rv = EncodeDBNicknameKey(entry->nickname, tmparena, &dbkey);
    if (rv != SECSuccess) {
        goto loser;
    }

    /* now write it to the database */
    rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
    if (rv != SECSuccess) {
        goto loser;
    }

    PORT_FreeArena(tmparena, PR_FALSE);
    return (SECSuccess);

loser:
    if (tmparena) {
        PORT_FreeArena(tmparena, PR_FALSE);
    }
    return (SECFailure);
}

static SECStatus
EncodeDBSMimeEntry(certDBEntrySMime *entry, PLArenaPool *arena,
                   SECItem *dbitem)
{
    unsigned char *buf;

    /* allocate space for encoded database record, including space
     * for low level header
     */

    dbitem->len = entry->subjectName.len + entry->smimeOptions.len +
                  entry->optionsDate.len +
                  DB_SMIME_ENTRY_HEADER_LEN + SEC_DB_ENTRY_HEADER_LEN;

    dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
    if (dbitem->data == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }

    /* fill in database record */
    buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];

    buf[0] = (PRUint8)(entry->subjectName.len >> 8);
    buf[1] = (PRUint8)(entry->subjectName.len);
    buf[2] = (PRUint8)(entry->smimeOptions.len >> 8);
    buf[3] = (PRUint8)(entry->smimeOptions.len);
    buf[4] = (PRUint8)(entry->optionsDate.len >> 8);
    buf[5] = (PRUint8)(entry->optionsDate.len);

    /* if no smime options, then there should not be an options date either */
    PORT_Assert(!((entry->smimeOptions.len == 0) &&
                  (entry->optionsDate.len != 0)));

    PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN], entry->subjectName.data,
                entry->subjectName.len);
    if (entry->smimeOptions.len) {
        PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN + entry->subjectName.len],
                    entry->smimeOptions.data,
                    entry->smimeOptions.len);
        PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN + entry->subjectName.len +
                         entry->smimeOptions.len],
                    entry->optionsDate.data,
                    entry->optionsDate.len);
    }

    return (SECSuccess);

loser:
    return (SECFailure);
}

/*
 * Encode a database key for a SMIME record
 */

static SECStatus
EncodeDBSMimeKey(char *emailAddr, PLArenaPool *arena,
                 SECItem *dbkey)
{
    unsigned int addrlen;

    addrlen = PORT_Strlen(emailAddr) + 1; /* includes null */

    /* now get the database key and format it */
    dbkey->len = addrlen + SEC_DB_KEY_HEADER_LEN;
    if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
        goto loser;
    dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
    if (dbkey->data == NULL) {
        goto loser;
    }
    PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], emailAddr, addrlen);
    dbkey->data[0] = certDBEntryTypeSMimeProfile;

    return (SECSuccess);

loser:
    return (SECFailure);
}

/*
 * Decode a database SMIME record
 */

static SECStatus
DecodeDBSMimeEntry(certDBEntrySMime *entry, SECItem *dbentry, char *emailAddr)
{
    int lenDiff;

    /* is record long enough for header? */
    if (dbentry->len < DB_SMIME_ENTRY_HEADER_LEN) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        goto loser;
    }

    /* is database entry correct length? */
    entry->subjectName.len = ((dbentry->data[0] << 8) | dbentry->data[1]);
    entry->smimeOptions.len = ((dbentry->data[2] << 8) | dbentry->data[3]);
    entry->optionsDate.len = ((dbentry->data[4] << 8) | dbentry->data[5]);
    lenDiff = dbentry->len - (entry->subjectName.len +
                              entry->smimeOptions.len +
                              entry->optionsDate.len +
                              DB_SMIME_ENTRY_HEADER_LEN);
    if (lenDiff) {
        if (lenDiff < 0 || (lenDiff & 0xffff) != 0) {
            PORT_SetError(SEC_ERROR_BAD_DATABASE);
            goto loser;
        }
        /* The entry size exceeded 64KB.  Reconstruct the correct length. */
        entry->subjectName.len += lenDiff;
    }

    /* copy the subject name */
    entry->subjectName.data =
        (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
                                         entry->subjectName.len);
    if (entry->subjectName.data == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }
    PORT_Memcpy(entry->subjectName.data,
                &dbentry->data[DB_SMIME_ENTRY_HEADER_LEN],
                entry->subjectName.len);

    /* copy the smime options */
    if (entry->smimeOptions.len) {
        entry->smimeOptions.data =
            (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
                                             entry->smimeOptions.len);
        if (entry->smimeOptions.data == NULL) {
            PORT_SetError(SEC_ERROR_NO_MEMORY);
            goto loser;
        }
        PORT_Memcpy(entry->smimeOptions.data,
                    &dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
                                   entry->subjectName.len],
                    entry->smimeOptions.len);
    } else {
        entry->smimeOptions.data = NULL;
    }
    if (entry->optionsDate.len) {
        entry->optionsDate.data =
            (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
                                             entry->optionsDate.len);
        if (entry->optionsDate.data == NULL) {
            PORT_SetError(SEC_ERROR_NO_MEMORY);
            goto loser;
        }
        PORT_Memcpy(entry->optionsDate.data,
                    &dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
                                   entry->subjectName.len +
                                   entry->smimeOptions.len],
                    entry->optionsDate.len);
    } else {
        entry->optionsDate.data = NULL;
    }

    /* both options and options date must either exist or not exist */
    if (((entry->optionsDate.len == 0) ||
         (entry->smimeOptions.len == 0)) &&
        entry->smimeOptions.len != entry->optionsDate.len) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        goto loser;
    }

    entry->emailAddr = (char *)PORT_ArenaAlloc(entry->common.arena,
                                               PORT_Strlen(emailAddr) + 1);
    if (entry->emailAddr) {
        PORT_Strcpy(entry->emailAddr, emailAddr);
    }

    return (SECSuccess);

loser:
    return (SECFailure);
}

/*
 * create a new SMIME entry
 */

static certDBEntrySMime *
NewDBSMimeEntry(char *emailAddr, SECItem *subjectName, SECItem *smimeOptions,
                SECItem *optionsDate, unsigned int flags)
{
    PLArenaPool *arena = NULL;
    certDBEntrySMime *entry;
    int addrlen;
    SECStatus rv;

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (arena == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }

    entry = (certDBEntrySMime *)PORT_ArenaAlloc(arena,
                                                sizeof(certDBEntrySMime));
    if (entry == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }

    /* init common fields */
    entry->common.arena = arena;
    entry->common.type = certDBEntryTypeSMimeProfile;
    entry->common.version = CERT_DB_FILE_VERSION;
    entry->common.flags = flags;

    /* copy the email addr */
    addrlen = PORT_Strlen(emailAddr) + 1;

    entry->emailAddr = (char *)PORT_ArenaAlloc(arena, addrlen);
    if (entry->emailAddr == NULL) {
        goto loser;
    }

    PORT_Memcpy(entry->emailAddr, emailAddr, addrlen);

    /* copy the subject name */
    rv = SECITEM_CopyItem(arena, &entry->subjectName, subjectName);
    if (rv != SECSuccess) {
        goto loser;
    }

    /* copy the smime options */
    if (smimeOptions) {
        rv = SECITEM_CopyItem(arena, &entry->smimeOptions, smimeOptions);
        if (rv != SECSuccess) {
            goto loser;
        }
    } else {
        PORT_Assert(optionsDate == NULL);
        entry->smimeOptions.data = NULL;
        entry->smimeOptions.len = 0;
    }

    /* copy the options date */
    if (optionsDate) {
        rv = SECITEM_CopyItem(arena, &entry->optionsDate, optionsDate);
        if (rv != SECSuccess) {
            goto loser;
        }
    } else {
        PORT_Assert(smimeOptions == NULL);
        entry->optionsDate.data = NULL;
        entry->optionsDate.len = 0;
    }

    return (entry);
loser:
    if (arena) {
        PORT_FreeArena(arena, PR_FALSE);
    }

    return (NULL);
}

/*
 * delete a SMIME entry
 */

static SECStatus
DeleteDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, char *emailAddr)
{
    PLArenaPool *arena = NULL;
    SECStatus rv;
    SECItem dbkey;

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (arena == NULL) {
        goto loser;
    }

    rv = EncodeDBSMimeKey(emailAddr, arena, &dbkey);
    if (rv != SECSuccess) {
        goto loser;
    }

    rv = DeleteDBEntry(handle, certDBEntryTypeSMimeProfile, &dbkey);
    if (rv == SECFailure) {
        goto loser;
    }

    PORT_FreeArena(arena, PR_FALSE);
    return (SECSuccess);

loser:
    if (arena) {
        PORT_FreeArena(arena, PR_FALSE);
    }

    return (SECFailure);
}

/*
 * Read a SMIME entry
 */

certDBEntrySMime *
nsslowcert_ReadDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, char *emailAddr)
{
    PLArenaPool *arena = NULL;
    PLArenaPool *tmparena = NULL;
    certDBEntrySMime *entry = NULL;
    SECItem dbkey;
    SECItem dbentry;
    SECStatus rv;

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (arena == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }

    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (tmparena == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }

    entry = (certDBEntrySMime *)PORT_ArenaZAlloc(arena,
                                                 sizeof(certDBEntrySMime));
    if (entry == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }
    entry->common.arena = arena;
    entry->common.type = certDBEntryTypeSMimeProfile;

    rv = EncodeDBSMimeKey(emailAddr, tmparena, &dbkey);
    if (rv != SECSuccess) {
        goto loser;
    }

    rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
    if (rv == SECFailure) {
        goto loser;
    }

    /* is record long enough for header? */
    if (dbentry.len < DB_SMIME_ENTRY_HEADER_LEN) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        goto loser;
    }

    rv = DecodeDBSMimeEntry(entry, &dbentry, emailAddr);
    if (rv != SECSuccess) {
        goto loser;
    }

    PORT_FreeArena(tmparena, PR_FALSE);
    return (entry);

loser:
    if (tmparena) {
        PORT_FreeArena(tmparena, PR_FALSE);
    }
    if (arena) {
        PORT_FreeArena(arena, PR_FALSE);
    }

    return (NULL);
}

/*
 * Encode a SMIME entry into byte stream suitable for
 * the database
 */

static SECStatus
WriteDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, certDBEntrySMime *entry)
{
    SECItem dbitem, dbkey;
    PLArenaPool *tmparena = NULL;
    SECStatus rv;

    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (tmparena == NULL) {
        goto loser;
    }

    rv = EncodeDBSMimeEntry(entry, tmparena, &dbitem);
    if (rv != SECSuccess) {
        goto loser;
    }

    rv = EncodeDBSMimeKey(entry->emailAddr, tmparena, &dbkey);
    if (rv != SECSuccess) {
        goto loser;
    }

    /* now write it to the database */
    rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
    if (rv != SECSuccess) {
        goto loser;
    }

    PORT_FreeArena(tmparena, PR_FALSE);
    return (SECSuccess);

loser:
    if (tmparena) {
        PORT_FreeArena(tmparena, PR_FALSE);
    }
    return (SECFailure);
}

/*
 * Encode a database subject record
 */

static SECStatus
EncodeDBSubjectEntry(certDBEntrySubject *entry, PLArenaPool *arena,
                     SECItem *dbitem)
{
    unsigned char *buf;
    int len;
    unsigned int ncerts;
    unsigned int i;
    unsigned char *tmpbuf;
    unsigned int nnlen = 0;
    unsigned int eaddrslen = 0;
    int keyidoff;
    SECItem *certKeys = entry->certKeys;
    SECItem *keyIDs = entry->keyIDs;
    ;

    if (entry->nickname) {
        nnlen = PORT_Strlen(entry->nickname) + 1;
    }
    if (entry->emailAddrs) {
        eaddrslen = 2;
        for (i = 0; i < entry->nemailAddrs; i++) {
            eaddrslen += PORT_Strlen(entry->emailAddrs[i]) + 1 + 2;
        }
    }

    ncerts = entry->ncerts;

    /* compute the length of the entry */
    keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen;
    len = keyidoff + (4 * ncerts) + eaddrslen;
    for (i = 0; i < ncerts; i++) {
        if (keyIDs[i].len > 0xffff ||
            (certKeys[i].len > 0xffff)) {
            PORT_SetError(SEC_ERROR_INPUT_LEN);
            goto loser;
        }
        len += certKeys[i].len;
        len += keyIDs[i].len;
    }

    /* allocate space for encoded database record, including space
     * for low level header
     */

    dbitem->len = len + SEC_DB_ENTRY_HEADER_LEN;

    dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
    if (dbitem->data == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }

    /* fill in database record */
    buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];

    buf[0] = (PRUint8)(ncerts >> 8);
    buf[1] = (PRUint8)(ncerts);
    buf[2] = (PRUint8)(nnlen >> 8);
    buf[3] = (PRUint8)(nnlen);
    /* v7 email field is NULL in v8 */
    buf[4] = 0;
    buf[5] = 0;

    PORT_Assert(DB_SUBJECT_ENTRY_HEADER_LEN == 6);

    if (entry->nickname) {
        PORT_Memcpy(&buf[DB_SUBJECT_ENTRY_HEADER_LEN], entry->nickname, nnlen);
    }
    tmpbuf = &buf[keyidoff];
    for (i = 0; i < ncerts; i++) {
        tmpbuf[0] = (PRUint8)(certKeys[i].len >> 8);
        tmpbuf[1] = (PRUint8)(certKeys[i].len);
        tmpbuf += 2;
    }
    for (i = 0; i < ncerts; i++) {
        tmpbuf[0] = (PRUint8)(keyIDs[i].len >> 8);
        tmpbuf[1] = (PRUint8)(keyIDs[i].len);
        tmpbuf += 2;
    }

    for (i = 0; i < ncerts; i++) {
        PORT_Memcpy(tmpbuf, certKeys[i].data, certKeys[i].len);
        tmpbuf += certKeys[i].len;
    }
    for (i = 0; i < ncerts; i++) {
        if (keyIDs[i].len) {
            PORT_Memcpy(tmpbuf, keyIDs[i].data, keyIDs[i].len);
            tmpbuf += keyIDs[i].len;
        }
    }

    if (entry->emailAddrs) {
        tmpbuf[0] = (PRUint8)(entry->nemailAddrs >> 8);
        tmpbuf[1] = (PRUint8)(entry->nemailAddrs);
        tmpbuf += 2;
        for (i = 0; i < entry->nemailAddrs; i++) {
            int nameLen = PORT_Strlen(entry->emailAddrs[i]) + 1;
            tmpbuf[0] = (PRUint8)(nameLen >> 8);
            tmpbuf[1] = (PRUint8)(nameLen);
            tmpbuf += 2;
            PORT_Memcpy(tmpbuf, entry->emailAddrs[i], nameLen);
            tmpbuf += nameLen;
        }
    }

    PORT_Assert(tmpbuf == &buf[len]);

    return (SECSuccess);

loser:
    return (SECFailure);
}

/*
 * Encode a database key for a subject record
 */

static SECStatus
EncodeDBSubjectKey(SECItem *derSubject, PLArenaPool *arena,
                   SECItem *dbkey)
{
    dbkey->len = derSubject->len + SEC_DB_KEY_HEADER_LEN;
    if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
        goto loser;
    dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
    if (dbkey->data == NULL) {
        goto loser;
    }
    PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], derSubject->data,
                derSubject->len);
    dbkey->data[0] = certDBEntryTypeSubject;

    return (SECSuccess);

loser:
    return (SECFailure);
}

static SECStatus
DecodeDBSubjectEntry(certDBEntrySubject *entry, SECItem *dbentry,
                     const SECItem *derSubject)
{
    PLArenaPool *arena = entry->common.arena;
    unsigned char *tmpbuf;
    unsigned char *end;
    void *mark = PORT_ArenaMark(arena);
    unsigned int eaddrlen;
    unsigned int i;
    unsigned int keyidoff;
    unsigned int len;
    unsigned int ncerts = 0;
    unsigned int nnlen;
    SECStatus rv;

    rv = SECITEM_CopyItem(arena, &entry->derSubject, derSubject);
    if (rv != SECSuccess) {
        goto loser;
    }

    /* is record long enough for header? */
    if (dbentry->len < DB_SUBJECT_ENTRY_HEADER_LEN) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        goto loser;
    }

    entry->ncerts = ncerts = ((dbentry->data[0] << 8) | dbentry->data[1]);
    nnlen = ((dbentry->data[2] << 8) | dbentry->data[3]);
    eaddrlen = ((dbentry->data[4] << 8) | dbentry->data[5]);
    keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen + eaddrlen;
    len = keyidoff + (4 * ncerts);
    if (dbentry->len < len) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        goto loser;
    }

    entry->certKeys = PORT_ArenaNewArray(arena, SECItem, ncerts);
    entry->keyIDs = PORT_ArenaNewArray(arena, SECItem, ncerts);
    if ((entry->certKeys == NULL) || (entry->keyIDs == NULL)) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }

    if (nnlen > 1) { /* null terminator is stored */
        entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
        if (entry->nickname == NULL) {
            PORT_SetError(SEC_ERROR_NO_MEMORY);
            goto loser;
        }
        PORT_Memcpy(entry->nickname,
                    &dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN],
                    nnlen);
    } else {
        entry->nickname = NULL;
    }

    /* if we have an old style email entry, there is only one */
    entry->nemailAddrs = 0;
    if (eaddrlen > 1) { /* null terminator is stored */
        entry->emailAddrs = PORT_ArenaNewArray(arena, char *, 2);
        if (entry->emailAddrs == NULL) {
            PORT_SetError(SEC_ERROR_NO_MEMORY);
            goto loser;
        }
        entry->emailAddrs[0] = (char *)PORT_ArenaAlloc(arena, eaddrlen);
        if (entry->emailAddrs[0] == NULL) {
            PORT_SetError(SEC_ERROR_NO_MEMORY);
            goto loser;
        }
        PORT_Memcpy(entry->emailAddrs[0],
                    &dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN + nnlen],
                    eaddrlen);
        entry->nemailAddrs = 1;
    } else {
        entry->emailAddrs = NULL;
    }

    /* collect the lengths of the certKeys and keyIDs, and total the
     * overall length.
     */

    tmpbuf = &dbentry->data[keyidoff];
    for (i = 0; i < ncerts; i++) {
        unsigned int itemlen = (tmpbuf[0] << 8) | tmpbuf[1];
        entry->certKeys[i].len = itemlen;
        len += itemlen;
        tmpbuf += 2;
    }
    for (i = 0; i < ncerts; i++) {
        unsigned int itemlen = (tmpbuf[0] << 8) | tmpbuf[1];
        entry->keyIDs[i].len = itemlen;
        len += itemlen;
        tmpbuf += 2;
    }

    /* is encoded entry large enough ? */
    if (len > dbentry->len) {
        PORT_SetError(SEC_ERROR_BAD_DATABASE);
        goto loser;
    }

    for (i = 0; i < ncerts; i++) {
        unsigned int kLen = entry->certKeys[i].len;
        entry->certKeys[i].data = (unsigned char *)PORT_ArenaAlloc(arena, kLen);
        if (entry->certKeys[i].data == NULL) {
            PORT_SetError(SEC_ERROR_NO_MEMORY);
            goto loser;
        }
        PORT_Memcpy(entry->certKeys[i].data, tmpbuf, kLen);
        tmpbuf += kLen;
    }
    for (i = 0; i < ncerts; i++) {
        unsigned int iLen = entry->keyIDs[i].len;
        entry->keyIDs[i].data = (unsigned char *)PORT_ArenaAlloc(arena, iLen);
        if (entry->keyIDs[i].data == NULL) {
            PORT_SetError(SEC_ERROR_NO_MEMORY);
            goto loser;
        }
        PORT_Memcpy(entry->keyIDs[i].data, tmpbuf, iLen);
        tmpbuf += iLen;
    }

    end = dbentry->data + dbentry->len;
    if ((eaddrlen == 0) && (end - tmpbuf > 1)) {
        /* read in the additional email addresses */
        entry->nemailAddrs = (((unsigned int)tmpbuf[0]) << 8) | tmpbuf[1];
        tmpbuf += 2;
        if (end - tmpbuf < 2 * (int)entry->nemailAddrs)
            goto loser;
        entry->emailAddrs = PORT_ArenaNewArray(arena, char *, entry->nemailAddrs);
        if (entry->emailAddrs == NULL) {
            PORT_SetError(SEC_ERROR_NO_MEMORY);
            goto loser;
        }
        for (i = 0; i < entry->nemailAddrs; i++) {
            int nameLen;
            if (end - tmpbuf < 2) {
                goto loser;
            }
            nameLen = (((int)tmpbuf[0]) << 8) | tmpbuf[1];
            tmpbuf += 2;
            if (end - tmpbuf < nameLen) {
                goto loser;
            }
            entry->emailAddrs[i] = PORT_ArenaAlloc(arena, nameLen);
            if (entry->emailAddrs == NULL) {
                PORT_SetError(SEC_ERROR_NO_MEMORY);
                goto loser;
            }
            PORT_Memcpy(entry->emailAddrs[i], tmpbuf, nameLen);
            tmpbuf += nameLen;
        }
        if (tmpbuf != end)
            goto loser;
    }
    PORT_ArenaUnmark(arena, mark);
    return (SECSuccess);

loser:
    PORT_ArenaRelease(arena, mark); /* discard above allocations */
    return (SECFailure);
}

/*
 * create a new subject entry with a single cert
 */

static certDBEntrySubject *
NewDBSubjectEntry(SECItem *derSubject, SECItem *certKey,
                  SECItem *keyID, char *nickname, char *emailAddr,
                  unsigned int flags)
{
    PLArenaPool *arena = NULL;
    certDBEntrySubject *entry;
    SECStatus rv;
    unsigned int nnlen;

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (arena == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }

    entry = (certDBEntrySubject *)PORT_ArenaAlloc(arena,
                                                  sizeof(certDBEntrySubject));
    if (entry == NULL) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        goto loser;
    }

    /* init common fields */
    entry->common.arena = arena;
    entry->common.type = certDBEntryTypeSubject;
    entry->common.version = CERT_DB_FILE_VERSION;
    entry->common.flags = flags;

    /* copy the subject */
    rv = SECITEM_CopyItem(arena, &entry->derSubject, derSubject);
    if (rv != SECSuccess) {
        goto loser;
    }

    entry->ncerts = 1;
    entry->nemailAddrs = 0;
    /* copy nickname */
    if (nickname && (*nickname != '\0')) {
        nnlen = PORT_Strlen(nickname) + 1;
        entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
        if (entry->nickname == NULL) {
            goto loser;
        }

        PORT_Memcpy(entry->nickname, nickname, nnlen);
    } else {
        entry->nickname = NULL;
    }

    /* copy email addr */
    if (emailAddr && (*emailAddr != '\0')) {
        emailAddr = nsslowcert_FixupEmailAddr(emailAddr);
        if (emailAddr == NULL) {
            entry->emailAddrs = NULL;
            goto loser;
        }

        entry->emailAddrs = (char **)PORT_ArenaAlloc(arena, sizeof(char *));
        if (entry->emailAddrs == NULL) {
            PORT_Free(emailAddr);
            goto loser;
        }
        entry->emailAddrs[0] = PORT_ArenaStrdup(arena, emailAddr);
        if (entry->emailAddrs[0]) {
            entry->nemailAddrs = 1;
        }

        PORT_Free(emailAddr);
    } else {
        entry->emailAddrs = NULL;
    }

    /* allocate space for certKeys and keyIDs */
    entry->certKeys = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem));
    entry->keyIDs = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem));
    if ((entry->certKeys == NULL) || (entry->keyIDs == NULL)) {
        goto loser;
    }

    /* copy the certKey and keyID */
    rv = SECITEM_CopyItem(arena, &entry->certKeys[0], certKey);
    if (rv != SECSuccess) {
        goto loser;
    }
    rv = SECITEM_CopyItem(arena, &entry->keyIDs[0], keyID);
    if (rv != SECSuccess) {
        goto loser;
    }

    return (entry);
loser:
    if (arena) {
        PORT_FreeArena(arena, PR_FALSE);
    }

    return (NULL);
}

/*
 * delete a subject entry
 */

static SECStatus
DeleteDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, SECItem *derSubject)
{
    SECItem dbkey;
    PLArenaPool *arena = NULL;
    SECStatus rv;

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

--> maximum size reached

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

Messung V0.5
C=96 H=85 G=90

¤ Dauer der Verarbeitung: 0.27 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.