tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

pcertdb.c (144026B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /*
      6 * Permanent Certificate database handling code
      7 */
      8 #include "lowkeyti.h"
      9 #include "pcert.h"
     10 #include "mcom_db.h"
     11 #include "pcert.h"
     12 #include "secitem.h"
     13 #include "secder.h"
     14 
     15 #include "secerr.h"
     16 #include "lgdb.h"
     17 
     18 /* forward declaration */
     19 NSSLOWCERTCertificate *
     20 nsslowcert_FindCertByDERCertNoLocking(NSSLOWCERTCertDBHandle *handle, SECItem *derCert);
     21 static SECStatus
     22 nsslowcert_UpdateSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle,
     23                              char *emailAddr, SECItem *derSubject, SECItem *emailProfile,
     24                              SECItem *profileTime);
     25 static SECStatus
     26 nsslowcert_UpdatePermCert(NSSLOWCERTCertDBHandle *dbhandle,
     27                          NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust);
     28 static SECStatus
     29 nsslowcert_UpdateCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl,
     30                     SECItem *crlKey, char *url, PRBool isKRL);
     31 
     32 static NSSLOWCERTCertificate *certListHead = NULL;
     33 static NSSLOWCERTTrust *trustListHead = NULL;
     34 static certDBEntryCert *entryListHead = NULL;
     35 static int certListCount = 0;
     36 static int trustListCount = 0;
     37 static int entryListCount = 0;
     38 #define MAX_CERT_LIST_COUNT 10
     39 #define MAX_TRUST_LIST_COUNT 10
     40 #define MAX_ENTRY_LIST_COUNT 10
     41 
     42 /*
     43 * the following functions are wrappers for the db library that implement
     44 * a global lock to make the database thread safe.
     45 */
     46 static PZLock *dbLock = NULL;
     47 static PZLock *certRefCountLock = NULL;
     48 static PZLock *certTrustLock = NULL;
     49 static PZLock *freeListLock = NULL;
     50 
     51 void
     52 certdb_InitDBLock(NSSLOWCERTCertDBHandle *handle)
     53 {
     54    if (dbLock == NULL) {
     55        dbLock = PZ_NewLock(nssILockCertDB);
     56        PORT_Assert(dbLock != NULL);
     57    }
     58 }
     59 
     60 SECStatus
     61 nsslowcert_InitLocks(void)
     62 {
     63    if (freeListLock == NULL) {
     64        freeListLock = PZ_NewLock(nssILockRefLock);
     65        if (freeListLock == NULL) {
     66            return SECFailure;
     67        }
     68    }
     69    if (certRefCountLock == NULL) {
     70        certRefCountLock = PZ_NewLock(nssILockRefLock);
     71        if (certRefCountLock == NULL) {
     72            return SECFailure;
     73        }
     74    }
     75    if (certTrustLock == NULL) {
     76        certTrustLock = PZ_NewLock(nssILockCertDB);
     77        if (certTrustLock == NULL) {
     78            return SECFailure;
     79        }
     80    }
     81 
     82    return SECSuccess;
     83 }
     84 
     85 /*
     86 * Acquire the global lock on the cert database.
     87 * This lock is currently used for the following operations:
     88 *  adding or deleting a cert to either the temp or perm databases
     89 *  converting a temp to perm or perm to temp
     90 *  changing (maybe just adding!?) the trust of a cert
     91 *      chaning the DB status checking Configuration
     92 */
     93 static void
     94 nsslowcert_LockDB(NSSLOWCERTCertDBHandle *handle)
     95 {
     96    PZ_EnterMonitor(handle->dbMon);
     97    return;
     98 }
     99 
    100 /*
    101 * Free the global cert database lock.
    102 */
    103 static void
    104 nsslowcert_UnlockDB(NSSLOWCERTCertDBHandle *handle)
    105 {
    106 #ifdef DEBUG
    107    PRStatus prstat = PZ_ExitMonitor(handle->dbMon);
    108    PORT_Assert(prstat == PR_SUCCESS);
    109 #else
    110    PZ_ExitMonitor(handle->dbMon);
    111 #endif
    112 }
    113 
    114 /*
    115 * Acquire the cert reference count lock
    116 * There is currently one global lock for all certs, but I'm putting a cert
    117 * arg here so that it will be easy to make it per-cert in the future if
    118 * that turns out to be necessary.
    119 */
    120 static void
    121 nsslowcert_LockCertRefCount(NSSLOWCERTCertificate *cert)
    122 {
    123    PORT_Assert(certRefCountLock != NULL);
    124 
    125    PZ_Lock(certRefCountLock);
    126    return;
    127 }
    128 
    129 /*
    130 * Free the cert reference count lock
    131 */
    132 static void
    133 nsslowcert_UnlockCertRefCount(NSSLOWCERTCertificate *cert)
    134 {
    135    PORT_Assert(certRefCountLock != NULL);
    136 
    137 #ifdef DEBUG
    138    {
    139        PRStatus prstat = PZ_Unlock(certRefCountLock);
    140        PORT_Assert(prstat == PR_SUCCESS);
    141    }
    142 #else
    143    PZ_Unlock(certRefCountLock);
    144 #endif
    145 }
    146 
    147 /*
    148 * Acquire the cert trust lock
    149 * There is currently one global lock for all certs, but I'm putting a cert
    150 * arg here so that it will be easy to make it per-cert in the future if
    151 * that turns out to be necessary.
    152 */
    153 static void
    154 nsslowcert_LockCertTrust(NSSLOWCERTCertificate *cert)
    155 {
    156    PORT_Assert(certTrustLock != NULL);
    157 
    158    PZ_Lock(certTrustLock);
    159    return;
    160 }
    161 
    162 /*
    163 * Free the cert trust lock
    164 */
    165 static void
    166 nsslowcert_UnlockCertTrust(NSSLOWCERTCertificate *cert)
    167 {
    168    PORT_Assert(certTrustLock != NULL);
    169 
    170 #ifdef DEBUG
    171    {
    172        PRStatus prstat = PZ_Unlock(certTrustLock);
    173        PORT_Assert(prstat == PR_SUCCESS);
    174    }
    175 #else
    176    PZ_Unlock(certTrustLock);
    177 #endif
    178 }
    179 
    180 /*
    181 * Acquire the cert reference count lock
    182 * There is currently one global lock for all certs, but I'm putting a cert
    183 * arg here so that it will be easy to make it per-cert in the future if
    184 * that turns out to be necessary.
    185 */
    186 static void
    187 nsslowcert_LockFreeList(void)
    188 {
    189    PORT_Assert(freeListLock != NULL);
    190 
    191    SKIP_AFTER_FORK(PZ_Lock(freeListLock));
    192    return;
    193 }
    194 
    195 /*
    196 * Free the cert reference count lock
    197 */
    198 static void
    199 nsslowcert_UnlockFreeList(void)
    200 {
    201    PORT_Assert(freeListLock != NULL);
    202 
    203 #ifdef DEBUG
    204    {
    205        PRStatus prstat = PR_SUCCESS;
    206        SKIP_AFTER_FORK(prstat = PZ_Unlock(freeListLock));
    207        PORT_Assert(prstat == PR_SUCCESS);
    208    }
    209 #else
    210    SKIP_AFTER_FORK(PZ_Unlock(freeListLock));
    211 #endif
    212 }
    213 
    214 NSSLOWCERTCertificate *
    215 nsslowcert_DupCertificate(NSSLOWCERTCertificate *c)
    216 {
    217    if (c) {
    218        nsslowcert_LockCertRefCount(c);
    219        ++c->referenceCount;
    220        nsslowcert_UnlockCertRefCount(c);
    221    }
    222    return c;
    223 }
    224 
    225 static int
    226 certdb_Get(DB *db, DBT *key, DBT *data, unsigned int flags)
    227 {
    228    int ret;
    229 
    230    PORT_Assert(dbLock != NULL);
    231    PZ_Lock(dbLock);
    232 
    233    ret = (*db->get)(db, key, data, flags);
    234 
    235    (void)PZ_Unlock(dbLock);
    236 
    237    return (ret);
    238 }
    239 
    240 static int
    241 certdb_Put(DB *db, DBT *key, DBT *data, unsigned int flags)
    242 {
    243    int ret = 0;
    244 
    245    PORT_Assert(dbLock != NULL);
    246    PZ_Lock(dbLock);
    247 
    248    ret = (*db->put)(db, key, data, flags);
    249 
    250    (void)PZ_Unlock(dbLock);
    251 
    252    return (ret);
    253 }
    254 
    255 static int
    256 certdb_Sync(DB *db, unsigned int flags)
    257 {
    258    int ret;
    259 
    260    PORT_Assert(dbLock != NULL);
    261    PZ_Lock(dbLock);
    262 
    263    ret = (*db->sync)(db, flags);
    264 
    265    (void)PZ_Unlock(dbLock);
    266 
    267    return (ret);
    268 }
    269 
    270 #define DB_NOT_FOUND -30991 /* from DBM 3.2 */
    271 static int
    272 certdb_Del(DB *db, DBT *key, unsigned int flags)
    273 {
    274    int ret;
    275 
    276    PORT_Assert(dbLock != NULL);
    277    PZ_Lock(dbLock);
    278 
    279    ret = (*db->del)(db, key, flags);
    280 
    281    (void)PZ_Unlock(dbLock);
    282 
    283    /* don't fail if the record is already deleted */
    284    if (ret == DB_NOT_FOUND) {
    285        ret = 0;
    286    }
    287 
    288    return (ret);
    289 }
    290 
    291 static int
    292 certdb_Seq(DB *db, DBT *key, DBT *data, unsigned int flags)
    293 {
    294    int ret;
    295 
    296    PORT_Assert(dbLock != NULL);
    297    PZ_Lock(dbLock);
    298 
    299    ret = (*db->seq)(db, key, data, flags);
    300 
    301    (void)PZ_Unlock(dbLock);
    302 
    303    return (ret);
    304 }
    305 
    306 static void
    307 certdb_Close(DB *db)
    308 {
    309    PORT_Assert(dbLock != NULL);
    310    SKIP_AFTER_FORK(PZ_Lock(dbLock));
    311 
    312    (*db->close)(db);
    313 
    314    SKIP_AFTER_FORK(PZ_Unlock(dbLock));
    315 
    316    return;
    317 }
    318 
    319 void
    320 pkcs11_freeNickname(char *nickname, char *space)
    321 {
    322    if (nickname && nickname != space) {
    323        PORT_Free(nickname);
    324    }
    325 }
    326 
    327 char *
    328 pkcs11_copyNickname(char *nickname, char *space, int spaceLen)
    329 {
    330    int len;
    331    char *copy = NULL;
    332 
    333    len = PORT_Strlen(nickname) + 1;
    334    if (len <= spaceLen) {
    335        copy = space;
    336        PORT_Memcpy(copy, nickname, len);
    337    } else {
    338        copy = PORT_Strdup(nickname);
    339    }
    340 
    341    return copy;
    342 }
    343 
    344 void
    345 pkcs11_freeStaticData(unsigned char *data, unsigned char *space)
    346 {
    347    if (data && data != space) {
    348        PORT_Free(data);
    349    }
    350 }
    351 
    352 unsigned char *
    353 pkcs11_allocStaticData(int len, unsigned char *space, int spaceLen)
    354 {
    355    unsigned char *data = NULL;
    356 
    357    if (len <= spaceLen) {
    358        data = space;
    359    } else {
    360        data = (unsigned char *)PORT_Alloc(len);
    361    }
    362 
    363    return data;
    364 }
    365 
    366 unsigned char *
    367 pkcs11_copyStaticData(unsigned char *data, int len,
    368                      unsigned char *space, int spaceLen)
    369 {
    370    unsigned char *copy = pkcs11_allocStaticData(len, space, spaceLen);
    371    if (copy) {
    372        PORT_Memcpy(copy, data, len);
    373    }
    374 
    375    return copy;
    376 }
    377 
    378 /*
    379 * destroy a database entry
    380 */
    381 static void
    382 DestroyDBEntry(certDBEntry *entry)
    383 {
    384    PLArenaPool *arena = entry->common.arena;
    385 
    386    /* must be one of our certDBEntry from the free list */
    387    if (arena == NULL) {
    388        certDBEntryCert *certEntry;
    389        if (entry->common.type != certDBEntryTypeCert) {
    390            return;
    391        }
    392        certEntry = (certDBEntryCert *)entry;
    393 
    394        pkcs11_freeStaticData(certEntry->derCert.data, certEntry->derCertSpace);
    395        pkcs11_freeNickname(certEntry->nickname, certEntry->nicknameSpace);
    396 
    397        nsslowcert_LockFreeList();
    398        if (entryListCount > MAX_ENTRY_LIST_COUNT) {
    399            PORT_Free(certEntry);
    400        } else {
    401            entryListCount++;
    402            PORT_Memset(certEntry, 0, sizeof(*certEntry));
    403            certEntry->next = entryListHead;
    404            entryListHead = certEntry;
    405        }
    406        nsslowcert_UnlockFreeList();
    407        return;
    408    }
    409 
    410    /* Zero out the entry struct, so that any further attempts to use it
    411     * will cause an exception (e.g. null pointer reference). */
    412    PORT_Memset(&entry->common, 0, sizeof entry->common);
    413    PORT_FreeArena(arena, PR_FALSE);
    414 
    415    return;
    416 }
    417 
    418 /* forward references */
    419 static void nsslowcert_DestroyCertificateNoLocking(NSSLOWCERTCertificate *cert);
    420 
    421 static SECStatus
    422 DeleteDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryType type, SECItem *dbkey)
    423 {
    424    DBT key;
    425    int ret;
    426 
    427    /* init the database key */
    428    key.data = dbkey->data;
    429    key.size = dbkey->len;
    430 
    431    dbkey->data[0] = (unsigned char)type;
    432 
    433    /* delete entry from database */
    434    ret = certdb_Del(handle->permCertDB, &key, 0);
    435    if (ret != 0) {
    436        PORT_SetError(SEC_ERROR_BAD_DATABASE);
    437        goto loser;
    438    }
    439 
    440    ret = certdb_Sync(handle->permCertDB, 0);
    441    if (ret) {
    442        PORT_SetError(SEC_ERROR_BAD_DATABASE);
    443        goto loser;
    444    }
    445 
    446    return (SECSuccess);
    447 
    448 loser:
    449    return (SECFailure);
    450 }
    451 
    452 static SECStatus
    453 ReadDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCommon *entry,
    454            SECItem *dbkey, SECItem *dbentry, PLArenaPool *arena)
    455 {
    456    DBT data, key;
    457    int ret;
    458    unsigned char *buf;
    459 
    460    /* init the database key */
    461    key.data = dbkey->data;
    462    key.size = dbkey->len;
    463 
    464    dbkey->data[0] = (unsigned char)entry->type;
    465 
    466    /* read entry from database */
    467    ret = certdb_Get(handle->permCertDB, &key, &data, 0);
    468    if (ret != 0) {
    469        PORT_SetError(SEC_ERROR_BAD_DATABASE);
    470        goto loser;
    471    }
    472 
    473    /* validate the entry */
    474    if (data.size < SEC_DB_ENTRY_HEADER_LEN) {
    475        PORT_SetError(SEC_ERROR_BAD_DATABASE);
    476        goto loser;
    477    }
    478    buf = (unsigned char *)data.data;
    479    /* version 7 has the same schema, we may be using a v7 db if we openned
    480     * the databases readonly. */
    481    if (!((buf[0] == (unsigned char)CERT_DB_FILE_VERSION) ||
    482          (buf[0] == (unsigned char)CERT_DB_V7_FILE_VERSION))) {
    483        PORT_SetError(SEC_ERROR_BAD_DATABASE);
    484        goto loser;
    485    }
    486    if (buf[1] != (unsigned char)entry->type) {
    487        PORT_SetError(SEC_ERROR_BAD_DATABASE);
    488        goto loser;
    489    }
    490 
    491    /* copy out header information */
    492    entry->version = (unsigned int)buf[0];
    493    entry->type = (certDBEntryType)buf[1];
    494    entry->flags = (unsigned int)buf[2];
    495 
    496    /* format body of entry for return to caller */
    497    dbentry->len = data.size - SEC_DB_ENTRY_HEADER_LEN;
    498    if (dbentry->len) {
    499        if (arena) {
    500            dbentry->data = (unsigned char *)
    501                PORT_ArenaAlloc(arena, dbentry->len);
    502            if (dbentry->data == NULL) {
    503                PORT_SetError(SEC_ERROR_NO_MEMORY);
    504                goto loser;
    505            }
    506 
    507            PORT_Memcpy(dbentry->data, &buf[SEC_DB_ENTRY_HEADER_LEN],
    508                        dbentry->len);
    509        } else {
    510            dbentry->data = &buf[SEC_DB_ENTRY_HEADER_LEN];
    511        }
    512    } else {
    513        dbentry->data = NULL;
    514    }
    515 
    516    return (SECSuccess);
    517 
    518 loser:
    519    return (SECFailure);
    520 }
    521 
    522 /**
    523 ** Implement low level database access
    524 **/
    525 static SECStatus
    526 WriteDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCommon *entry,
    527             SECItem *dbkey, SECItem *dbentry)
    528 {
    529    int ret;
    530    DBT data, key;
    531    unsigned char *buf;
    532 
    533    data.data = dbentry->data;
    534    data.size = dbentry->len;
    535 
    536    buf = (unsigned char *)data.data;
    537 
    538    buf[0] = (unsigned char)entry->version;
    539    buf[1] = (unsigned char)entry->type;
    540    buf[2] = (unsigned char)entry->flags;
    541 
    542    key.data = dbkey->data;
    543    key.size = dbkey->len;
    544 
    545    dbkey->data[0] = (unsigned char)entry->type;
    546 
    547    /* put the record into the database now */
    548    ret = certdb_Put(handle->permCertDB, &key, &data, 0);
    549 
    550    if (ret != 0) {
    551        goto loser;
    552    }
    553 
    554    ret = certdb_Sync(handle->permCertDB, 0);
    555 
    556    if (ret) {
    557        goto loser;
    558    }
    559 
    560    return (SECSuccess);
    561 
    562 loser:
    563    return (SECFailure);
    564 }
    565 
    566 /*
    567 * encode a database cert record
    568 */
    569 static SECStatus
    570 EncodeDBCertEntry(certDBEntryCert *entry, PLArenaPool *arena, SECItem *dbitem)
    571 {
    572    unsigned int nnlen;
    573    unsigned char *buf;
    574    char *nn;
    575    char zbuf = 0;
    576 
    577    if (entry->nickname) {
    578        nn = entry->nickname;
    579    } else {
    580        nn = &zbuf;
    581    }
    582    nnlen = PORT_Strlen(nn) + 1;
    583 
    584    /* allocate space for encoded database record, including space
    585     * for low level header
    586     */
    587    dbitem->len = entry->derCert.len + nnlen + DB_CERT_ENTRY_HEADER_LEN +
    588                  SEC_DB_ENTRY_HEADER_LEN;
    589 
    590    dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
    591    if (dbitem->data == NULL) {
    592        PORT_SetError(SEC_ERROR_NO_MEMORY);
    593        goto loser;
    594    }
    595 
    596    /* fill in database record */
    597    buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
    598 
    599    buf[0] = (PRUint8)(entry->trust.sslFlags >> 8);
    600    buf[1] = (PRUint8)(entry->trust.sslFlags);
    601    buf[2] = (PRUint8)(entry->trust.emailFlags >> 8);
    602    buf[3] = (PRUint8)(entry->trust.emailFlags);
    603    buf[4] = (PRUint8)(entry->trust.objectSigningFlags >> 8);
    604    buf[5] = (PRUint8)(entry->trust.objectSigningFlags);
    605    buf[6] = (PRUint8)(entry->derCert.len >> 8);
    606    buf[7] = (PRUint8)(entry->derCert.len);
    607    buf[8] = (PRUint8)(nnlen >> 8);
    608    buf[9] = (PRUint8)(nnlen);
    609 
    610    PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN], entry->derCert.data,
    611                entry->derCert.len);
    612 
    613    PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN + entry->derCert.len],
    614                nn, nnlen);
    615 
    616    return (SECSuccess);
    617 
    618 loser:
    619    return (SECFailure);
    620 }
    621 
    622 /*
    623 * encode a database key for a cert record
    624 */
    625 static SECStatus
    626 EncodeDBCertKey(const SECItem *certKey, PLArenaPool *arena, SECItem *dbkey)
    627 {
    628    unsigned int len = certKey->len + SEC_DB_KEY_HEADER_LEN;
    629    if (len > NSS_MAX_LEGACY_DB_KEY_SIZE)
    630        goto loser;
    631    if (arena) {
    632        dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, len);
    633    } else {
    634        if (dbkey->len < len) {
    635            dbkey->data = (unsigned char *)PORT_Alloc(len);
    636        }
    637    }
    638    dbkey->len = len;
    639    if (dbkey->data == NULL) {
    640        goto loser;
    641    }
    642    PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],
    643                certKey->data, certKey->len);
    644    dbkey->data[0] = certDBEntryTypeCert;
    645 
    646    return (SECSuccess);
    647 loser:
    648    return (SECFailure);
    649 }
    650 
    651 static SECStatus
    652 EncodeDBGenericKey(const SECItem *certKey, PLArenaPool *arena, SECItem *dbkey,
    653                   certDBEntryType entryType)
    654 {
    655    /*
    656     * we only allow _one_ KRL key!
    657     */
    658    if (entryType == certDBEntryTypeKeyRevocation) {
    659        dbkey->len = SEC_DB_KEY_HEADER_LEN;
    660        dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
    661        if (dbkey->data == NULL) {
    662            goto loser;
    663        }
    664        dbkey->data[0] = (unsigned char)entryType;
    665        return (SECSuccess);
    666    }
    667 
    668    dbkey->len = certKey->len + SEC_DB_KEY_HEADER_LEN;
    669    if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
    670        goto loser;
    671    dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
    672    if (dbkey->data == NULL) {
    673        goto loser;
    674    }
    675    PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],
    676                certKey->data, certKey->len);
    677    dbkey->data[0] = (unsigned char)entryType;
    678 
    679    return (SECSuccess);
    680 loser:
    681    return (SECFailure);
    682 }
    683 
    684 static SECStatus
    685 DecodeDBCertEntry(certDBEntryCert *entry, SECItem *dbentry)
    686 {
    687    unsigned int nnlen;
    688    unsigned int headerlen;
    689    int lenoff;
    690 
    691    /* allow updates of old versions of the database */
    692    switch (entry->common.version) {
    693        case 5:
    694            headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
    695            lenoff = 3;
    696            break;
    697        case 6:
    698            /* should not get here */
    699            PORT_Assert(0);
    700            headerlen = DB_CERT_V6_ENTRY_HEADER_LEN;
    701            lenoff = 3;
    702            break;
    703        case 7:
    704        case 8:
    705            headerlen = DB_CERT_ENTRY_HEADER_LEN;
    706            lenoff = 6;
    707            break;
    708        default:
    709            /* better not get here */
    710            PORT_Assert(0);
    711            headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
    712            lenoff = 3;
    713            break;
    714    }
    715 
    716    /* is record long enough for header? */
    717    if (dbentry->len < headerlen) {
    718        PORT_SetError(SEC_ERROR_BAD_DATABASE);
    719        goto loser;
    720    }
    721 
    722    /* is database entry correct length? */
    723    entry->derCert.len = ((dbentry->data[lenoff] << 8) |
    724                          dbentry->data[lenoff + 1]);
    725    nnlen = ((dbentry->data[lenoff + 2] << 8) | dbentry->data[lenoff + 3]);
    726    lenoff = dbentry->len - (entry->derCert.len + nnlen + headerlen);
    727    if (lenoff) {
    728        if (lenoff < 0 || (lenoff & 0xffff) != 0) {
    729            PORT_SetError(SEC_ERROR_BAD_DATABASE);
    730            goto loser;
    731        }
    732        /* The cert size exceeded 64KB.  Reconstruct the correct length. */
    733        entry->derCert.len += lenoff;
    734    }
    735 
    736    /* Is data long enough? */
    737    if (dbentry->len < headerlen + entry->derCert.len) {
    738        PORT_SetError(SEC_ERROR_BAD_DATABASE);
    739        goto loser;
    740    }
    741 
    742    /* copy the dercert */
    743    entry->derCert.data = pkcs11_copyStaticData(&dbentry->data[headerlen],
    744                                                entry->derCert.len, entry->derCertSpace, sizeof(entry->derCertSpace));
    745    if (entry->derCert.data == NULL) {
    746        PORT_SetError(SEC_ERROR_NO_MEMORY);
    747        goto loser;
    748    }
    749 
    750    /* copy the nickname */
    751    if (nnlen > 1) {
    752        /* Is data long enough? */
    753        if (dbentry->len < headerlen + entry->derCert.len + nnlen) {
    754            PORT_SetError(SEC_ERROR_BAD_DATABASE);
    755            goto loser;
    756        }
    757        entry->nickname = (char *)pkcs11_copyStaticData(
    758            &dbentry->data[headerlen + entry->derCert.len], nnlen,
    759            (unsigned char *)entry->nicknameSpace,
    760            sizeof(entry->nicknameSpace));
    761        if (entry->nickname == NULL) {
    762            PORT_SetError(SEC_ERROR_NO_MEMORY);
    763            goto loser;
    764        }
    765    } else {
    766        entry->nickname = NULL;
    767    }
    768 
    769    if (entry->common.version < 7) {
    770        /* allow updates of v5 db */
    771        entry->trust.sslFlags = dbentry->data[0];
    772        entry->trust.emailFlags = dbentry->data[1];
    773        entry->trust.objectSigningFlags = dbentry->data[2];
    774    } else {
    775        entry->trust.sslFlags = (dbentry->data[0] << 8) | dbentry->data[1];
    776        entry->trust.emailFlags = (dbentry->data[2] << 8) | dbentry->data[3];
    777        entry->trust.objectSigningFlags =
    778            (dbentry->data[4] << 8) | dbentry->data[5];
    779    }
    780 
    781    return (SECSuccess);
    782 loser:
    783    return (SECFailure);
    784 }
    785 
    786 /*
    787 * Create a new certDBEntryCert from existing data
    788 */
    789 static certDBEntryCert *
    790 NewDBCertEntry(SECItem *derCert, char *nickname,
    791               NSSLOWCERTCertTrust *trust, int flags)
    792 {
    793    certDBEntryCert *entry;
    794    PLArenaPool *arena = NULL;
    795    int nnlen;
    796 
    797    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    798 
    799    if (!arena) {
    800        goto loser;
    801    }
    802 
    803    entry = PORT_ArenaZNew(arena, certDBEntryCert);
    804    if (entry == NULL) {
    805        goto loser;
    806    }
    807 
    808    /* fill in the dbCert */
    809    entry->common.arena = arena;
    810    entry->common.type = certDBEntryTypeCert;
    811    entry->common.version = CERT_DB_FILE_VERSION;
    812    entry->common.flags = flags;
    813 
    814    if (trust) {
    815        entry->trust = *trust;
    816    }
    817 
    818    entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, derCert->len);
    819    if (!entry->derCert.data) {
    820        goto loser;
    821    }
    822    entry->derCert.len = derCert->len;
    823    PORT_Memcpy(entry->derCert.data, derCert->data, derCert->len);
    824 
    825    nnlen = (nickname ? strlen(nickname) + 1 : 0);
    826 
    827    if (nnlen) {
    828        entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
    829        if (!entry->nickname) {
    830            goto loser;
    831        }
    832        PORT_Memcpy(entry->nickname, nickname, nnlen);
    833 
    834    } else {
    835        entry->nickname = 0;
    836    }
    837 
    838    return (entry);
    839 
    840 loser:
    841 
    842    /* allocation error, free arena and return */
    843    if (arena) {
    844        PORT_FreeArena(arena, PR_FALSE);
    845    }
    846 
    847    PORT_SetError(SEC_ERROR_NO_MEMORY);
    848    return (0);
    849 }
    850 
    851 /*
    852 * Decode a version 4 DBCert from the byte stream database format
    853 * and construct a current database entry struct
    854 */
    855 static certDBEntryCert *
    856 DecodeV4DBCertEntry(unsigned char *buf, int len)
    857 {
    858    certDBEntryCert *entry;
    859    int certlen;
    860    int nnlen;
    861    PLArenaPool *arena;
    862 
    863    /* make sure length is at least long enough for the header */
    864    if (len < DBCERT_V4_HEADER_LEN) {
    865        PORT_SetError(SEC_ERROR_BAD_DATABASE);
    866        return (0);
    867    }
    868 
    869    /* get other lengths */
    870    certlen = buf[3] << 8 | buf[4];
    871    nnlen = buf[5] << 8 | buf[6];
    872 
    873    /* make sure DB entry is the right size */
    874    if ((certlen + nnlen + DBCERT_V4_HEADER_LEN) != len) {
    875        PORT_SetError(SEC_ERROR_BAD_DATABASE);
    876        return (0);
    877    }
    878 
    879    /* allocate arena */
    880    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    881 
    882    if (!arena) {
    883        PORT_SetError(SEC_ERROR_NO_MEMORY);
    884        return (0);
    885    }
    886 
    887    /* allocate structure and members */
    888    entry = (certDBEntryCert *)PORT_ArenaAlloc(arena, sizeof(certDBEntryCert));
    889 
    890    if (!entry) {
    891        goto loser;
    892    }
    893 
    894    entry->common.arena = arena;
    895    entry->common.version = CERT_DB_FILE_VERSION;
    896    entry->common.type = certDBEntryTypeCert;
    897    entry->common.flags = 0;
    898    entry->trust.sslFlags = buf[0];
    899    entry->trust.emailFlags = buf[1];
    900    entry->trust.objectSigningFlags = buf[2];
    901 
    902    entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, certlen);
    903    if (!entry->derCert.data) {
    904        goto loser;
    905    }
    906    entry->derCert.len = certlen;
    907    PORT_Memcpy(entry->derCert.data, &buf[DBCERT_V4_HEADER_LEN], certlen);
    908 
    909    if (nnlen) {
    910        entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
    911        if (!entry->nickname) {
    912            goto loser;
    913        }
    914        PORT_Memcpy(entry->nickname, &buf[DBCERT_V4_HEADER_LEN + certlen], nnlen);
    915 
    916        if (PORT_Strcmp(entry->nickname, "Server-Cert") == 0) {
    917            entry->trust.sslFlags |= CERTDB_USER;
    918        }
    919    } else {
    920        entry->nickname = 0;
    921    }
    922 
    923    return (entry);
    924 
    925 loser:
    926    PORT_FreeArena(arena, PR_FALSE);
    927    PORT_SetError(SEC_ERROR_NO_MEMORY);
    928    return (0);
    929 }
    930 
    931 /*
    932 * Encode a Certificate database entry into byte stream suitable for
    933 * the database
    934 */
    935 static SECStatus
    936 WriteDBCertEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry)
    937 {
    938    SECItem dbitem, dbkey;
    939    PLArenaPool *tmparena = NULL;
    940    SECItem tmpitem;
    941    SECStatus rv;
    942 
    943    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    944    if (tmparena == NULL) {
    945        goto loser;
    946    }
    947 
    948    rv = EncodeDBCertEntry(entry, tmparena, &dbitem);
    949    if (rv != SECSuccess) {
    950        goto loser;
    951    }
    952 
    953    /* get the database key and format it */
    954    rv = nsslowcert_KeyFromDERCert(tmparena, &entry->derCert, &tmpitem);
    955    if (rv == SECFailure) {
    956        goto loser;
    957    }
    958 
    959    rv = EncodeDBCertKey(&tmpitem, tmparena, &dbkey);
    960    if (rv == SECFailure) {
    961        goto loser;
    962    }
    963 
    964    /* now write it to the database */
    965    rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
    966    if (rv != SECSuccess) {
    967        goto loser;
    968    }
    969 
    970    PORT_FreeArena(tmparena, PR_FALSE);
    971    return (SECSuccess);
    972 
    973 loser:
    974    if (tmparena) {
    975        PORT_FreeArena(tmparena, PR_FALSE);
    976    }
    977    return (SECFailure);
    978 }
    979 
    980 /*
    981 * delete a certificate entry
    982 */
    983 static SECStatus
    984 DeleteDBCertEntry(NSSLOWCERTCertDBHandle *handle, SECItem *certKey)
    985 {
    986    SECItem dbkey;
    987    SECStatus rv;
    988 
    989    dbkey.data = NULL;
    990    dbkey.len = 0;
    991 
    992    rv = EncodeDBCertKey(certKey, NULL, &dbkey);
    993    if (rv != SECSuccess) {
    994        goto loser;
    995    }
    996 
    997    rv = DeleteDBEntry(handle, certDBEntryTypeCert, &dbkey);
    998    if (rv == SECFailure) {
    999        goto loser;
   1000    }
   1001 
   1002    PORT_Free(dbkey.data);
   1003 
   1004    return (SECSuccess);
   1005 
   1006 loser:
   1007    if (dbkey.data) {
   1008        PORT_Free(dbkey.data);
   1009    }
   1010    return (SECFailure);
   1011 }
   1012 
   1013 static certDBEntryCert *
   1014 CreateCertEntry(void)
   1015 {
   1016    certDBEntryCert *entry;
   1017 
   1018    nsslowcert_LockFreeList();
   1019    entry = entryListHead;
   1020    if (entry) {
   1021        entryListCount--;
   1022        entryListHead = entry->next;
   1023    }
   1024    PORT_Assert(entryListCount >= 0);
   1025    nsslowcert_UnlockFreeList();
   1026    if (entry) {
   1027        return entry;
   1028    }
   1029 
   1030    return PORT_ZNew(certDBEntryCert);
   1031 }
   1032 
   1033 static void
   1034 DestroyCertEntryFreeList(void)
   1035 {
   1036    certDBEntryCert *entry;
   1037 
   1038    nsslowcert_LockFreeList();
   1039    while (NULL != (entry = entryListHead)) {
   1040        entryListCount--;
   1041        entryListHead = entry->next;
   1042        PORT_Free(entry);
   1043    }
   1044    PORT_Assert(!entryListCount);
   1045    entryListCount = 0;
   1046    nsslowcert_UnlockFreeList();
   1047 }
   1048 
   1049 /*
   1050 * Read a certificate entry
   1051 */
   1052 static certDBEntryCert *
   1053 ReadDBCertEntry(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
   1054 {
   1055    certDBEntryCert *entry;
   1056    SECItem dbkey;
   1057    SECItem dbentry;
   1058    SECStatus rv;
   1059    unsigned char buf[512];
   1060 
   1061    dbkey.data = buf;
   1062    dbkey.len = sizeof(buf);
   1063 
   1064    entry = CreateCertEntry();
   1065    if (entry == NULL) {
   1066        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1067        goto loser;
   1068    }
   1069    entry->common.arena = NULL;
   1070    entry->common.type = certDBEntryTypeCert;
   1071 
   1072    rv = EncodeDBCertKey(certKey, NULL, &dbkey);
   1073    if (rv != SECSuccess) {
   1074        goto loser;
   1075    }
   1076 
   1077    rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, NULL);
   1078    if (rv == SECFailure) {
   1079        goto loser;
   1080    }
   1081 
   1082    rv = DecodeDBCertEntry(entry, &dbentry);
   1083    if (rv != SECSuccess) {
   1084        goto loser;
   1085    }
   1086 
   1087    pkcs11_freeStaticData(dbkey.data, buf);
   1088    dbkey.data = NULL;
   1089    return (entry);
   1090 
   1091 loser:
   1092    pkcs11_freeStaticData(dbkey.data, buf);
   1093    dbkey.data = NULL;
   1094    if (entry) {
   1095        DestroyDBEntry((certDBEntry *)entry);
   1096    }
   1097 
   1098    return (NULL);
   1099 }
   1100 
   1101 /*
   1102 * encode a database cert record
   1103 */
   1104 static SECStatus
   1105 EncodeDBCrlEntry(certDBEntryRevocation *entry, PLArenaPool *arena, SECItem *dbitem)
   1106 {
   1107    unsigned int nnlen = 0;
   1108    unsigned char *buf;
   1109 
   1110    if (entry->url) {
   1111        nnlen = PORT_Strlen(entry->url) + 1;
   1112    }
   1113 
   1114    /* allocate space for encoded database record, including space
   1115     * for low level header
   1116     */
   1117    dbitem->len = entry->derCrl.len + nnlen + SEC_DB_ENTRY_HEADER_LEN + DB_CRL_ENTRY_HEADER_LEN;
   1118 
   1119    dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
   1120    if (dbitem->data == NULL) {
   1121        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1122        goto loser;
   1123    }
   1124 
   1125    /* fill in database record */
   1126    buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
   1127 
   1128    buf[0] = (PRUint8)(entry->derCrl.len >> 8);
   1129    buf[1] = (PRUint8)(entry->derCrl.len);
   1130    buf[2] = (PRUint8)(nnlen >> 8);
   1131    buf[3] = (PRUint8)(nnlen);
   1132 
   1133    PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN], entry->derCrl.data,
   1134                entry->derCrl.len);
   1135 
   1136    if (nnlen != 0) {
   1137        PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN + entry->derCrl.len],
   1138                    entry->url, nnlen);
   1139    }
   1140 
   1141    return (SECSuccess);
   1142 
   1143 loser:
   1144    return (SECFailure);
   1145 }
   1146 
   1147 static SECStatus
   1148 DecodeDBCrlEntry(certDBEntryRevocation *entry, SECItem *dbentry)
   1149 {
   1150    unsigned int urlLen;
   1151    int lenDiff;
   1152 
   1153    /* is record long enough for header? */
   1154    if (dbentry->len < DB_CRL_ENTRY_HEADER_LEN) {
   1155        PORT_SetError(SEC_ERROR_BAD_DATABASE);
   1156        goto loser;
   1157    }
   1158 
   1159    /* is database entry correct length? */
   1160    entry->derCrl.len = ((dbentry->data[0] << 8) | dbentry->data[1]);
   1161    urlLen = ((dbentry->data[2] << 8) | dbentry->data[3]);
   1162    lenDiff = dbentry->len -
   1163              (entry->derCrl.len + urlLen + DB_CRL_ENTRY_HEADER_LEN);
   1164    if (lenDiff) {
   1165        if (lenDiff < 0 || (lenDiff & 0xffff) != 0) {
   1166            PORT_SetError(SEC_ERROR_BAD_DATABASE);
   1167            goto loser;
   1168        }
   1169        /* CRL entry is greater than 64 K. Hack to make this continue to work */
   1170        entry->derCrl.len += lenDiff;
   1171    }
   1172 
   1173    /* copy the der CRL */
   1174    entry->derCrl.data = (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
   1175                                                          entry->derCrl.len);
   1176    if (entry->derCrl.data == NULL) {
   1177        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1178        goto loser;
   1179    }
   1180    PORT_Memcpy(entry->derCrl.data, &dbentry->data[DB_CRL_ENTRY_HEADER_LEN],
   1181                entry->derCrl.len);
   1182 
   1183    /* copy the url */
   1184    entry->url = NULL;
   1185    if (urlLen != 0) {
   1186        entry->url = (char *)PORT_ArenaAlloc(entry->common.arena, urlLen);
   1187        if (entry->url == NULL) {
   1188            PORT_SetError(SEC_ERROR_NO_MEMORY);
   1189            goto loser;
   1190        }
   1191        PORT_Memcpy(entry->url,
   1192                    &dbentry->data[DB_CRL_ENTRY_HEADER_LEN + entry->derCrl.len],
   1193                    urlLen);
   1194    }
   1195 
   1196    return (SECSuccess);
   1197 loser:
   1198    return (SECFailure);
   1199 }
   1200 
   1201 /*
   1202 * Create a new certDBEntryRevocation from existing data
   1203 */
   1204 static certDBEntryRevocation *
   1205 NewDBCrlEntry(SECItem *derCrl, char *url, certDBEntryType crlType, int flags)
   1206 {
   1207    certDBEntryRevocation *entry;
   1208    PLArenaPool *arena = NULL;
   1209    int nnlen;
   1210 
   1211    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   1212 
   1213    if (!arena) {
   1214        goto loser;
   1215    }
   1216 
   1217    entry = PORT_ArenaZNew(arena, certDBEntryRevocation);
   1218    if (entry == NULL) {
   1219        goto loser;
   1220    }
   1221 
   1222    /* fill in the dbRevolcation */
   1223    entry->common.arena = arena;
   1224    entry->common.type = crlType;
   1225    entry->common.version = CERT_DB_FILE_VERSION;
   1226    entry->common.flags = flags;
   1227 
   1228    entry->derCrl.data = (unsigned char *)PORT_ArenaAlloc(arena, derCrl->len);
   1229    if (!entry->derCrl.data) {
   1230        goto loser;
   1231    }
   1232 
   1233    if (url) {
   1234        nnlen = PORT_Strlen(url) + 1;
   1235        entry->url = (char *)PORT_ArenaAlloc(arena, nnlen);
   1236        if (!entry->url) {
   1237            goto loser;
   1238        }
   1239        PORT_Memcpy(entry->url, url, nnlen);
   1240    } else {
   1241        entry->url = NULL;
   1242    }
   1243 
   1244    entry->derCrl.len = derCrl->len;
   1245    PORT_Memcpy(entry->derCrl.data, derCrl->data, derCrl->len);
   1246 
   1247    return (entry);
   1248 
   1249 loser:
   1250 
   1251    /* allocation error, free arena and return */
   1252    if (arena) {
   1253        PORT_FreeArena(arena, PR_FALSE);
   1254    }
   1255 
   1256    PORT_SetError(SEC_ERROR_NO_MEMORY);
   1257    return (0);
   1258 }
   1259 
   1260 static SECStatus
   1261 WriteDBCrlEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryRevocation *entry,
   1262                SECItem *crlKey)
   1263 {
   1264    SECItem dbkey;
   1265    PLArenaPool *tmparena = NULL;
   1266    SECItem encodedEntry;
   1267    SECStatus rv;
   1268 
   1269    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   1270    if (tmparena == NULL) {
   1271        goto loser;
   1272    }
   1273 
   1274    rv = EncodeDBCrlEntry(entry, tmparena, &encodedEntry);
   1275    if (rv == SECFailure) {
   1276        goto loser;
   1277    }
   1278 
   1279    rv = EncodeDBGenericKey(crlKey, tmparena, &dbkey, entry->common.type);
   1280    if (rv == SECFailure) {
   1281        goto loser;
   1282    }
   1283 
   1284    /* now write it to the database */
   1285    rv = WriteDBEntry(handle, &entry->common, &dbkey, &encodedEntry);
   1286    if (rv != SECSuccess) {
   1287        goto loser;
   1288    }
   1289 
   1290    PORT_FreeArena(tmparena, PR_FALSE);
   1291    return (SECSuccess);
   1292 
   1293 loser:
   1294    if (tmparena) {
   1295        PORT_FreeArena(tmparena, PR_FALSE);
   1296    }
   1297    return (SECFailure);
   1298 }
   1299 /*
   1300 * delete a crl entry
   1301 */
   1302 static SECStatus
   1303 DeleteDBCrlEntry(NSSLOWCERTCertDBHandle *handle, const SECItem *crlKey,
   1304                 certDBEntryType crlType)
   1305 {
   1306    SECItem dbkey;
   1307    PLArenaPool *arena = NULL;
   1308    SECStatus rv;
   1309 
   1310    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   1311    if (arena == NULL) {
   1312        goto loser;
   1313    }
   1314 
   1315    rv = EncodeDBGenericKey(crlKey, arena, &dbkey, crlType);
   1316    if (rv != SECSuccess) {
   1317        goto loser;
   1318    }
   1319 
   1320    rv = DeleteDBEntry(handle, crlType, &dbkey);
   1321    if (rv == SECFailure) {
   1322        goto loser;
   1323    }
   1324 
   1325    PORT_FreeArena(arena, PR_FALSE);
   1326    return (SECSuccess);
   1327 
   1328 loser:
   1329    if (arena) {
   1330        PORT_FreeArena(arena, PR_FALSE);
   1331    }
   1332 
   1333    return (SECFailure);
   1334 }
   1335 
   1336 /*
   1337 * Read a certificate entry
   1338 */
   1339 static certDBEntryRevocation *
   1340 ReadDBCrlEntry(NSSLOWCERTCertDBHandle *handle, SECItem *certKey,
   1341               certDBEntryType crlType)
   1342 {
   1343    PLArenaPool *arena = NULL;
   1344    PLArenaPool *tmparena = NULL;
   1345    certDBEntryRevocation *entry;
   1346    SECItem dbkey;
   1347    SECItem dbentry;
   1348    SECStatus rv;
   1349 
   1350    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   1351    if (arena == NULL) {
   1352        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1353        goto loser;
   1354    }
   1355 
   1356    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   1357    if (tmparena == NULL) {
   1358        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1359        goto loser;
   1360    }
   1361 
   1362    entry = (certDBEntryRevocation *)
   1363        PORT_ArenaAlloc(arena, sizeof(certDBEntryRevocation));
   1364    if (entry == NULL) {
   1365        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1366        goto loser;
   1367    }
   1368    entry->common.arena = arena;
   1369    entry->common.type = crlType;
   1370 
   1371    rv = EncodeDBGenericKey(certKey, tmparena, &dbkey, crlType);
   1372    if (rv != SECSuccess) {
   1373        goto loser;
   1374    }
   1375 
   1376    rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, NULL);
   1377    if (rv == SECFailure) {
   1378        goto loser;
   1379    }
   1380 
   1381    rv = DecodeDBCrlEntry(entry, &dbentry);
   1382    if (rv != SECSuccess) {
   1383        goto loser;
   1384    }
   1385 
   1386    PORT_FreeArena(tmparena, PR_FALSE);
   1387    return (entry);
   1388 
   1389 loser:
   1390    if (tmparena) {
   1391        PORT_FreeArena(tmparena, PR_FALSE);
   1392    }
   1393    if (arena) {
   1394        PORT_FreeArena(arena, PR_FALSE);
   1395    }
   1396 
   1397    return (NULL);
   1398 }
   1399 
   1400 void
   1401 nsslowcert_DestroyDBEntry(certDBEntry *entry)
   1402 {
   1403    DestroyDBEntry(entry);
   1404    return;
   1405 }
   1406 
   1407 /*
   1408 * Encode a database nickname record
   1409 */
   1410 static SECStatus
   1411 EncodeDBNicknameEntry(certDBEntryNickname *entry, PLArenaPool *arena,
   1412                      SECItem *dbitem)
   1413 {
   1414    unsigned char *buf;
   1415 
   1416    /* allocate space for encoded database record, including space
   1417     * for low level header
   1418     */
   1419    dbitem->len = entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN +
   1420                  SEC_DB_ENTRY_HEADER_LEN;
   1421    dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
   1422    if (dbitem->data == NULL) {
   1423        goto loser;
   1424    }
   1425 
   1426    /* fill in database record */
   1427    buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
   1428    buf[0] = (PRUint8)(entry->subjectName.len >> 8);
   1429    buf[1] = (PRUint8)(entry->subjectName.len);
   1430    PORT_Memcpy(&buf[DB_NICKNAME_ENTRY_HEADER_LEN], entry->subjectName.data,
   1431                entry->subjectName.len);
   1432 
   1433    return (SECSuccess);
   1434 
   1435 loser:
   1436    return (SECFailure);
   1437 }
   1438 
   1439 /*
   1440 * Encode a database key for a nickname record
   1441 */
   1442 static SECStatus
   1443 EncodeDBNicknameKey(char *nickname, PLArenaPool *arena,
   1444                    SECItem *dbkey)
   1445 {
   1446    unsigned int nnlen;
   1447 
   1448    nnlen = PORT_Strlen(nickname) + 1; /* includes null */
   1449 
   1450    /* now get the database key and format it */
   1451    dbkey->len = nnlen + SEC_DB_KEY_HEADER_LEN;
   1452    if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
   1453        goto loser;
   1454    dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
   1455    if (dbkey->data == NULL) {
   1456        goto loser;
   1457    }
   1458    PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], nickname, nnlen);
   1459    dbkey->data[0] = certDBEntryTypeNickname;
   1460 
   1461    return (SECSuccess);
   1462 
   1463 loser:
   1464    return (SECFailure);
   1465 }
   1466 
   1467 static SECStatus
   1468 DecodeDBNicknameEntry(certDBEntryNickname *entry, SECItem *dbentry,
   1469                      char *nickname)
   1470 {
   1471    int lenDiff;
   1472 
   1473    /* is record long enough for header? */
   1474    if (dbentry->len < DB_NICKNAME_ENTRY_HEADER_LEN) {
   1475        PORT_SetError(SEC_ERROR_BAD_DATABASE);
   1476        goto loser;
   1477    }
   1478 
   1479    /* is database entry correct length? */
   1480    entry->subjectName.len = ((dbentry->data[0] << 8) | dbentry->data[1]);
   1481    lenDiff = dbentry->len -
   1482              (entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN);
   1483    if (lenDiff) {
   1484        if (lenDiff < 0 || (lenDiff & 0xffff) != 0) {
   1485            PORT_SetError(SEC_ERROR_BAD_DATABASE);
   1486            goto loser;
   1487        }
   1488        /* The entry size exceeded 64KB.  Reconstruct the correct length. */
   1489        entry->subjectName.len += lenDiff;
   1490    }
   1491 
   1492    /* copy the certkey */
   1493    entry->subjectName.data =
   1494        (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
   1495                                         entry->subjectName.len);
   1496    if (entry->subjectName.data == NULL) {
   1497        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1498        goto loser;
   1499    }
   1500    PORT_Memcpy(entry->subjectName.data,
   1501                &dbentry->data[DB_NICKNAME_ENTRY_HEADER_LEN],
   1502                entry->subjectName.len);
   1503    entry->subjectName.type = siBuffer;
   1504 
   1505    entry->nickname = (char *)PORT_ArenaAlloc(entry->common.arena,
   1506                                              PORT_Strlen(nickname) + 1);
   1507    if (entry->nickname) {
   1508        PORT_Strcpy(entry->nickname, nickname);
   1509    }
   1510 
   1511    return (SECSuccess);
   1512 
   1513 loser:
   1514    return (SECFailure);
   1515 }
   1516 
   1517 /*
   1518 * create a new nickname entry
   1519 */
   1520 static certDBEntryNickname *
   1521 NewDBNicknameEntry(char *nickname, SECItem *subjectName, unsigned int flags)
   1522 {
   1523    PLArenaPool *arena = NULL;
   1524    certDBEntryNickname *entry;
   1525    int nnlen;
   1526    SECStatus rv;
   1527 
   1528    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   1529    if (arena == NULL) {
   1530        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1531        goto loser;
   1532    }
   1533 
   1534    entry = (certDBEntryNickname *)PORT_ArenaAlloc(arena,
   1535                                                   sizeof(certDBEntryNickname));
   1536    if (entry == NULL) {
   1537        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1538        goto loser;
   1539    }
   1540 
   1541    /* init common fields */
   1542    entry->common.arena = arena;
   1543    entry->common.type = certDBEntryTypeNickname;
   1544    entry->common.version = CERT_DB_FILE_VERSION;
   1545    entry->common.flags = flags;
   1546 
   1547    /* copy the nickname */
   1548    nnlen = PORT_Strlen(nickname) + 1;
   1549 
   1550    entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
   1551    if (entry->nickname == NULL) {
   1552        goto loser;
   1553    }
   1554 
   1555    PORT_Memcpy(entry->nickname, nickname, nnlen);
   1556 
   1557    rv = SECITEM_CopyItem(arena, &entry->subjectName, subjectName);
   1558    if (rv != SECSuccess) {
   1559        goto loser;
   1560    }
   1561 
   1562    return (entry);
   1563 loser:
   1564    if (arena) {
   1565        PORT_FreeArena(arena, PR_FALSE);
   1566    }
   1567 
   1568    return (NULL);
   1569 }
   1570 
   1571 /*
   1572 * delete a nickname entry
   1573 */
   1574 static SECStatus
   1575 DeleteDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, char *nickname)
   1576 {
   1577    PLArenaPool *arena = NULL;
   1578    SECStatus rv;
   1579    SECItem dbkey;
   1580 
   1581    if (nickname == NULL) {
   1582        return (SECSuccess);
   1583    }
   1584 
   1585    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   1586    if (arena == NULL) {
   1587        goto loser;
   1588    }
   1589 
   1590    rv = EncodeDBNicknameKey(nickname, arena, &dbkey);
   1591    if (rv != SECSuccess) {
   1592        goto loser;
   1593    }
   1594 
   1595    rv = DeleteDBEntry(handle, certDBEntryTypeNickname, &dbkey);
   1596    if (rv == SECFailure) {
   1597        goto loser;
   1598    }
   1599 
   1600    PORT_FreeArena(arena, PR_FALSE);
   1601    return (SECSuccess);
   1602 
   1603 loser:
   1604    if (arena) {
   1605        PORT_FreeArena(arena, PR_FALSE);
   1606    }
   1607 
   1608    return (SECFailure);
   1609 }
   1610 
   1611 /*
   1612 * Read a nickname entry
   1613 */
   1614 static certDBEntryNickname *
   1615 ReadDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, char *nickname)
   1616 {
   1617    PLArenaPool *arena = NULL;
   1618    PLArenaPool *tmparena = NULL;
   1619    certDBEntryNickname *entry;
   1620    SECItem dbkey;
   1621    SECItem dbentry;
   1622    SECStatus rv;
   1623 
   1624    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   1625    if (arena == NULL) {
   1626        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1627        goto loser;
   1628    }
   1629 
   1630    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   1631    if (tmparena == NULL) {
   1632        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1633        goto loser;
   1634    }
   1635 
   1636    entry = (certDBEntryNickname *)PORT_ArenaAlloc(arena,
   1637                                                   sizeof(certDBEntryNickname));
   1638    if (entry == NULL) {
   1639        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1640        goto loser;
   1641    }
   1642    entry->common.arena = arena;
   1643    entry->common.type = certDBEntryTypeNickname;
   1644 
   1645    rv = EncodeDBNicknameKey(nickname, tmparena, &dbkey);
   1646    if (rv != SECSuccess) {
   1647        goto loser;
   1648    }
   1649 
   1650    rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
   1651    if (rv == SECFailure) {
   1652        goto loser;
   1653    }
   1654 
   1655    /* is record long enough for header? */
   1656    if (dbentry.len < DB_NICKNAME_ENTRY_HEADER_LEN) {
   1657        PORT_SetError(SEC_ERROR_BAD_DATABASE);
   1658        goto loser;
   1659    }
   1660 
   1661    rv = DecodeDBNicknameEntry(entry, &dbentry, nickname);
   1662    if (rv != SECSuccess) {
   1663        goto loser;
   1664    }
   1665 
   1666    PORT_FreeArena(tmparena, PR_FALSE);
   1667    return (entry);
   1668 
   1669 loser:
   1670    if (tmparena) {
   1671        PORT_FreeArena(tmparena, PR_FALSE);
   1672    }
   1673    if (arena) {
   1674        PORT_FreeArena(arena, PR_FALSE);
   1675    }
   1676 
   1677    return (NULL);
   1678 }
   1679 
   1680 /*
   1681 * Encode a nickname entry into byte stream suitable for
   1682 * the database
   1683 */
   1684 static SECStatus
   1685 WriteDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryNickname *entry)
   1686 {
   1687    SECItem dbitem, dbkey;
   1688    PLArenaPool *tmparena = NULL;
   1689    SECStatus rv;
   1690 
   1691    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   1692    if (tmparena == NULL) {
   1693        goto loser;
   1694    }
   1695 
   1696    rv = EncodeDBNicknameEntry(entry, tmparena, &dbitem);
   1697    if (rv != SECSuccess) {
   1698        goto loser;
   1699    }
   1700 
   1701    rv = EncodeDBNicknameKey(entry->nickname, tmparena, &dbkey);
   1702    if (rv != SECSuccess) {
   1703        goto loser;
   1704    }
   1705 
   1706    /* now write it to the database */
   1707    rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
   1708    if (rv != SECSuccess) {
   1709        goto loser;
   1710    }
   1711 
   1712    PORT_FreeArena(tmparena, PR_FALSE);
   1713    return (SECSuccess);
   1714 
   1715 loser:
   1716    if (tmparena) {
   1717        PORT_FreeArena(tmparena, PR_FALSE);
   1718    }
   1719    return (SECFailure);
   1720 }
   1721 
   1722 static SECStatus
   1723 EncodeDBSMimeEntry(certDBEntrySMime *entry, PLArenaPool *arena,
   1724                   SECItem *dbitem)
   1725 {
   1726    unsigned char *buf;
   1727 
   1728    /* allocate space for encoded database record, including space
   1729     * for low level header
   1730     */
   1731    dbitem->len = entry->subjectName.len + entry->smimeOptions.len +
   1732                  entry->optionsDate.len +
   1733                  DB_SMIME_ENTRY_HEADER_LEN + SEC_DB_ENTRY_HEADER_LEN;
   1734 
   1735    dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
   1736    if (dbitem->data == NULL) {
   1737        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1738        goto loser;
   1739    }
   1740 
   1741    /* fill in database record */
   1742    buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
   1743 
   1744    buf[0] = (PRUint8)(entry->subjectName.len >> 8);
   1745    buf[1] = (PRUint8)(entry->subjectName.len);
   1746    buf[2] = (PRUint8)(entry->smimeOptions.len >> 8);
   1747    buf[3] = (PRUint8)(entry->smimeOptions.len);
   1748    buf[4] = (PRUint8)(entry->optionsDate.len >> 8);
   1749    buf[5] = (PRUint8)(entry->optionsDate.len);
   1750 
   1751    /* if no smime options, then there should not be an options date either */
   1752    PORT_Assert(!((entry->smimeOptions.len == 0) &&
   1753                  (entry->optionsDate.len != 0)));
   1754 
   1755    PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN], entry->subjectName.data,
   1756                entry->subjectName.len);
   1757    if (entry->smimeOptions.len) {
   1758        PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN + entry->subjectName.len],
   1759                    entry->smimeOptions.data,
   1760                    entry->smimeOptions.len);
   1761        PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN + entry->subjectName.len +
   1762                         entry->smimeOptions.len],
   1763                    entry->optionsDate.data,
   1764                    entry->optionsDate.len);
   1765    }
   1766 
   1767    return (SECSuccess);
   1768 
   1769 loser:
   1770    return (SECFailure);
   1771 }
   1772 
   1773 /*
   1774 * Encode a database key for a SMIME record
   1775 */
   1776 static SECStatus
   1777 EncodeDBSMimeKey(char *emailAddr, PLArenaPool *arena,
   1778                 SECItem *dbkey)
   1779 {
   1780    unsigned int addrlen;
   1781 
   1782    addrlen = PORT_Strlen(emailAddr) + 1; /* includes null */
   1783 
   1784    /* now get the database key and format it */
   1785    dbkey->len = addrlen + SEC_DB_KEY_HEADER_LEN;
   1786    if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
   1787        goto loser;
   1788    dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
   1789    if (dbkey->data == NULL) {
   1790        goto loser;
   1791    }
   1792    PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], emailAddr, addrlen);
   1793    dbkey->data[0] = certDBEntryTypeSMimeProfile;
   1794 
   1795    return (SECSuccess);
   1796 
   1797 loser:
   1798    return (SECFailure);
   1799 }
   1800 
   1801 /*
   1802 * Decode a database SMIME record
   1803 */
   1804 static SECStatus
   1805 DecodeDBSMimeEntry(certDBEntrySMime *entry, SECItem *dbentry, char *emailAddr)
   1806 {
   1807    int lenDiff;
   1808 
   1809    /* is record long enough for header? */
   1810    if (dbentry->len < DB_SMIME_ENTRY_HEADER_LEN) {
   1811        PORT_SetError(SEC_ERROR_BAD_DATABASE);
   1812        goto loser;
   1813    }
   1814 
   1815    /* is database entry correct length? */
   1816    entry->subjectName.len = ((dbentry->data[0] << 8) | dbentry->data[1]);
   1817    entry->smimeOptions.len = ((dbentry->data[2] << 8) | dbentry->data[3]);
   1818    entry->optionsDate.len = ((dbentry->data[4] << 8) | dbentry->data[5]);
   1819    lenDiff = dbentry->len - (entry->subjectName.len +
   1820                              entry->smimeOptions.len +
   1821                              entry->optionsDate.len +
   1822                              DB_SMIME_ENTRY_HEADER_LEN);
   1823    if (lenDiff) {
   1824        if (lenDiff < 0 || (lenDiff & 0xffff) != 0) {
   1825            PORT_SetError(SEC_ERROR_BAD_DATABASE);
   1826            goto loser;
   1827        }
   1828        /* The entry size exceeded 64KB.  Reconstruct the correct length. */
   1829        entry->subjectName.len += lenDiff;
   1830    }
   1831 
   1832    /* copy the subject name */
   1833    entry->subjectName.data =
   1834        (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
   1835                                         entry->subjectName.len);
   1836    if (entry->subjectName.data == NULL) {
   1837        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1838        goto loser;
   1839    }
   1840    PORT_Memcpy(entry->subjectName.data,
   1841                &dbentry->data[DB_SMIME_ENTRY_HEADER_LEN],
   1842                entry->subjectName.len);
   1843 
   1844    /* copy the smime options */
   1845    if (entry->smimeOptions.len) {
   1846        entry->smimeOptions.data =
   1847            (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
   1848                                             entry->smimeOptions.len);
   1849        if (entry->smimeOptions.data == NULL) {
   1850            PORT_SetError(SEC_ERROR_NO_MEMORY);
   1851            goto loser;
   1852        }
   1853        PORT_Memcpy(entry->smimeOptions.data,
   1854                    &dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
   1855                                   entry->subjectName.len],
   1856                    entry->smimeOptions.len);
   1857    } else {
   1858        entry->smimeOptions.data = NULL;
   1859    }
   1860    if (entry->optionsDate.len) {
   1861        entry->optionsDate.data =
   1862            (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
   1863                                             entry->optionsDate.len);
   1864        if (entry->optionsDate.data == NULL) {
   1865            PORT_SetError(SEC_ERROR_NO_MEMORY);
   1866            goto loser;
   1867        }
   1868        PORT_Memcpy(entry->optionsDate.data,
   1869                    &dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
   1870                                   entry->subjectName.len +
   1871                                   entry->smimeOptions.len],
   1872                    entry->optionsDate.len);
   1873    } else {
   1874        entry->optionsDate.data = NULL;
   1875    }
   1876 
   1877    /* both options and options date must either exist or not exist */
   1878    if (((entry->optionsDate.len == 0) ||
   1879         (entry->smimeOptions.len == 0)) &&
   1880        entry->smimeOptions.len != entry->optionsDate.len) {
   1881        PORT_SetError(SEC_ERROR_BAD_DATABASE);
   1882        goto loser;
   1883    }
   1884 
   1885    entry->emailAddr = (char *)PORT_ArenaAlloc(entry->common.arena,
   1886                                               PORT_Strlen(emailAddr) + 1);
   1887    if (entry->emailAddr) {
   1888        PORT_Strcpy(entry->emailAddr, emailAddr);
   1889    }
   1890 
   1891    return (SECSuccess);
   1892 
   1893 loser:
   1894    return (SECFailure);
   1895 }
   1896 
   1897 /*
   1898 * create a new SMIME entry
   1899 */
   1900 static certDBEntrySMime *
   1901 NewDBSMimeEntry(char *emailAddr, SECItem *subjectName, SECItem *smimeOptions,
   1902                SECItem *optionsDate, unsigned int flags)
   1903 {
   1904    PLArenaPool *arena = NULL;
   1905    certDBEntrySMime *entry;
   1906    int addrlen;
   1907    SECStatus rv;
   1908 
   1909    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   1910    if (arena == NULL) {
   1911        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1912        goto loser;
   1913    }
   1914 
   1915    entry = (certDBEntrySMime *)PORT_ArenaAlloc(arena,
   1916                                                sizeof(certDBEntrySMime));
   1917    if (entry == NULL) {
   1918        PORT_SetError(SEC_ERROR_NO_MEMORY);
   1919        goto loser;
   1920    }
   1921 
   1922    /* init common fields */
   1923    entry->common.arena = arena;
   1924    entry->common.type = certDBEntryTypeSMimeProfile;
   1925    entry->common.version = CERT_DB_FILE_VERSION;
   1926    entry->common.flags = flags;
   1927 
   1928    /* copy the email addr */
   1929    addrlen = PORT_Strlen(emailAddr) + 1;
   1930 
   1931    entry->emailAddr = (char *)PORT_ArenaAlloc(arena, addrlen);
   1932    if (entry->emailAddr == NULL) {
   1933        goto loser;
   1934    }
   1935 
   1936    PORT_Memcpy(entry->emailAddr, emailAddr, addrlen);
   1937 
   1938    /* copy the subject name */
   1939    rv = SECITEM_CopyItem(arena, &entry->subjectName, subjectName);
   1940    if (rv != SECSuccess) {
   1941        goto loser;
   1942    }
   1943 
   1944    /* copy the smime options */
   1945    if (smimeOptions) {
   1946        rv = SECITEM_CopyItem(arena, &entry->smimeOptions, smimeOptions);
   1947        if (rv != SECSuccess) {
   1948            goto loser;
   1949        }
   1950    } else {
   1951        PORT_Assert(optionsDate == NULL);
   1952        entry->smimeOptions.data = NULL;
   1953        entry->smimeOptions.len = 0;
   1954    }
   1955 
   1956    /* copy the options date */
   1957    if (optionsDate) {
   1958        rv = SECITEM_CopyItem(arena, &entry->optionsDate, optionsDate);
   1959        if (rv != SECSuccess) {
   1960            goto loser;
   1961        }
   1962    } else {
   1963        PORT_Assert(smimeOptions == NULL);
   1964        entry->optionsDate.data = NULL;
   1965        entry->optionsDate.len = 0;
   1966    }
   1967 
   1968    return (entry);
   1969 loser:
   1970    if (arena) {
   1971        PORT_FreeArena(arena, PR_FALSE);
   1972    }
   1973 
   1974    return (NULL);
   1975 }
   1976 
   1977 /*
   1978 * delete a SMIME entry
   1979 */
   1980 static SECStatus
   1981 DeleteDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, char *emailAddr)
   1982 {
   1983    PLArenaPool *arena = NULL;
   1984    SECStatus rv;
   1985    SECItem dbkey;
   1986 
   1987    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   1988    if (arena == NULL) {
   1989        goto loser;
   1990    }
   1991 
   1992    rv = EncodeDBSMimeKey(emailAddr, arena, &dbkey);
   1993    if (rv != SECSuccess) {
   1994        goto loser;
   1995    }
   1996 
   1997    rv = DeleteDBEntry(handle, certDBEntryTypeSMimeProfile, &dbkey);
   1998    if (rv == SECFailure) {
   1999        goto loser;
   2000    }
   2001 
   2002    PORT_FreeArena(arena, PR_FALSE);
   2003    return (SECSuccess);
   2004 
   2005 loser:
   2006    if (arena) {
   2007        PORT_FreeArena(arena, PR_FALSE);
   2008    }
   2009 
   2010    return (SECFailure);
   2011 }
   2012 
   2013 /*
   2014 * Read a SMIME entry
   2015 */
   2016 certDBEntrySMime *
   2017 nsslowcert_ReadDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, char *emailAddr)
   2018 {
   2019    PLArenaPool *arena = NULL;
   2020    PLArenaPool *tmparena = NULL;
   2021    certDBEntrySMime *entry = NULL;
   2022    SECItem dbkey;
   2023    SECItem dbentry;
   2024    SECStatus rv;
   2025 
   2026    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   2027    if (arena == NULL) {
   2028        PORT_SetError(SEC_ERROR_NO_MEMORY);
   2029        goto loser;
   2030    }
   2031 
   2032    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   2033    if (tmparena == NULL) {
   2034        PORT_SetError(SEC_ERROR_NO_MEMORY);
   2035        goto loser;
   2036    }
   2037 
   2038    entry = (certDBEntrySMime *)PORT_ArenaZAlloc(arena,
   2039                                                 sizeof(certDBEntrySMime));
   2040    if (entry == NULL) {
   2041        PORT_SetError(SEC_ERROR_NO_MEMORY);
   2042        goto loser;
   2043    }
   2044    entry->common.arena = arena;
   2045    entry->common.type = certDBEntryTypeSMimeProfile;
   2046 
   2047    rv = EncodeDBSMimeKey(emailAddr, tmparena, &dbkey);
   2048    if (rv != SECSuccess) {
   2049        goto loser;
   2050    }
   2051 
   2052    rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
   2053    if (rv == SECFailure) {
   2054        goto loser;
   2055    }
   2056 
   2057    /* is record long enough for header? */
   2058    if (dbentry.len < DB_SMIME_ENTRY_HEADER_LEN) {
   2059        PORT_SetError(SEC_ERROR_BAD_DATABASE);
   2060        goto loser;
   2061    }
   2062 
   2063    rv = DecodeDBSMimeEntry(entry, &dbentry, emailAddr);
   2064    if (rv != SECSuccess) {
   2065        goto loser;
   2066    }
   2067 
   2068    PORT_FreeArena(tmparena, PR_FALSE);
   2069    return (entry);
   2070 
   2071 loser:
   2072    if (tmparena) {
   2073        PORT_FreeArena(tmparena, PR_FALSE);
   2074    }
   2075    if (arena) {
   2076        PORT_FreeArena(arena, PR_FALSE);
   2077    }
   2078 
   2079    return (NULL);
   2080 }
   2081 
   2082 /*
   2083 * Encode a SMIME entry into byte stream suitable for
   2084 * the database
   2085 */
   2086 static SECStatus
   2087 WriteDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, certDBEntrySMime *entry)
   2088 {
   2089    SECItem dbitem, dbkey;
   2090    PLArenaPool *tmparena = NULL;
   2091    SECStatus rv;
   2092 
   2093    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   2094    if (tmparena == NULL) {
   2095        goto loser;
   2096    }
   2097 
   2098    rv = EncodeDBSMimeEntry(entry, tmparena, &dbitem);
   2099    if (rv != SECSuccess) {
   2100        goto loser;
   2101    }
   2102 
   2103    rv = EncodeDBSMimeKey(entry->emailAddr, tmparena, &dbkey);
   2104    if (rv != SECSuccess) {
   2105        goto loser;
   2106    }
   2107 
   2108    /* now write it to the database */
   2109    rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
   2110    if (rv != SECSuccess) {
   2111        goto loser;
   2112    }
   2113 
   2114    PORT_FreeArena(tmparena, PR_FALSE);
   2115    return (SECSuccess);
   2116 
   2117 loser:
   2118    if (tmparena) {
   2119        PORT_FreeArena(tmparena, PR_FALSE);
   2120    }
   2121    return (SECFailure);
   2122 }
   2123 
   2124 /*
   2125 * Encode a database subject record
   2126 */
   2127 static SECStatus
   2128 EncodeDBSubjectEntry(certDBEntrySubject *entry, PLArenaPool *arena,
   2129                     SECItem *dbitem)
   2130 {
   2131    unsigned char *buf;
   2132    int len;
   2133    unsigned int ncerts;
   2134    unsigned int i;
   2135    unsigned char *tmpbuf;
   2136    unsigned int nnlen = 0;
   2137    unsigned int eaddrslen = 0;
   2138    int keyidoff;
   2139    SECItem *certKeys = entry->certKeys;
   2140    SECItem *keyIDs = entry->keyIDs;
   2141    ;
   2142 
   2143    if (entry->nickname) {
   2144        nnlen = PORT_Strlen(entry->nickname) + 1;
   2145    }
   2146    if (entry->emailAddrs) {
   2147        eaddrslen = 2;
   2148        for (i = 0; i < entry->nemailAddrs; i++) {
   2149            eaddrslen += PORT_Strlen(entry->emailAddrs[i]) + 1 + 2;
   2150        }
   2151    }
   2152 
   2153    ncerts = entry->ncerts;
   2154 
   2155    /* compute the length of the entry */
   2156    keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen;
   2157    len = keyidoff + (4 * ncerts) + eaddrslen;
   2158    for (i = 0; i < ncerts; i++) {
   2159        if (keyIDs[i].len > 0xffff ||
   2160            (certKeys[i].len > 0xffff)) {
   2161            PORT_SetError(SEC_ERROR_INPUT_LEN);
   2162            goto loser;
   2163        }
   2164        len += certKeys[i].len;
   2165        len += keyIDs[i].len;
   2166    }
   2167 
   2168    /* allocate space for encoded database record, including space
   2169     * for low level header
   2170     */
   2171    dbitem->len = len + SEC_DB_ENTRY_HEADER_LEN;
   2172 
   2173    dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
   2174    if (dbitem->data == NULL) {
   2175        PORT_SetError(SEC_ERROR_NO_MEMORY);
   2176        goto loser;
   2177    }
   2178 
   2179    /* fill in database record */
   2180    buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
   2181 
   2182    buf[0] = (PRUint8)(ncerts >> 8);
   2183    buf[1] = (PRUint8)(ncerts);
   2184    buf[2] = (PRUint8)(nnlen >> 8);
   2185    buf[3] = (PRUint8)(nnlen);
   2186    /* v7 email field is NULL in v8 */
   2187    buf[4] = 0;
   2188    buf[5] = 0;
   2189 
   2190    PORT_Assert(DB_SUBJECT_ENTRY_HEADER_LEN == 6);
   2191 
   2192    if (entry->nickname) {
   2193        PORT_Memcpy(&buf[DB_SUBJECT_ENTRY_HEADER_LEN], entry->nickname, nnlen);
   2194    }
   2195    tmpbuf = &buf[keyidoff];
   2196    for (i = 0; i < ncerts; i++) {
   2197        tmpbuf[0] = (PRUint8)(certKeys[i].len >> 8);
   2198        tmpbuf[1] = (PRUint8)(certKeys[i].len);
   2199        tmpbuf += 2;
   2200    }
   2201    for (i = 0; i < ncerts; i++) {
   2202        tmpbuf[0] = (PRUint8)(keyIDs[i].len >> 8);
   2203        tmpbuf[1] = (PRUint8)(keyIDs[i].len);
   2204        tmpbuf += 2;
   2205    }
   2206 
   2207    for (i = 0; i < ncerts; i++) {
   2208        PORT_Memcpy(tmpbuf, certKeys[i].data, certKeys[i].len);
   2209        tmpbuf += certKeys[i].len;
   2210    }
   2211    for (i = 0; i < ncerts; i++) {
   2212        if (keyIDs[i].len) {
   2213            PORT_Memcpy(tmpbuf, keyIDs[i].data, keyIDs[i].len);
   2214            tmpbuf += keyIDs[i].len;
   2215        }
   2216    }
   2217 
   2218    if (entry->emailAddrs) {
   2219        tmpbuf[0] = (PRUint8)(entry->nemailAddrs >> 8);
   2220        tmpbuf[1] = (PRUint8)(entry->nemailAddrs);
   2221        tmpbuf += 2;
   2222        for (i = 0; i < entry->nemailAddrs; i++) {
   2223            int nameLen = PORT_Strlen(entry->emailAddrs[i]) + 1;
   2224            tmpbuf[0] = (PRUint8)(nameLen >> 8);
   2225            tmpbuf[1] = (PRUint8)(nameLen);
   2226            tmpbuf += 2;
   2227            PORT_Memcpy(tmpbuf, entry->emailAddrs[i], nameLen);
   2228            tmpbuf += nameLen;
   2229        }
   2230    }
   2231 
   2232    PORT_Assert(tmpbuf == &buf[len]);
   2233 
   2234    return (SECSuccess);
   2235 
   2236 loser:
   2237    return (SECFailure);
   2238 }
   2239 
   2240 /*
   2241 * Encode a database key for a subject record
   2242 */
   2243 static SECStatus
   2244 EncodeDBSubjectKey(SECItem *derSubject, PLArenaPool *arena,
   2245                   SECItem *dbkey)
   2246 {
   2247    dbkey->len = derSubject->len + SEC_DB_KEY_HEADER_LEN;
   2248    if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
   2249        goto loser;
   2250    dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
   2251    if (dbkey->data == NULL) {
   2252        goto loser;
   2253    }
   2254    PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], derSubject->data,
   2255                derSubject->len);
   2256    dbkey->data[0] = certDBEntryTypeSubject;
   2257 
   2258    return (SECSuccess);
   2259 
   2260 loser:
   2261    return (SECFailure);
   2262 }
   2263 
   2264 static SECStatus
   2265 DecodeDBSubjectEntry(certDBEntrySubject *entry, SECItem *dbentry,
   2266                     const SECItem *derSubject)
   2267 {
   2268    PLArenaPool *arena = entry->common.arena;
   2269    unsigned char *tmpbuf;
   2270    unsigned char *end;
   2271    void *mark = PORT_ArenaMark(arena);
   2272    unsigned int eaddrlen;
   2273    unsigned int i;
   2274    unsigned int keyidoff;
   2275    unsigned int len;
   2276    unsigned int ncerts = 0;
   2277    unsigned int nnlen;
   2278    SECStatus rv;
   2279 
   2280    rv = SECITEM_CopyItem(arena, &entry->derSubject, derSubject);
   2281    if (rv != SECSuccess) {
   2282        goto loser;
   2283    }
   2284 
   2285    /* is record long enough for header? */
   2286    if (dbentry->len < DB_SUBJECT_ENTRY_HEADER_LEN) {
   2287        PORT_SetError(SEC_ERROR_BAD_DATABASE);
   2288        goto loser;
   2289    }
   2290 
   2291    entry->ncerts = ncerts = ((dbentry->data[0] << 8) | dbentry->data[1]);
   2292    nnlen = ((dbentry->data[2] << 8) | dbentry->data[3]);
   2293    eaddrlen = ((dbentry->data[4] << 8) | dbentry->data[5]);
   2294    keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen + eaddrlen;
   2295    len = keyidoff + (4 * ncerts);
   2296    if (dbentry->len < len) {
   2297        PORT_SetError(SEC_ERROR_BAD_DATABASE);
   2298        goto loser;
   2299    }
   2300 
   2301    entry->certKeys = PORT_ArenaNewArray(arena, SECItem, ncerts);
   2302    entry->keyIDs = PORT_ArenaNewArray(arena, SECItem, ncerts);
   2303    if ((entry->certKeys == NULL) || (entry->keyIDs == NULL)) {
   2304        PORT_SetError(SEC_ERROR_NO_MEMORY);
   2305        goto loser;
   2306    }
   2307 
   2308    if (nnlen > 1) { /* null terminator is stored */
   2309        entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
   2310        if (entry->nickname == NULL) {
   2311            PORT_SetError(SEC_ERROR_NO_MEMORY);
   2312            goto loser;
   2313        }
   2314        PORT_Memcpy(entry->nickname,
   2315                    &dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN],
   2316                    nnlen);
   2317    } else {
   2318        entry->nickname = NULL;
   2319    }
   2320 
   2321    /* if we have an old style email entry, there is only one */
   2322    entry->nemailAddrs = 0;
   2323    if (eaddrlen > 1) { /* null terminator is stored */
   2324        entry->emailAddrs = PORT_ArenaNewArray(arena, char *, 2);
   2325        if (entry->emailAddrs == NULL) {
   2326            PORT_SetError(SEC_ERROR_NO_MEMORY);
   2327            goto loser;
   2328        }
   2329        entry->emailAddrs[0] = (char *)PORT_ArenaAlloc(arena, eaddrlen);
   2330        if (entry->emailAddrs[0] == NULL) {
   2331            PORT_SetError(SEC_ERROR_NO_MEMORY);
   2332            goto loser;
   2333        }
   2334        PORT_Memcpy(entry->emailAddrs[0],
   2335                    &dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN + nnlen],
   2336                    eaddrlen);
   2337        entry->nemailAddrs = 1;
   2338    } else {
   2339        entry->emailAddrs = NULL;
   2340    }
   2341 
   2342    /* collect the lengths of the certKeys and keyIDs, and total the
   2343     * overall length.
   2344     */
   2345    tmpbuf = &dbentry->data[keyidoff];
   2346    for (i = 0; i < ncerts; i++) {
   2347        unsigned int itemlen = (tmpbuf[0] << 8) | tmpbuf[1];
   2348        entry->certKeys[i].len = itemlen;
   2349        len += itemlen;
   2350        tmpbuf += 2;
   2351    }
   2352    for (i = 0; i < ncerts; i++) {
   2353        unsigned int itemlen = (tmpbuf[0] << 8) | tmpbuf[1];
   2354        entry->keyIDs[i].len = itemlen;
   2355        len += itemlen;
   2356        tmpbuf += 2;
   2357    }
   2358 
   2359    /* is encoded entry large enough ? */
   2360    if (len > dbentry->len) {
   2361        PORT_SetError(SEC_ERROR_BAD_DATABASE);
   2362        goto loser;
   2363    }
   2364 
   2365    for (i = 0; i < ncerts; i++) {
   2366        unsigned int kLen = entry->certKeys[i].len;
   2367        entry->certKeys[i].data = (unsigned char *)PORT_ArenaAlloc(arena, kLen);
   2368        if (entry->certKeys[i].data == NULL) {
   2369            PORT_SetError(SEC_ERROR_NO_MEMORY);
   2370            goto loser;
   2371        }
   2372        PORT_Memcpy(entry->certKeys[i].data, tmpbuf, kLen);
   2373        tmpbuf += kLen;
   2374    }
   2375    for (i = 0; i < ncerts; i++) {
   2376        unsigned int iLen = entry->keyIDs[i].len;
   2377        entry->keyIDs[i].data = (unsigned char *)PORT_ArenaAlloc(arena, iLen);
   2378        if (entry->keyIDs[i].data == NULL) {
   2379            PORT_SetError(SEC_ERROR_NO_MEMORY);
   2380            goto loser;
   2381        }
   2382        PORT_Memcpy(entry->keyIDs[i].data, tmpbuf, iLen);
   2383        tmpbuf += iLen;
   2384    }
   2385 
   2386    end = dbentry->data + dbentry->len;
   2387    if ((eaddrlen == 0) && (end - tmpbuf > 1)) {
   2388        /* read in the additional email addresses */
   2389        entry->nemailAddrs = (((unsigned int)tmpbuf[0]) << 8) | tmpbuf[1];
   2390        tmpbuf += 2;
   2391        if (end - tmpbuf < 2 * (int)entry->nemailAddrs)
   2392            goto loser;
   2393        entry->emailAddrs = PORT_ArenaNewArray(arena, char *, entry->nemailAddrs);
   2394        if (entry->emailAddrs == NULL) {
   2395            PORT_SetError(SEC_ERROR_NO_MEMORY);
   2396            goto loser;
   2397        }
   2398        for (i = 0; i < entry->nemailAddrs; i++) {
   2399            int nameLen;
   2400            if (end - tmpbuf < 2) {
   2401                goto loser;
   2402            }
   2403            nameLen = (((int)tmpbuf[0]) << 8) | tmpbuf[1];
   2404            tmpbuf += 2;
   2405            if (end - tmpbuf < nameLen) {
   2406                goto loser;
   2407            }
   2408            entry->emailAddrs[i] = PORT_ArenaAlloc(arena, nameLen);
   2409            if (entry->emailAddrs == NULL) {
   2410                PORT_SetError(SEC_ERROR_NO_MEMORY);
   2411                goto loser;
   2412            }
   2413            PORT_Memcpy(entry->emailAddrs[i], tmpbuf, nameLen);
   2414            tmpbuf += nameLen;
   2415        }
   2416        if (tmpbuf != end)
   2417            goto loser;
   2418    }
   2419    PORT_ArenaUnmark(arena, mark);
   2420    return (SECSuccess);
   2421 
   2422 loser:
   2423    PORT_ArenaRelease(arena, mark); /* discard above allocations */
   2424    return (SECFailure);
   2425 }
   2426 
   2427 /*
   2428 * create a new subject entry with a single cert
   2429 */
   2430 static certDBEntrySubject *
   2431 NewDBSubjectEntry(SECItem *derSubject, SECItem *certKey,
   2432                  SECItem *keyID, char *nickname, char *emailAddr,
   2433                  unsigned int flags)
   2434 {
   2435    PLArenaPool *arena = NULL;
   2436    certDBEntrySubject *entry;
   2437    SECStatus rv;
   2438    unsigned int nnlen;
   2439 
   2440    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   2441    if (arena == NULL) {
   2442        PORT_SetError(SEC_ERROR_NO_MEMORY);
   2443        goto loser;
   2444    }
   2445 
   2446    entry = (certDBEntrySubject *)PORT_ArenaAlloc(arena,
   2447                                                  sizeof(certDBEntrySubject));
   2448    if (entry == NULL) {
   2449        PORT_SetError(SEC_ERROR_NO_MEMORY);
   2450        goto loser;
   2451    }
   2452 
   2453    /* init common fields */
   2454    entry->common.arena = arena;
   2455    entry->common.type = certDBEntryTypeSubject;
   2456    entry->common.version = CERT_DB_FILE_VERSION;
   2457    entry->common.flags = flags;
   2458 
   2459    /* copy the subject */
   2460    rv = SECITEM_CopyItem(arena, &entry->derSubject, derSubject);
   2461    if (rv != SECSuccess) {
   2462        goto loser;
   2463    }
   2464 
   2465    entry->ncerts = 1;
   2466    entry->nemailAddrs = 0;
   2467    /* copy nickname */
   2468    if (nickname && (*nickname != '\0')) {
   2469        nnlen = PORT_Strlen(nickname) + 1;
   2470        entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
   2471        if (entry->nickname == NULL) {
   2472            goto loser;
   2473        }
   2474 
   2475        PORT_Memcpy(entry->nickname, nickname, nnlen);
   2476    } else {
   2477        entry->nickname = NULL;
   2478    }
   2479 
   2480    /* copy email addr */
   2481    if (emailAddr && (*emailAddr != '\0')) {
   2482        emailAddr = nsslowcert_FixupEmailAddr(emailAddr);
   2483        if (emailAddr == NULL) {
   2484            entry->emailAddrs = NULL;
   2485            goto loser;
   2486        }
   2487 
   2488        entry->emailAddrs = (char **)PORT_ArenaAlloc(arena, sizeof(char *));
   2489        if (entry->emailAddrs == NULL) {
   2490            PORT_Free(emailAddr);
   2491            goto loser;
   2492        }
   2493        entry->emailAddrs[0] = PORT_ArenaStrdup(arena, emailAddr);
   2494        if (entry->emailAddrs[0]) {
   2495            entry->nemailAddrs = 1;
   2496        }
   2497 
   2498        PORT_Free(emailAddr);
   2499    } else {
   2500        entry->emailAddrs = NULL;
   2501    }
   2502 
   2503    /* allocate space for certKeys and keyIDs */
   2504    entry->certKeys = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem));
   2505    entry->keyIDs = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem));
   2506    if ((entry->certKeys == NULL) || (entry->keyIDs == NULL)) {
   2507        goto loser;
   2508    }
   2509 
   2510    /* copy the certKey and keyID */
   2511    rv = SECITEM_CopyItem(arena, &entry->certKeys[0], certKey);
   2512    if (rv != SECSuccess) {
   2513        goto loser;
   2514    }
   2515    rv = SECITEM_CopyItem(arena, &entry->keyIDs[0], keyID);
   2516    if (rv != SECSuccess) {
   2517        goto loser;
   2518    }
   2519 
   2520    return (entry);
   2521 loser:
   2522    if (arena) {
   2523        PORT_FreeArena(arena, PR_FALSE);
   2524    }
   2525 
   2526    return (NULL);
   2527 }
   2528 
   2529 /*
   2530 * delete a subject entry
   2531 */
   2532 static SECStatus
   2533 DeleteDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, SECItem *derSubject)
   2534 {
   2535    SECItem dbkey;
   2536    PLArenaPool *arena = NULL;
   2537    SECStatus rv;
   2538 
   2539    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   2540    if (arena == NULL) {
   2541        goto loser;
   2542    }
   2543 
   2544    rv = EncodeDBSubjectKey(derSubject, arena, &dbkey);
   2545    if (rv != SECSuccess) {
   2546        goto loser;
   2547    }
   2548 
   2549    rv = DeleteDBEntry(handle, certDBEntryTypeSubject, &dbkey);
   2550    if (rv == SECFailure) {
   2551        goto loser;
   2552    }
   2553 
   2554    PORT_FreeArena(arena, PR_FALSE);
   2555    return (SECSuccess);
   2556 
   2557 loser:
   2558    if (arena) {
   2559        PORT_FreeArena(arena, PR_FALSE);
   2560    }
   2561 
   2562    return (SECFailure);
   2563 }
   2564 
   2565 /*
   2566 * Read the subject entry
   2567 */
   2568 static certDBEntrySubject *
   2569 ReadDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, SECItem *derSubject)
   2570 {
   2571    /* |arena| isn't function-bounded, so cannot be a PORTCheapArenaPool. */
   2572    PLArenaPool *arena = NULL;
   2573    PORTCheapArenaPool tmpArena;
   2574 
   2575    certDBEntrySubject *entry;
   2576    SECItem dbkey;
   2577    SECItem dbentry;
   2578    SECStatus rv;
   2579 
   2580    PORT_InitCheapArena(&tmpArena, DER_DEFAULT_CHUNKSIZE);
   2581    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   2582    if (arena == NULL) {
   2583        PORT_SetError(SEC_ERROR_NO_MEMORY);
   2584        goto loser;
   2585    }
   2586 
   2587    entry = (certDBEntrySubject *)PORT_ArenaAlloc(arena,
   2588                                                  sizeof(certDBEntrySubject));
   2589    if (entry == NULL) {
   2590        PORT_SetError(SEC_ERROR_NO_MEMORY);
   2591        goto loser;
   2592    }
   2593    entry->common.arena = arena;
   2594    entry->common.type = certDBEntryTypeSubject;
   2595 
   2596    rv = EncodeDBSubjectKey(derSubject, &tmpArena.arena, &dbkey);
   2597    if (rv != SECSuccess) {
   2598        goto loser;
   2599    }
   2600 
   2601    rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, &tmpArena.arena);
   2602    if (rv == SECFailure) {
   2603        goto loser;
   2604    }
   2605 
   2606    rv = DecodeDBSubjectEntry(entry, &dbentry, derSubject);
   2607    if (rv == SECFailure) {
   2608        goto loser;
   2609    }
   2610 
   2611    PORT_DestroyCheapArena(&tmpArena);
   2612    return (entry);
   2613 
   2614 loser:
   2615    PORT_DestroyCheapArena(&tmpArena);
   2616    if (arena) {
   2617        PORT_FreeArena(arena, PR_FALSE);
   2618    }
   2619 
   2620    return (NULL);
   2621 }
   2622 
   2623 /*
   2624 * Encode a subject name entry into byte stream suitable for
   2625 * the database
   2626 */
   2627 static SECStatus
   2628 WriteDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, certDBEntrySubject *entry)
   2629 {
   2630    SECItem dbitem, dbkey;
   2631    PLArenaPool *tmparena = NULL;
   2632    SECStatus rv;
   2633 
   2634    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   2635    if (tmparena == NULL) {
   2636        goto loser;
   2637    }
   2638 
   2639    rv = EncodeDBSubjectEntry(entry, tmparena, &dbitem);
   2640    if (rv != SECSuccess) {
   2641        goto loser;
   2642    }
   2643 
   2644    rv = EncodeDBSubjectKey(&entry->derSubject, tmparena, &dbkey);
   2645    if (rv != SECSuccess) {
   2646        goto loser;
   2647    }
   2648 
   2649    /* now write it to the database */
   2650    rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
   2651    if (rv != SECSuccess) {
   2652        goto loser;
   2653    }
   2654 
   2655    PORT_FreeArena(tmparena, PR_FALSE);
   2656    return (SECSuccess);
   2657 
   2658 loser:
   2659    if (tmparena) {
   2660        PORT_FreeArena(tmparena, PR_FALSE);
   2661    }
   2662    return (SECFailure);
   2663 }
   2664 
   2665 typedef enum { nsslowcert_remove,
   2666               nsslowcert_add } nsslowcertUpdateType;
   2667 
   2668 static SECStatus
   2669 nsslowcert_UpdateSubjectEmailAddr(NSSLOWCERTCertDBHandle *dbhandle,
   2670                                  SECItem *derSubject, char *emailAddr, nsslowcertUpdateType updateType)
   2671 {
   2672    certDBEntrySubject *entry = NULL;
   2673    int index = -1, i;
   2674    SECStatus rv;
   2675 
   2676    if (emailAddr) {
   2677        emailAddr = nsslowcert_FixupEmailAddr(emailAddr);
   2678        if (emailAddr == NULL) {
   2679            return SECFailure;
   2680        }
   2681    } else {
   2682        return SECSuccess;
   2683    }
   2684 
   2685    entry = ReadDBSubjectEntry(dbhandle, derSubject);
   2686    if (entry == NULL) {
   2687        rv = SECFailure;
   2688        goto done;
   2689    }
   2690 
   2691    for (i = 0; i < (int)(entry->nemailAddrs); i++) {
   2692        if (PORT_Strcmp(entry->emailAddrs[i], emailAddr) == 0) {
   2693            index = i;
   2694        }
   2695    }
   2696 
   2697    if (updateType == nsslowcert_remove) {
   2698        if (index == -1) {
   2699            rv = SECSuccess;
   2700            goto done;
   2701        }
   2702        entry->nemailAddrs--;
   2703        for (i = index; i < (int)(entry->nemailAddrs); i++) {
   2704            entry->emailAddrs[i] = entry->emailAddrs[i + 1];
   2705        }
   2706    } else {
   2707        char **newAddrs = NULL;
   2708 
   2709        if (index != -1) {
   2710            rv = SECSuccess;
   2711            goto done;
   2712        }
   2713        newAddrs = (char **)PORT_ArenaAlloc(entry->common.arena,
   2714                                            (entry->nemailAddrs + 1) * sizeof(char *));
   2715        if (!newAddrs) {
   2716            rv = SECFailure;
   2717            goto done;
   2718        }
   2719        for (i = 0; i < (int)(entry->nemailAddrs); i++) {
   2720            newAddrs[i] = entry->emailAddrs[i];
   2721        }
   2722        newAddrs[entry->nemailAddrs] =
   2723            PORT_ArenaStrdup(entry->common.arena, emailAddr);
   2724        if (!newAddrs[entry->nemailAddrs]) {
   2725            rv = SECFailure;
   2726            goto done;
   2727        }
   2728        entry->emailAddrs = newAddrs;
   2729        entry->nemailAddrs++;
   2730    }
   2731 
   2732    /* delete the subject entry */
   2733    DeleteDBSubjectEntry(dbhandle, derSubject);
   2734 
   2735    /* write the new one */
   2736    rv = WriteDBSubjectEntry(dbhandle, entry);
   2737 
   2738 done:
   2739    if (entry)
   2740        DestroyDBEntry((certDBEntry *)entry);
   2741    if (emailAddr)
   2742        PORT_Free(emailAddr);
   2743    return rv;
   2744 }
   2745 
   2746 /*
   2747 * writes a nickname to an existing subject entry that does not currently
   2748 * have one
   2749 */
   2750 static SECStatus
   2751 AddNicknameToSubject(NSSLOWCERTCertDBHandle *dbhandle,
   2752                     NSSLOWCERTCertificate *cert, char *nickname)
   2753 {
   2754    certDBEntrySubject *entry;
   2755    SECStatus rv;
   2756 
   2757    if (nickname == NULL) {
   2758        return (SECFailure);
   2759    }
   2760 
   2761    entry = ReadDBSubjectEntry(dbhandle, &cert->derSubject);
   2762    PORT_Assert(entry != NULL);
   2763    if (entry == NULL) {
   2764        goto loser;
   2765    }
   2766 
   2767    PORT_Assert(entry->nickname == NULL);
   2768    if (entry->nickname != NULL) {
   2769        goto loser;
   2770    }
   2771 
   2772    entry->nickname = PORT_ArenaStrdup(entry->common.arena, nickname);
   2773 
   2774    if (entry->nickname == NULL) {
   2775        goto loser;
   2776    }
   2777 
   2778    /* delete the subject entry */
   2779    DeleteDBSubjectEntry(dbhandle, &cert->derSubject);
   2780 
   2781    /* write the new one */
   2782    rv = WriteDBSubjectEntry(dbhandle, entry);
   2783    if (rv != SECSuccess) {
   2784        goto loser;
   2785    }
   2786 
   2787    DestroyDBEntry((certDBEntry *)entry);
   2788    return (SECSuccess);
   2789 
   2790 loser:
   2791    DestroyDBEntry((certDBEntry *)entry);
   2792    return (SECFailure);
   2793 }
   2794 
   2795 /*
   2796 * create a new version entry
   2797 */
   2798 static certDBEntryVersion *
   2799 NewDBVersionEntry(unsigned int flags)
   2800 {
   2801    PLArenaPool *arena = NULL;
   2802    certDBEntryVersion *entry;
   2803 
   2804    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   2805    if (arena == NULL) {
   2806        PORT_SetError(SEC_ERROR_NO_MEMORY);
   2807        goto loser;
   2808    }
   2809 
   2810    entry = (certDBEntryVersion *)PORT_ArenaAlloc(arena,
   2811                                                  sizeof(certDBEntryVersion));
   2812    if (entry == NULL) {
   2813        PORT_SetError(SEC_ERROR_NO_MEMORY);
   2814        goto loser;
   2815    }
   2816    entry->common.arena = arena;
   2817    entry->common.type = certDBEntryTypeVersion;
   2818    entry->common.version = CERT_DB_FILE_VERSION;
   2819    entry->common.flags = flags;
   2820 
   2821    return (entry);
   2822 loser:
   2823    if (arena) {
   2824        PORT_FreeArena(arena, PR_FALSE);
   2825    }
   2826 
   2827    return (NULL);
   2828 }
   2829 
   2830 /*
   2831 * Read the version entry
   2832 */
   2833 static certDBEntryVersion *
   2834 ReadDBVersionEntry(NSSLOWCERTCertDBHandle *handle)
   2835 {
   2836    PLArenaPool *arena = NULL;
   2837    PLArenaPool *tmparena = NULL;
   2838    certDBEntryVersion *entry;
   2839    SECItem dbkey;
   2840    SECItem dbentry;
   2841    SECStatus rv;
   2842 
   2843    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   2844    if (arena == NULL) {
   2845        PORT_SetError(SEC_ERROR_NO_MEMORY);
   2846        goto loser;
   2847    }
   2848 
   2849    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   2850    if (tmparena == NULL) {
   2851        PORT_SetError(SEC_ERROR_NO_MEMORY);
   2852        goto loser;
   2853    }
   2854 
   2855    entry = PORT_ArenaZNew(arena, certDBEntryVersion);
   2856    if (entry == NULL) {
   2857        PORT_SetError(SEC_ERROR_NO_MEMORY);
   2858        goto loser;
   2859    }
   2860    entry->common.arena = arena;
   2861    entry->common.type = certDBEntryTypeVersion;
   2862 
   2863    /* now get the database key and format it */
   2864    dbkey.len = SEC_DB_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
   2865    dbkey.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbkey.len);
   2866    if (dbkey.data == NULL) {
   2867        goto loser;
   2868    }
   2869    PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_VERSION_KEY,
   2870                SEC_DB_VERSION_KEY_LEN);
   2871 
   2872    rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
   2873    if (rv != SECSuccess) {
   2874        goto loser;
   2875    }
   2876 
   2877    PORT_FreeArena(tmparena, PR_FALSE);
   2878    return (entry);
   2879 
   2880 loser:
   2881    if (tmparena) {
   2882        PORT_FreeArena(tmparena, PR_FALSE);
   2883    }
   2884    if (arena) {
   2885        PORT_FreeArena(arena, PR_FALSE);
   2886    }
   2887 
   2888    return (NULL);
   2889 }
   2890 
   2891 /*
   2892 * Encode a version entry into byte stream suitable for
   2893 * the database
   2894 */
   2895 static SECStatus
   2896 WriteDBVersionEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryVersion *entry)
   2897 {
   2898    SECItem dbitem, dbkey;
   2899    PLArenaPool *tmparena = NULL;
   2900    SECStatus rv;
   2901 
   2902    tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   2903    if (tmparena == NULL) {
   2904        goto loser;
   2905    }
   2906 
   2907    /* allocate space for encoded database record, including space
   2908     * for low level header
   2909     */
   2910    dbitem.len = SEC_DB_ENTRY_HEADER_LEN;
   2911 
   2912    dbitem.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbitem.len);
   2913    if (dbitem.data == NULL) {
   2914        PORT_SetError(SEC_ERROR_NO_MEMORY);
   2915        goto loser;
   2916    }
   2917 
   2918    /* now get the database key and format it */
   2919    dbkey.len = SEC_DB_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
   2920    dbkey.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbkey.len);
   2921    if (dbkey.data == NULL) {
   2922        goto loser;
   2923    }
   2924    PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_VERSION_KEY,
   2925                SEC_DB_VERSION_KEY_LEN);
   2926 
   2927    /* now write it to the database */
   2928    rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
   2929    if (rv != SECSuccess) {
   2930        goto loser;
   2931    }
   2932 
   2933    PORT_FreeArena(tmparena, PR_FALSE);
   2934    return (SECSuccess);
   2935 
   2936 loser:
   2937    if (tmparena) {
   2938        PORT_FreeArena(tmparena, PR_FALSE);
   2939    }
   2940    return (SECFailure);
   2941 }
   2942 
   2943 /*
   2944 * cert is no longer a perm cert, but will remain a temp cert
   2945 */
   2946 static SECStatus
   2947 RemovePermSubjectNode(NSSLOWCERTCertificate *cert)
   2948 {
   2949    certDBEntrySubject *entry;
   2950    unsigned int i;
   2951    SECStatus rv;
   2952 
   2953    entry = ReadDBSubjectEntry(cert->dbhandle, &cert->derSubject);
   2954    if (entry == NULL) {
   2955        return (SECFailure);
   2956    }
   2957 
   2958    PORT_Assert(entry->ncerts);
   2959    rv = SECFailure;
   2960 
   2961    if (entry->ncerts > 1) {
   2962        for (i = 0; i < entry->ncerts; i++) {
   2963            if (SECITEM_CompareItem(&entry->certKeys[i], &cert->certKey) ==
   2964                SECEqual) {
   2965                /* copy rest of list forward one entry */
   2966                for (i = i + 1; i < entry->ncerts; i++) {
   2967                    entry->certKeys[i - 1] = entry->certKeys[i];
   2968                    entry->keyIDs[i - 1] = entry->keyIDs[i];
   2969                }
   2970                entry->ncerts--;
   2971                DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
   2972                rv = WriteDBSubjectEntry(cert->dbhandle, entry);
   2973                break;
   2974            }
   2975        }
   2976    } else {
   2977        /* no entries left, delete the perm entry in the DB */
   2978        if (entry->emailAddrs) {
   2979            /* if the subject had an email record, then delete it too */
   2980            for (i = 0; i < entry->nemailAddrs; i++) {
   2981                DeleteDBSMimeEntry(cert->dbhandle, entry->emailAddrs[i]);
   2982            }
   2983        }
   2984        if (entry->nickname) {
   2985            DeleteDBNicknameEntry(cert->dbhandle, entry->nickname);
   2986        }
   2987 
   2988        DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
   2989    }
   2990    DestroyDBEntry((certDBEntry *)entry);
   2991 
   2992    return (rv);
   2993 }
   2994 
   2995 /*
   2996 * add a cert to the perm subject list
   2997 */
   2998 static SECStatus
   2999 AddPermSubjectNode(certDBEntrySubject *entry, NSSLOWCERTCertificate *cert,
   3000                   char *nickname)
   3001 {
   3002    SECItem *newCertKeys, *newKeyIDs;
   3003    unsigned int i, new_i;
   3004    SECStatus rv;
   3005    unsigned int ncerts;
   3006 
   3007    PORT_Assert(entry);
   3008    ncerts = entry->ncerts;
   3009 
   3010    if (nickname && entry->nickname) {
   3011        /* nicknames must be the same */
   3012        PORT_Assert(PORT_Strcmp(nickname, entry->nickname) == 0);
   3013    }
   3014 
   3015    if ((entry->nickname == NULL) && (nickname != NULL)) {
   3016        /* copy nickname into the entry */
   3017        entry->nickname = PORT_ArenaStrdup(entry->common.arena, nickname);
   3018        if (entry->nickname == NULL) {
   3019            return (SECFailure);
   3020        }
   3021    }
   3022 
   3023    /* a DB entry already exists, so add this cert */
   3024    newCertKeys = PORT_ArenaZNewArray(entry->common.arena, SECItem, ncerts + 1);
   3025    newKeyIDs = PORT_ArenaZNewArray(entry->common.arena, SECItem, ncerts + 1);
   3026 
   3027    if ((newCertKeys == NULL) || (newKeyIDs == NULL)) {
   3028        return (SECFailure);
   3029    }
   3030 
   3031    /* Step 1: copy certs older than "cert" into new entry. */
   3032    for (i = 0, new_i = 0; i < ncerts; i++) {
   3033        NSSLOWCERTCertificate *cmpcert;
   3034        PRBool isNewer;
   3035        cmpcert = nsslowcert_FindCertByKey(cert->dbhandle,
   3036                                           &entry->certKeys[i]);
   3037        /* The entry has been corrupted, remove it from the list */
   3038        if (!cmpcert) {
   3039            continue;
   3040        }
   3041 
   3042        isNewer = nsslowcert_IsNewer(cert, cmpcert);
   3043        nsslowcert_DestroyCertificate(cmpcert);
   3044        if (isNewer)
   3045            break;
   3046        /* copy this cert entry */
   3047        newCertKeys[new_i] = entry->certKeys[i];
   3048        newKeyIDs[new_i] = entry->keyIDs[i];
   3049        new_i++;
   3050    }
   3051 
   3052    /* Step 2: Add "cert" to the entry. */
   3053    rv = SECITEM_CopyItem(entry->common.arena, &newCertKeys[new_i],
   3054                          &cert->certKey);
   3055    if (rv != SECSuccess) {
   3056        return (SECFailure);
   3057    }
   3058    rv = SECITEM_CopyItem(entry->common.arena, &newKeyIDs[new_i],
   3059                          &cert->subjectKeyID);
   3060    if (rv != SECSuccess) {
   3061        return (SECFailure);
   3062    }
   3063    new_i++;
   3064 
   3065    /* Step 3: copy remaining certs (if any) from old entry to new. */
   3066    for (; i < ncerts; i++, new_i++) {
   3067        newCertKeys[new_i] = entry->certKeys[i];
   3068        newKeyIDs[new_i] = entry->keyIDs[i];
   3069    }
   3070 
   3071    /* update certKeys and keyIDs */
   3072    entry->certKeys = newCertKeys;
   3073    entry->keyIDs = newKeyIDs;
   3074 
   3075    /* set new count value */
   3076    entry->ncerts = new_i;
   3077 
   3078    DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
   3079    rv = WriteDBSubjectEntry(cert->dbhandle, entry);
   3080    return (rv);
   3081 }
   3082 
   3083 SECStatus
   3084 nsslowcert_TraversePermCertsForSubject(NSSLOWCERTCertDBHandle *handle,
   3085                                       SECItem *derSubject,
   3086                                       NSSLOWCERTCertCallback cb, void *cbarg)
   3087 {
   3088    certDBEntrySubject *entry;
   3089    unsigned int i;
   3090    NSSLOWCERTCertificate *cert;
   3091    SECStatus rv = SECSuccess;
   3092 
   3093    entry = ReadDBSubjectEntry(handle, derSubject);
   3094 
   3095    if (entry == NULL) {
   3096        return (SECFailure);
   3097    }
   3098 
   3099    for (i = 0; i < entry->ncerts; i++) {
   3100        cert = nsslowcert_FindCertByKey(handle, &entry->certKeys[i]);
   3101        if (!cert) {
   3102            continue;
   3103        }
   3104        rv = (*cb)(cert, cbarg);
   3105        nsslowcert_DestroyCertificate(cert);
   3106        if (rv == SECFailure) {
   3107            break;
   3108        }
   3109    }
   3110 
   3111    DestroyDBEntry((certDBEntry *)entry);
   3112 
   3113    return (rv);
   3114 }
   3115 
   3116 int
   3117 nsslowcert_NumPermCertsForSubject(NSSLOWCERTCertDBHandle *handle,
   3118                                  SECItem *derSubject)
   3119 {
   3120    certDBEntrySubject *entry;
   3121    int ret;
   3122 
   3123    entry = ReadDBSubjectEntry(handle, derSubject);
   3124 
   3125    if (entry == NULL) {
   3126        return (SECFailure);
   3127    }
   3128 
   3129    ret = entry->ncerts;
   3130 
   3131    DestroyDBEntry((certDBEntry *)entry);
   3132 
   3133    return (ret);
   3134 }
   3135 
   3136 SECStatus
   3137 nsslowcert_TraversePermCertsForNickname(NSSLOWCERTCertDBHandle *handle,
   3138                                        char *nickname, NSSLOWCERTCertCallback cb, void *cbarg)
   3139 {
   3140    certDBEntryNickname *nnentry = NULL;
   3141    certDBEntrySMime *smentry = NULL;
   3142    SECStatus rv;
   3143    SECItem *derSubject = NULL;
   3144 
   3145    nnentry = ReadDBNicknameEntry(handle, nickname);
   3146    if (nnentry) {
   3147        derSubject = &nnentry->subjectName;
   3148    } else {
   3149        smentry = nsslowcert_ReadDBSMimeEntry(handle, nickname);
   3150        if (smentry) {
   3151            derSubject = &smentry->subjectName;
   3152        }
   3153    }
   3154 
   3155    if (derSubject) {
   3156        rv = nsslowcert_TraversePermCertsForSubject(handle, derSubject,
   3157                                                    cb, cbarg);
   3158    } else {
   3159        rv = SECFailure;
   3160    }
   3161 
   3162    if (nnentry) {
   3163        DestroyDBEntry((certDBEntry *)nnentry);
   3164    }
   3165    if (smentry) {
   3166        DestroyDBEntry((certDBEntry *)smentry);
   3167    }
   3168 
   3169    return (rv);
   3170 }
   3171 
   3172 int
   3173 nsslowcert_NumPermCertsForNickname(NSSLOWCERTCertDBHandle *handle,
   3174                                   char *nickname)
   3175 {
   3176    certDBEntryNickname *entry;
   3177    int ret;
   3178 
   3179    entry = ReadDBNicknameEntry(handle, nickname);
   3180 
   3181    if (entry) {
   3182        ret = nsslowcert_NumPermCertsForSubject(handle, &entry->subjectName);
   3183        DestroyDBEntry((certDBEntry *)entry);
   3184    } else {
   3185        ret = 0;
   3186    }
   3187    return (ret);
   3188 }
   3189 
   3190 /*
   3191 * add a nickname to a cert that doesn't have one
   3192 */
   3193 static SECStatus
   3194 AddNicknameToPermCert(NSSLOWCERTCertDBHandle *dbhandle,
   3195                      NSSLOWCERTCertificate *cert, char *nickname)
   3196 {
   3197    certDBEntryCert *entry;
   3198    int rv;
   3199 
   3200    entry = cert->dbEntry;
   3201    PORT_Assert(entry != NULL);
   3202    if (entry == NULL) {
   3203        goto loser;
   3204    }
   3205 
   3206    pkcs11_freeNickname(entry->nickname, entry->nicknameSpace);
   3207    entry->nickname = NULL;
   3208    entry->nickname = pkcs11_copyNickname(nickname, entry->nicknameSpace,
   3209                                          sizeof(entry->nicknameSpace));
   3210 
   3211    rv = WriteDBCertEntry(dbhandle, entry);
   3212    if (rv) {
   3213        goto loser;
   3214    }
   3215 
   3216    pkcs11_freeNickname(cert->nickname, cert->nicknameSpace);
   3217    cert->nickname = NULL;
   3218    cert->nickname = pkcs11_copyNickname(nickname, cert->nicknameSpace,
   3219                                         sizeof(cert->nicknameSpace));
   3220 
   3221    return (SECSuccess);
   3222 
   3223 loser:
   3224    return (SECFailure);
   3225 }
   3226 
   3227 /*
   3228 * add a nickname to a cert that is already in the perm database, but doesn't
   3229 * have one yet (it is probably an e-mail cert).
   3230 */
   3231 SECStatus
   3232 nsslowcert_AddPermNickname(NSSLOWCERTCertDBHandle *dbhandle,
   3233                           NSSLOWCERTCertificate *cert, char *nickname)
   3234 {
   3235    SECStatus rv = SECFailure;
   3236    certDBEntrySubject *entry = NULL;
   3237    certDBEntryNickname *nicknameEntry = NULL;
   3238 
   3239    nsslowcert_LockDB(dbhandle);
   3240 
   3241    entry = ReadDBSubjectEntry(dbhandle, &cert->derSubject);
   3242    if (entry == NULL)
   3243        goto loser;
   3244 
   3245    if (entry->nickname == NULL) {
   3246 
   3247        /* no nickname for subject */
   3248        rv = AddNicknameToSubject(dbhandle, cert, nickname);
   3249        if (rv != SECSuccess) {
   3250            goto loser;
   3251        }
   3252        rv = AddNicknameToPermCert(dbhandle, cert, nickname);
   3253        if (rv != SECSuccess) {
   3254            goto loser;
   3255        }
   3256        nicknameEntry = NewDBNicknameEntry(nickname, &cert->derSubject, 0);
   3257        if (nicknameEntry == NULL) {
   3258            goto loser;
   3259        }
   3260 
   3261        rv = WriteDBNicknameEntry(dbhandle, nicknameEntry);
   3262        if (rv != SECSuccess) {
   3263            goto loser;
   3264        }
   3265    } else {
   3266        /* subject already has a nickname */
   3267        rv = AddNicknameToPermCert(dbhandle, cert, entry->nickname);
   3268        if (rv != SECSuccess) {
   3269            goto loser;
   3270        }
   3271        /* make sure nickname entry exists. If the database was corrupted,
   3272         * we may have lost the nickname entry. Add it back now  */
   3273        nicknameEntry = ReadDBNicknameEntry(dbhandle, entry->nickname);
   3274        if (nicknameEntry == NULL) {
   3275            nicknameEntry = NewDBNicknameEntry(entry->nickname,
   3276                                               &cert->derSubject, 0);
   3277            if (nicknameEntry == NULL) {
   3278                goto loser;
   3279            }
   3280 
   3281            rv = WriteDBNicknameEntry(dbhandle, nicknameEntry);
   3282            if (rv != SECSuccess) {
   3283                goto loser;
   3284            }
   3285        }
   3286    }
   3287    rv = SECSuccess;
   3288 
   3289 loser:
   3290    if (entry) {
   3291        DestroyDBEntry((certDBEntry *)entry);
   3292    }
   3293    if (nicknameEntry) {
   3294        DestroyDBEntry((certDBEntry *)nicknameEntry);
   3295    }
   3296    nsslowcert_UnlockDB(dbhandle);
   3297    return (rv);
   3298 }
   3299 
   3300 static certDBEntryCert *
   3301 AddCertToPermDB(NSSLOWCERTCertDBHandle *handle, NSSLOWCERTCertificate *cert,
   3302                char *nickname, NSSLOWCERTCertTrust *trust)
   3303 {
   3304    certDBEntryCert *certEntry = NULL;
   3305    certDBEntryNickname *nicknameEntry = NULL;
   3306    certDBEntrySubject *subjectEntry = NULL;
   3307    int state = 0;
   3308    SECStatus rv;
   3309    PRBool donnentry = PR_FALSE;
   3310 
   3311    if (nickname) {
   3312        donnentry = PR_TRUE;
   3313    }
   3314 
   3315    subjectEntry = ReadDBSubjectEntry(handle, &cert->derSubject);
   3316 
   3317    if (subjectEntry && subjectEntry->nickname) {
   3318        donnentry = PR_FALSE;
   3319        nickname = subjectEntry->nickname;
   3320    }
   3321 
   3322    certEntry = NewDBCertEntry(&cert->derCert, nickname, trust, 0);
   3323    if (certEntry == NULL) {
   3324        goto loser;
   3325    }
   3326 
   3327    if (donnentry) {
   3328        nicknameEntry = NewDBNicknameEntry(nickname, &cert->derSubject, 0);
   3329        if (nicknameEntry == NULL) {
   3330            goto loser;
   3331        }
   3332    }
   3333 
   3334    rv = WriteDBCertEntry(handle, certEntry);
   3335    if (rv != SECSuccess) {
   3336        goto loser;
   3337    }
   3338    state = 1;
   3339 
   3340    if (nicknameEntry) {
   3341        rv = WriteDBNicknameEntry(handle, nicknameEntry);
   3342        if (rv != SECSuccess) {
   3343            goto loser;
   3344        }
   3345    }
   3346 
   3347    state = 2;
   3348 
   3349    /* "Change" handles if necessary */
   3350    cert->dbhandle = handle;
   3351 
   3352    /* add to or create new subject entry */
   3353    if (subjectEntry) {
   3354        /* REWRITE BASED ON SUBJECT ENTRY */
   3355        rv = AddPermSubjectNode(subjectEntry, cert, nickname);
   3356        if (rv != SECSuccess) {
   3357            goto loser;
   3358        }
   3359    } else {
   3360        /* make a new subject entry - this case is only used when updating
   3361         * an old version of the database.  This is OK because the oldnickname
   3362         * db format didn't allow multiple certs with the same subject.
   3363         */
   3364        /* where does subjectKeyID and certKey come from? */
   3365        subjectEntry = NewDBSubjectEntry(&cert->derSubject, &cert->certKey,
   3366                                         &cert->subjectKeyID, nickname,
   3367                                         NULL, 0);
   3368        if (subjectEntry == NULL) {
   3369            goto loser;
   3370        }
   3371        rv = WriteDBSubjectEntry(handle, subjectEntry);
   3372        if (rv != SECSuccess) {
   3373            goto loser;
   3374        }
   3375    }
   3376 
   3377    state = 3;
   3378 
   3379    if (nicknameEntry) {
   3380        DestroyDBEntry((certDBEntry *)nicknameEntry);
   3381    }
   3382 
   3383    if (subjectEntry) {
   3384        DestroyDBEntry((certDBEntry *)subjectEntry);
   3385    }
   3386 
   3387    return (certEntry);
   3388 
   3389 loser:
   3390    /* don't leave partial entry in the database */
   3391    if (state > 0) {
   3392        DeleteDBCertEntry(handle, &cert->certKey);
   3393    }
   3394    if ((state > 1) && donnentry) {
   3395        DeleteDBNicknameEntry(handle, nickname);
   3396    }
   3397    if (certEntry) {
   3398        DestroyDBEntry((certDBEntry *)certEntry);
   3399    }
   3400    if (nicknameEntry) {
   3401        DestroyDBEntry((certDBEntry *)nicknameEntry);
   3402    }
   3403    if (subjectEntry) {
   3404        DestroyDBEntry((certDBEntry *)subjectEntry);
   3405    }
   3406 
   3407    return (NULL);
   3408 }
   3409 
   3410 /* forward declaration */
   3411 static SECStatus
   3412 UpdateV7DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb);
   3413 
   3414 /*
   3415 * version 8 uses the same schema as version 7. The only differences are
   3416 * 1) version 8 db uses the blob shim to store data entries > 32k.
   3417 * 2) version 8 db sets the db block size to 32k.
   3418 * both of these are dealt with by the handle.
   3419 */
   3420 
   3421 static SECStatus
   3422 UpdateV8DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
   3423 {
   3424    return UpdateV7DB(handle, updatedb);
   3425 }
   3426 
   3427 /*
   3428 * we could just blindly sequence through reading key data pairs and writing
   3429 * them back out, but some cert.db's have gotten quite large and may have some
   3430 * subtle corruption problems, so instead we cycle through the certs and
   3431 * CRL's and S/MIME profiles and rebuild our subject lists from those records.
   3432 */
   3433 static SECStatus
   3434 UpdateV7DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
   3435 {
   3436    DBT key, data;
   3437    int ret;
   3438    NSSLOWCERTCertificate *cert;
   3439    PRBool isKRL = PR_FALSE;
   3440    certDBEntryType entryType;
   3441    SECItem dbEntry, dbKey;
   3442    certDBEntryRevocation crlEntry;
   3443    certDBEntryCert certEntry;
   3444    certDBEntrySMime smimeEntry;
   3445    SECStatus rv;
   3446 
   3447    ret = (*updatedb->seq)(updatedb, &key, &data, R_FIRST);
   3448 
   3449    if (ret) {
   3450        return (SECFailure);
   3451    }
   3452 
   3453    do {
   3454        unsigned char *dataBuf = (unsigned char *)data.data;
   3455        unsigned char *keyBuf = (unsigned char *)key.data;
   3456        dbEntry.data = &dataBuf[SEC_DB_ENTRY_HEADER_LEN];
   3457        dbEntry.len = data.size - SEC_DB_ENTRY_HEADER_LEN;
   3458        entryType = (certDBEntryType)keyBuf[0];
   3459        dbKey.data = &keyBuf[SEC_DB_KEY_HEADER_LEN];
   3460        dbKey.len = key.size - SEC_DB_KEY_HEADER_LEN;
   3461        if ((dbEntry.len <= 0) || (dbKey.len <= 0)) {
   3462            continue;
   3463        }
   3464 
   3465        switch (entryType) {
   3466            /* these entries will get regenerated as we read the
   3467             * rest of the data from the database */
   3468            case certDBEntryTypeVersion:
   3469            case certDBEntryTypeSubject:
   3470            case certDBEntryTypeContentVersion:
   3471            case certDBEntryTypeNickname:
   3472            /* smime profiles need entries created after the certs have
   3473             * been imported, loop over them in a second run */
   3474            case certDBEntryTypeSMimeProfile:
   3475                break;
   3476 
   3477            case certDBEntryTypeCert:
   3478                /* decode Entry */
   3479                certEntry.common.version = (unsigned int)dataBuf[0];
   3480                certEntry.common.type = entryType;
   3481                certEntry.common.flags = (unsigned int)dataBuf[2];
   3482                rv = DecodeDBCertEntry(&certEntry, &dbEntry);
   3483                if (rv != SECSuccess) {
   3484                    break;
   3485                }
   3486                /* should we check for existing duplicates? */
   3487                cert = nsslowcert_DecodeDERCertificate(&certEntry.derCert,
   3488                                                       certEntry.nickname);
   3489                if (cert) {
   3490                    nsslowcert_UpdatePermCert(handle, cert, certEntry.nickname,
   3491                                              &certEntry.trust);
   3492                    nsslowcert_DestroyCertificate(cert);
   3493                }
   3494                /* free any data the decode may have allocated. */
   3495                pkcs11_freeStaticData(certEntry.derCert.data,
   3496                                      certEntry.derCertSpace);
   3497                pkcs11_freeNickname(certEntry.nickname, certEntry.nicknameSpace);
   3498                break;
   3499 
   3500            case certDBEntryTypeKeyRevocation:
   3501                isKRL = PR_TRUE;
   3502            /* fall through */
   3503            case certDBEntryTypeRevocation:
   3504                crlEntry.common.version = (unsigned int)dataBuf[0];
   3505                crlEntry.common.type = entryType;
   3506                crlEntry.common.flags = (unsigned int)dataBuf[2];
   3507                crlEntry.common.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   3508                if (crlEntry.common.arena == NULL) {
   3509                    break;
   3510                }
   3511                rv = DecodeDBCrlEntry(&crlEntry, &dbEntry);
   3512                if (rv != SECSuccess) {
   3513                    break;
   3514                }
   3515                nsslowcert_UpdateCrl(handle, &crlEntry.derCrl, &dbKey,
   3516                                     crlEntry.url, isKRL);
   3517                /* free data allocated by the decode */
   3518                PORT_FreeArena(crlEntry.common.arena, PR_FALSE);
   3519                crlEntry.common.arena = NULL;
   3520                break;
   3521 
   3522            default:
   3523                break;
   3524        }
   3525    } while ((*updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0);
   3526 
   3527    /* now loop again updating just the SMimeProfile. */
   3528    ret = (*updatedb->seq)(updatedb, &key, &data, R_FIRST);
   3529 
   3530    if (ret) {
   3531        return (SECFailure);
   3532    }
   3533 
   3534    do {
   3535        unsigned char *dataBuf = (unsigned char *)data.data;
   3536        unsigned char *keyBuf = (unsigned char *)key.data;
   3537        dbEntry.data = &dataBuf[SEC_DB_ENTRY_HEADER_LEN];
   3538        dbEntry.len = data.size - SEC_DB_ENTRY_HEADER_LEN;
   3539        entryType = (certDBEntryType)keyBuf[0];
   3540        if (entryType != certDBEntryTypeSMimeProfile) {
   3541            continue;
   3542        }
   3543        dbKey.data = &keyBuf[SEC_DB_KEY_HEADER_LEN];
   3544        dbKey.len = key.size - SEC_DB_KEY_HEADER_LEN;
   3545        if ((dbEntry.len <= 0) || (dbKey.len <= 0)) {
   3546            continue;
   3547        }
   3548        smimeEntry.common.version = (unsigned int)dataBuf[0];
   3549        smimeEntry.common.type = entryType;
   3550        smimeEntry.common.flags = (unsigned int)dataBuf[2];
   3551        smimeEntry.common.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   3552        /* decode entry */
   3553        rv = DecodeDBSMimeEntry(&smimeEntry, &dbEntry, (char *)dbKey.data);
   3554        if (rv == SECSuccess) {
   3555            nsslowcert_UpdateSMimeProfile(handle, smimeEntry.emailAddr,
   3556                                          &smimeEntry.subjectName, &smimeEntry.smimeOptions,
   3557                                          &smimeEntry.optionsDate);
   3558        }
   3559        PORT_FreeArena(smimeEntry.common.arena, PR_FALSE);
   3560        smimeEntry.common.arena = NULL;
   3561    } while ((*updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0);
   3562 
   3563    (*updatedb->close)(updatedb);
   3564 
   3565    /* a database update is a good time to go back and verify the integrity of
   3566     * the keys and certs */
   3567    handle->dbVerify = PR_TRUE;
   3568    return (SECSuccess);
   3569 }
   3570 
   3571 /*
   3572 * NOTE - Version 6 DB did not go out to the real world in a release,
   3573 * so we can remove this function in a later release.
   3574 */
   3575 static SECStatus
   3576 UpdateV6DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
   3577 {
   3578    int ret;
   3579    DBT key, data;
   3580    unsigned char *buf, *tmpbuf = NULL;
   3581    certDBEntryType type;
   3582    certDBEntryNickname *nnEntry = NULL;
   3583    certDBEntrySubject *subjectEntry = NULL;
   3584    certDBEntrySMime *emailEntry = NULL;
   3585    char *nickname;
   3586    char *emailAddr;
   3587 
   3588    /*
   3589     * Sequence through the old database and copy all of the entries
   3590     * to the new database.  Subject name entries will have the new
   3591     * fields inserted into them (with zero length).
   3592     */
   3593    ret = (*updatedb->seq)(updatedb, &key, &data, R_FIRST);
   3594    if (ret) {
   3595        return (SECFailure);
   3596    }
   3597 
   3598    do {
   3599        buf = (unsigned char *)data.data;
   3600 
   3601        if (data.size >= 3) {
   3602            if (buf[0] == 6) { /* version number */
   3603                type = (certDBEntryType)buf[1];
   3604                if (type == certDBEntryTypeSubject) {
   3605                    /* expando subjecto entrieo */
   3606                    tmpbuf = (unsigned char *)PORT_Alloc(data.size + 4);
   3607                    if (tmpbuf) {
   3608                        /* copy header stuff */
   3609                        PORT_Memcpy(tmpbuf, buf, SEC_DB_ENTRY_HEADER_LEN + 2);
   3610                        /* insert 4 more bytes of zero'd header */
   3611                        PORT_Memset(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 2],
   3612                                    0, 4);
   3613                        /* copy rest of the data */
   3614                        PORT_Memcpy(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 6],
   3615                                    &buf[SEC_DB_ENTRY_HEADER_LEN + 2],
   3616                                    data.size - (SEC_DB_ENTRY_HEADER_LEN + 2));
   3617 
   3618                        data.data = (void *)tmpbuf;
   3619                        data.size += 4;
   3620                        buf = tmpbuf;
   3621                    }
   3622                } else if (type == certDBEntryTypeCert) {
   3623                    /* expando certo entrieo */
   3624                    tmpbuf = (unsigned char *)PORT_Alloc(data.size + 3);
   3625                    if (tmpbuf) {
   3626                        /* copy header stuff */
   3627                        PORT_Memcpy(tmpbuf, buf, SEC_DB_ENTRY_HEADER_LEN);
   3628 
   3629                        /* copy trust flage, setting msb's to 0 */
   3630                        tmpbuf[SEC_DB_ENTRY_HEADER_LEN] = 0;
   3631                        tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 1] =
   3632                            buf[SEC_DB_ENTRY_HEADER_LEN];
   3633                        tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 2] = 0;
   3634                        tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 3] =
   3635                            buf[SEC_DB_ENTRY_HEADER_LEN + 1];
   3636                        tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 4] = 0;
   3637                        tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 5] =
   3638                            buf[SEC_DB_ENTRY_HEADER_LEN + 2];
   3639 
   3640                        /* copy rest of the data */
   3641                        PORT_Memcpy(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 6],
   3642                                    &buf[SEC_DB_ENTRY_HEADER_LEN + 3],
   3643                                    data.size - (SEC_DB_ENTRY_HEADER_LEN + 3));
   3644 
   3645                        data.data = (void *)tmpbuf;
   3646                        data.size += 3;
   3647                        buf = tmpbuf;
   3648                    }
   3649                }
   3650 
   3651                /* update the record version number */
   3652                buf[0] = CERT_DB_FILE_VERSION;
   3653 
   3654                /* copy to the new database */
   3655                ret = certdb_Put(handle->permCertDB, &key, &data, 0);
   3656                if (tmpbuf) {
   3657                    PORT_Free(tmpbuf);
   3658                    tmpbuf = NULL;
   3659                }
   3660                if (ret) {
   3661                    return SECFailure;
   3662                }
   3663            }
   3664        }
   3665    } while ((*updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0);
   3666 
   3667    ret = certdb_Sync(handle->permCertDB, 0);
   3668    if (ret) {
   3669        return SECFailure;
   3670    }
   3671 
   3672    ret = (*updatedb->seq)(updatedb, &key, &data, R_FIRST);
   3673    if (ret) {
   3674        return (SECFailure);
   3675    }
   3676 
   3677    do {
   3678        buf = (unsigned char *)data.data;
   3679 
   3680        if (data.size >= 3) {
   3681            if (buf[0] == CERT_DB_FILE_VERSION) { /* version number */
   3682                type = (certDBEntryType)buf[1];
   3683                if (type == certDBEntryTypeNickname) {
   3684                    nickname = &((char *)key.data)[1];
   3685 
   3686                    /* get the matching nickname entry in the new DB */
   3687                    nnEntry = ReadDBNicknameEntry(handle, nickname);
   3688                    if (nnEntry == NULL) {
   3689                        goto endloop;
   3690                    }
   3691 
   3692                    /* find the subject entry pointed to by nickname */
   3693                    subjectEntry = ReadDBSubjectEntry(handle,
   3694                                                      &nnEntry->subjectName);
   3695                    if (subjectEntry == NULL) {
   3696                        goto endloop;
   3697                    }
   3698 
   3699                    subjectEntry->nickname =
   3700                        (char *)PORT_ArenaAlloc(subjectEntry->common.arena,
   3701                                                key.size - 1);
   3702                    if (subjectEntry->nickname) {
   3703                        PORT_Memcpy(subjectEntry->nickname, nickname,
   3704                                    key.size - 1);
   3705                        (void)WriteDBSubjectEntry(handle, subjectEntry);
   3706                    }
   3707                } else if (type == certDBEntryTypeSMimeProfile) {
   3708                    emailAddr = &((char *)key.data)[1];
   3709 
   3710                    /* get the matching smime entry in the new DB */
   3711                    emailEntry = nsslowcert_ReadDBSMimeEntry(handle, emailAddr);
   3712                    if (emailEntry == NULL) {
   3713                        goto endloop;
   3714                    }
   3715 
   3716                    /* find the subject entry pointed to by nickname */
   3717                    subjectEntry = ReadDBSubjectEntry(handle,
   3718                                                      &emailEntry->subjectName);
   3719                    if (subjectEntry == NULL) {
   3720                        goto endloop;
   3721                    }
   3722 
   3723                    subjectEntry->emailAddrs = (char **)
   3724                        PORT_ArenaAlloc(subjectEntry->common.arena,
   3725                                        sizeof(char *));
   3726                    if (subjectEntry->emailAddrs) {
   3727                        subjectEntry->emailAddrs[0] =
   3728                            (char *)PORT_ArenaAlloc(subjectEntry->common.arena,
   3729                                                    key.size - 1);
   3730                        if (subjectEntry->emailAddrs[0]) {
   3731                            PORT_Memcpy(subjectEntry->emailAddrs[0], emailAddr,
   3732                                        key.size - 1);
   3733                            subjectEntry->nemailAddrs = 1;
   3734                            (void)WriteDBSubjectEntry(handle, subjectEntry);
   3735                        }
   3736                    }
   3737                }
   3738 
   3739            endloop:
   3740                if (subjectEntry) {
   3741                    DestroyDBEntry((certDBEntry *)subjectEntry);
   3742                    subjectEntry = NULL;
   3743                }
   3744                if (nnEntry) {
   3745                    DestroyDBEntry((certDBEntry *)nnEntry);
   3746                    nnEntry = NULL;
   3747                }
   3748                if (emailEntry) {
   3749                    DestroyDBEntry((certDBEntry *)emailEntry);
   3750                    emailEntry = NULL;
   3751                }
   3752            }
   3753        }
   3754    } while ((*updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0);
   3755 
   3756    ret = certdb_Sync(handle->permCertDB, 0);
   3757    if (ret) {
   3758        return SECFailure;
   3759    }
   3760 
   3761    (*updatedb->close)(updatedb);
   3762    return (SECSuccess);
   3763 }
   3764 
   3765 static SECStatus
   3766 updateV5Callback(NSSLOWCERTCertificate *cert, SECItem *k, void *pdata)
   3767 {
   3768    NSSLOWCERTCertDBHandle *handle;
   3769    certDBEntryCert *entry;
   3770    NSSLOWCERTCertTrust *trust;
   3771 
   3772    handle = (NSSLOWCERTCertDBHandle *)pdata;
   3773    trust = &cert->dbEntry->trust;
   3774 
   3775    /* SSL user certs can be used for email if they have an email addr */
   3776    if (cert->emailAddr && (trust->sslFlags & CERTDB_USER) &&
   3777        (trust->emailFlags == 0)) {
   3778        trust->emailFlags = CERTDB_USER;
   3779    }
   3780    /* servers didn't set the user flags on the server cert.. */
   3781    if (PORT_Strcmp(cert->dbEntry->nickname, "Server-Cert") == 0) {
   3782        trust->sslFlags |= CERTDB_USER;
   3783    }
   3784 
   3785    entry = AddCertToPermDB(handle, cert, cert->dbEntry->nickname,
   3786                            &cert->dbEntry->trust);
   3787    if (entry) {
   3788        DestroyDBEntry((certDBEntry *)entry);
   3789    }
   3790 
   3791    return (SECSuccess);
   3792 }
   3793 
   3794 static SECStatus
   3795 UpdateV5DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
   3796 {
   3797    NSSLOWCERTCertDBHandle updatehandle;
   3798 
   3799    updatehandle.permCertDB = updatedb;
   3800    updatehandle.dbMon = PZ_NewMonitor(nssILockCertDB);
   3801    updatehandle.dbVerify = 0;
   3802    updatehandle.ref = 1; /* prevent premature close */
   3803 
   3804    (void)nsslowcert_TraversePermCerts(&updatehandle, updateV5Callback,
   3805                                       (void *)handle);
   3806 
   3807    PZ_DestroyMonitor(updatehandle.dbMon);
   3808 
   3809    (*updatedb->close)(updatedb);
   3810    return (SECSuccess);
   3811 }
   3812 
   3813 static PRBool
   3814 isV4DB(DB *db)
   3815 {
   3816    DBT key, data;
   3817    int ret;
   3818 
   3819    key.data = "Version";
   3820    key.size = 7;
   3821 
   3822    ret = (*db->get)(db, &key, &data, 0);
   3823    if (ret) {
   3824        return PR_FALSE;
   3825    }
   3826 
   3827    if ((data.size == 1) && (*(unsigned char *)data.data <= 4)) {
   3828        return PR_TRUE;
   3829    }
   3830 
   3831    return PR_FALSE;
   3832 }
   3833 
   3834 static SECStatus
   3835 UpdateV4DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
   3836 {
   3837    DBT key, data;
   3838    certDBEntryCert *entry, *entry2;
   3839    int ret;
   3840    NSSLOWCERTCertificate *cert;
   3841 
   3842    ret = (*updatedb->seq)(updatedb, &key, &data, R_FIRST);
   3843 
   3844    if (ret) {
   3845        return (SECFailure);
   3846    }
   3847 
   3848    do {
   3849        if (data.size != 1) { /* skip version number */
   3850 
   3851            /* decode the old DB entry */
   3852            entry = (certDBEntryCert *)
   3853                DecodeV4DBCertEntry((unsigned char *)data.data, data.size);
   3854 
   3855            if (entry) {
   3856                cert = nsslowcert_DecodeDERCertificate(&entry->derCert,
   3857                                                       entry->nickname);
   3858 
   3859                if (cert != NULL) {
   3860                    /* add to new database */
   3861                    entry2 = AddCertToPermDB(handle, cert, entry->nickname,
   3862                                             &entry->trust);
   3863 
   3864                    nsslowcert_DestroyCertificate(cert);
   3865                    if (entry2) {
   3866                        DestroyDBEntry((certDBEntry *)entry2);
   3867                    }
   3868                }
   3869                DestroyDBEntry((certDBEntry *)entry);
   3870            }
   3871        }
   3872    } while ((*updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0);
   3873 
   3874    (*updatedb->close)(updatedb);
   3875    return (SECSuccess);
   3876 }
   3877 
   3878 /*
   3879 * return true if a database key conflict exists
   3880 */
   3881 PRBool
   3882 nsslowcert_CertDBKeyConflict(SECItem *derCert, NSSLOWCERTCertDBHandle *handle)
   3883 {
   3884    SECStatus rv;
   3885    DBT tmpdata;
   3886    DBT namekey;
   3887    int ret;
   3888    SECItem keyitem;
   3889    PLArenaPool *arena = NULL;
   3890    SECItem derKey;
   3891 
   3892    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   3893    if (arena == NULL) {
   3894        goto loser;
   3895    }
   3896 
   3897    /* get the db key of the cert */
   3898    rv = nsslowcert_KeyFromDERCert(arena, derCert, &derKey);
   3899    if (rv != SECSuccess) {
   3900        goto loser;
   3901    }
   3902 
   3903    rv = EncodeDBCertKey(&derKey, arena, &keyitem);
   3904    if (rv != SECSuccess) {
   3905        goto loser;
   3906    }
   3907 
   3908    namekey.data = keyitem.data;
   3909    namekey.size = keyitem.len;
   3910 
   3911    ret = certdb_Get(handle->permCertDB, &namekey, &tmpdata, 0);
   3912    if (ret == 0) {
   3913        goto loser;
   3914    }
   3915 
   3916    PORT_FreeArena(arena, PR_FALSE);
   3917 
   3918    return (PR_FALSE);
   3919 loser:
   3920    if (arena) {
   3921        PORT_FreeArena(arena, PR_FALSE);
   3922    }
   3923 
   3924    return (PR_TRUE);
   3925 }
   3926 
   3927 /*
   3928 * return true if a nickname conflict exists
   3929 * NOTE: caller must have already made sure that this exact cert
   3930 * doesn't exist in the DB
   3931 */
   3932 static PRBool
   3933 nsslowcert_CertNicknameConflict(char *nickname, SECItem *derSubject,
   3934                                NSSLOWCERTCertDBHandle *handle)
   3935 {
   3936    PRBool rv;
   3937    certDBEntryNickname *entry;
   3938 
   3939    if (nickname == NULL) {
   3940        return (PR_FALSE);
   3941    }
   3942 
   3943    entry = ReadDBNicknameEntry(handle, nickname);
   3944 
   3945    if (entry == NULL) {
   3946        /* no entry for this nickname, so no conflict */
   3947        return (PR_FALSE);
   3948    }
   3949 
   3950    rv = PR_TRUE;
   3951    if (SECITEM_CompareItem(derSubject, &entry->subjectName) == SECEqual) {
   3952        /* if subject names are the same, then no conflict */
   3953        rv = PR_FALSE;
   3954    }
   3955 
   3956    DestroyDBEntry((certDBEntry *)entry);
   3957    return (rv);
   3958 }
   3959 
   3960 #ifdef DBM_USING_NSPR
   3961 #define NO_RDONLY PR_RDONLY
   3962 #define NO_RDWR PR_RDWR
   3963 #define NO_CREATE (PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE)
   3964 #else
   3965 #define NO_RDONLY O_RDONLY
   3966 #define NO_RDWR O_RDWR
   3967 #define NO_CREATE (O_RDWR | O_CREAT | O_TRUNC)
   3968 #endif
   3969 
   3970 /*
   3971 * open an old database that needs to be updated
   3972 */
   3973 static DB *
   3974 nsslowcert_openolddb(NSSLOWCERTDBNameFunc namecb, void *cbarg, int version)
   3975 {
   3976    char *tmpname;
   3977    DB *updatedb = NULL;
   3978 
   3979    tmpname = (*namecb)(cbarg, version); /* get v6 db name */
   3980    if (tmpname) {
   3981        updatedb = dbopen(tmpname, NO_RDONLY, 0600, DB_HASH, 0);
   3982        PORT_Free(tmpname);
   3983    }
   3984    return updatedb;
   3985 }
   3986 
   3987 static SECStatus
   3988 openNewCertDB(const char *appName, const char *prefix, const char *certdbname,
   3989              NSSLOWCERTCertDBHandle *handle, NSSLOWCERTDBNameFunc namecb, void *cbarg)
   3990 {
   3991    SECStatus rv;
   3992    certDBEntryVersion *versionEntry = NULL;
   3993    DB *updatedb = NULL;
   3994    int status = RDB_FAIL;
   3995 
   3996    if (appName) {
   3997        handle->permCertDB = rdbopen(appName, prefix, "cert", NO_CREATE, &status);
   3998    } else {
   3999        handle->permCertDB = dbsopen(certdbname, NO_CREATE, 0600, DB_HASH, 0);
   4000    }
   4001 
   4002    /* if create fails then we lose */
   4003    if (handle->permCertDB == 0) {
   4004        return status == RDB_RETRY ? SECWouldBlock : SECFailure;
   4005    }
   4006 
   4007    /* Verify version number; */
   4008    versionEntry = NewDBVersionEntry(0);
   4009    if (versionEntry == NULL) {
   4010        rv = SECFailure;
   4011        goto loser;
   4012    }
   4013 
   4014    rv = WriteDBVersionEntry(handle, versionEntry);
   4015 
   4016    DestroyDBEntry((certDBEntry *)versionEntry);
   4017 
   4018    if (rv != SECSuccess) {
   4019        goto loser;
   4020    }
   4021 
   4022    /* rv must already be Success here because of previous if statement */
   4023    /* try to upgrade old db here */
   4024    if (appName &&
   4025        (updatedb = dbsopen(certdbname, NO_RDONLY, 0600, DB_HASH, 0)) != NULL) {
   4026        rv = UpdateV8DB(handle, updatedb);
   4027    } else if ((updatedb = nsslowcert_openolddb(namecb, cbarg, 7)) != NULL) {
   4028        rv = UpdateV7DB(handle, updatedb);
   4029    } else if ((updatedb = nsslowcert_openolddb(namecb, cbarg, 6)) != NULL) {
   4030        rv = UpdateV6DB(handle, updatedb);
   4031    } else if ((updatedb = nsslowcert_openolddb(namecb, cbarg, 5)) != NULL) {
   4032        rv = UpdateV5DB(handle, updatedb);
   4033    } else if ((updatedb = nsslowcert_openolddb(namecb, cbarg, 4)) != NULL) {
   4034        /* NES has v5 format db's with v4 db names! */
   4035        if (isV4DB(updatedb)) {
   4036            rv = UpdateV4DB(handle, updatedb);
   4037        } else {
   4038            rv = UpdateV5DB(handle, updatedb);
   4039        }
   4040    }
   4041 
   4042 loser:
   4043    db_InitComplete(handle->permCertDB);
   4044    return rv;
   4045 }
   4046 
   4047 static int
   4048 nsslowcert_GetVersionNumber(NSSLOWCERTCertDBHandle *handle)
   4049 {
   4050    certDBEntryVersion *versionEntry = NULL;
   4051    int version = 0;
   4052 
   4053    versionEntry = ReadDBVersionEntry(handle);
   4054    if (versionEntry == NULL) {
   4055        return 0;
   4056    }
   4057    version = versionEntry->common.version;
   4058    DestroyDBEntry((certDBEntry *)versionEntry);
   4059    return version;
   4060 }
   4061 
   4062 /*
   4063 * Open the certificate database and index databases.  Create them if
   4064 * they are not there or bad.
   4065 */
   4066 static SECStatus
   4067 nsslowcert_OpenPermCertDB(NSSLOWCERTCertDBHandle *handle, PRBool readOnly,
   4068                          const char *appName, const char *prefix,
   4069                          NSSLOWCERTDBNameFunc namecb, void *cbarg)
   4070 {
   4071    SECStatus rv;
   4072    int openflags;
   4073    char *certdbname;
   4074    int version = 0;
   4075 
   4076    certdbname = (*namecb)(cbarg, CERT_DB_FILE_VERSION);
   4077    if (certdbname == NULL) {
   4078        return (SECFailure);
   4079    }
   4080 
   4081    openflags = readOnly ? NO_RDONLY : NO_RDWR;
   4082 
   4083    /*
   4084     * first open the permanent file based database.
   4085     */
   4086    if (appName) {
   4087        handle->permCertDB = rdbopen(appName, prefix, "cert", openflags, NULL);
   4088    } else {
   4089        handle->permCertDB = dbsopen(certdbname, openflags, 0600, DB_HASH, 0);
   4090    }
   4091 
   4092    /* check for correct version number */
   4093    if (handle->permCertDB) {
   4094        version = nsslowcert_GetVersionNumber(handle);
   4095        if ((version != CERT_DB_FILE_VERSION) &&
   4096            !(appName && version == CERT_DB_V7_FILE_VERSION)) {
   4097            goto loser;
   4098        }
   4099    } else if (readOnly) {
   4100        /* don't create if readonly */
   4101        /* Try openning a version 7 database */
   4102        handle->permCertDB = nsslowcert_openolddb(namecb, cbarg, 7);
   4103        if (!handle->permCertDB) {
   4104            goto loser;
   4105        }
   4106        if (nsslowcert_GetVersionNumber(handle) != 7) {
   4107            goto loser;
   4108        }
   4109    } else {
   4110        /* if first open fails, try to create a new DB */
   4111        rv = openNewCertDB(appName, prefix, certdbname, handle, namecb, cbarg);
   4112        if (rv == SECWouldBlock) {
   4113            /* only the rdb version can fail with wouldblock */
   4114            handle->permCertDB =
   4115                rdbopen(appName, prefix, "cert", openflags, NULL);
   4116 
   4117            /* check for correct version number */
   4118            if (!handle->permCertDB) {
   4119                goto loser;
   4120            }
   4121            version = nsslowcert_GetVersionNumber(handle);
   4122            if ((version != CERT_DB_FILE_VERSION) &&
   4123                !(appName && version == CERT_DB_V7_FILE_VERSION)) {
   4124                goto loser;
   4125            }
   4126        } else if (rv != SECSuccess) {
   4127            goto loser;
   4128        }
   4129    }
   4130 
   4131    PORT_Free(certdbname);
   4132 
   4133    return (SECSuccess);
   4134 
   4135 loser:
   4136 
   4137    PORT_SetError(SEC_ERROR_BAD_DATABASE);
   4138 
   4139    if (handle->permCertDB) {
   4140        certdb_Close(handle->permCertDB);
   4141        handle->permCertDB = 0;
   4142    }
   4143 
   4144    PORT_Free(certdbname);
   4145 
   4146    return (SECFailure);
   4147 }
   4148 
   4149 /*
   4150 * delete all DB records associated with a particular certificate
   4151 */
   4152 static SECStatus
   4153 DeletePermCert(NSSLOWCERTCertificate *cert)
   4154 {
   4155    SECStatus rv;
   4156    SECStatus ret;
   4157 
   4158    ret = SECSuccess;
   4159 
   4160    rv = DeleteDBCertEntry(cert->dbhandle, &cert->certKey);
   4161    if (rv != SECSuccess) {
   4162        ret = SECFailure;
   4163    }
   4164 
   4165    rv = RemovePermSubjectNode(cert);
   4166 
   4167    return (ret);
   4168 }
   4169 
   4170 /*
   4171 * Delete a certificate from the permanent database.
   4172 */
   4173 SECStatus
   4174 nsslowcert_DeletePermCertificate(NSSLOWCERTCertificate *cert)
   4175 {
   4176    SECStatus rv;
   4177 
   4178    nsslowcert_LockDB(cert->dbhandle);
   4179 
   4180    /* delete the records from the permanent database */
   4181    rv = DeletePermCert(cert);
   4182 
   4183    /* get rid of dbcert and stuff pointing to it */
   4184    DestroyDBEntry((certDBEntry *)cert->dbEntry);
   4185    cert->dbEntry = NULL;
   4186    cert->trust = NULL;
   4187 
   4188    nsslowcert_UnlockDB(cert->dbhandle);
   4189    return (rv);
   4190 }
   4191 
   4192 /*
   4193 * Traverse all of the entries in the database of a particular type
   4194 * call the given function for each one.
   4195 */
   4196 SECStatus
   4197 nsslowcert_TraverseDBEntries(NSSLOWCERTCertDBHandle *handle,
   4198                             certDBEntryType type,
   4199                             SECStatus (*callback)(SECItem *data, SECItem *key,
   4200                                                   certDBEntryType type, void *pdata),
   4201                             void *udata)
   4202 {
   4203    DBT data;
   4204    DBT key;
   4205    SECStatus rv = SECSuccess;
   4206    int ret;
   4207    SECItem dataitem;
   4208    SECItem keyitem;
   4209    unsigned char *buf;
   4210    unsigned char *keybuf;
   4211 
   4212    ret = certdb_Seq(handle->permCertDB, &key, &data, R_FIRST);
   4213    if (ret) {
   4214        return (SECFailure);
   4215    }
   4216    /* here, ret is zero and rv is SECSuccess.
   4217     * Below here, ret is a count of successful calls to the callback function.
   4218     */
   4219    do {
   4220        buf = (unsigned char *)data.data;
   4221 
   4222        if (buf[1] == (unsigned char)type) {
   4223            dataitem.len = data.size;
   4224            dataitem.data = buf;
   4225            dataitem.type = siBuffer;
   4226            keyitem.len = key.size - SEC_DB_KEY_HEADER_LEN;
   4227            keybuf = (unsigned char *)key.data;
   4228            keyitem.data = &keybuf[SEC_DB_KEY_HEADER_LEN];
   4229            keyitem.type = siBuffer;
   4230            /* type should equal keybuf[0].  */
   4231 
   4232            rv = (*callback)(&dataitem, &keyitem, type, udata);
   4233            if (rv == SECSuccess) {
   4234                ++ret;
   4235            }
   4236        }
   4237    } while (certdb_Seq(handle->permCertDB, &key, &data, R_NEXT) == 0);
   4238    /* If any callbacks succeeded, or no calls to callbacks were made,
   4239     * then report success.  Otherwise, report failure.
   4240     */
   4241    return (ret ? SECSuccess : rv);
   4242 }
   4243 /*
   4244 * Decode a certificate and enter it into the temporary certificate database.
   4245 * Deal with nicknames correctly
   4246 *
   4247 * This is the private entry point.
   4248 */
   4249 static NSSLOWCERTCertificate *
   4250 DecodeACert(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry)
   4251 {
   4252    NSSLOWCERTCertificate *cert = NULL;
   4253 
   4254    cert = nsslowcert_DecodeDERCertificate(&entry->derCert, entry->nickname);
   4255 
   4256    if (cert == NULL) {
   4257        goto loser;
   4258    }
   4259 
   4260    cert->dbhandle = handle;
   4261    cert->dbEntry = entry;
   4262    cert->trust = &entry->trust;
   4263 
   4264    return (cert);
   4265 
   4266 loser:
   4267    return (0);
   4268 }
   4269 
   4270 static NSSLOWCERTTrust *
   4271 CreateTrust(void)
   4272 {
   4273    NSSLOWCERTTrust *trust = NULL;
   4274 
   4275    nsslowcert_LockFreeList();
   4276    trust = trustListHead;
   4277    if (trust) {
   4278        trustListCount--;
   4279        trustListHead = trust->next;
   4280        trust->next = NULL;
   4281    }
   4282    PORT_Assert(trustListCount >= 0);
   4283    nsslowcert_UnlockFreeList();
   4284    if (trust) {
   4285        return trust;
   4286    }
   4287 
   4288    return PORT_ZNew(NSSLOWCERTTrust);
   4289 }
   4290 
   4291 static void
   4292 DestroyTrustFreeList(void)
   4293 {
   4294    NSSLOWCERTTrust *trust;
   4295 
   4296    nsslowcert_LockFreeList();
   4297    while (NULL != (trust = trustListHead)) {
   4298        trustListCount--;
   4299        trustListHead = trust->next;
   4300        PORT_Free(trust);
   4301    }
   4302    PORT_Assert(!trustListCount);
   4303    trustListCount = 0;
   4304    nsslowcert_UnlockFreeList();
   4305 }
   4306 
   4307 static NSSLOWCERTTrust *
   4308 DecodeTrustEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry,
   4309                 const SECItem *dbKey)
   4310 {
   4311    NSSLOWCERTTrust *trust = CreateTrust();
   4312    if (trust == NULL) {
   4313        return trust;
   4314    }
   4315    trust->dbhandle = handle;
   4316    trust->dbEntry = entry;
   4317    trust->dbKey.data = pkcs11_copyStaticData(dbKey->data, dbKey->len,
   4318                                              trust->dbKeySpace, sizeof(trust->dbKeySpace));
   4319    if (!trust->dbKey.data) {
   4320        PORT_Free(trust);
   4321        return NULL;
   4322    }
   4323    trust->dbKey.len = dbKey->len;
   4324 
   4325    trust->trust = &entry->trust;
   4326    trust->derCert = &entry->derCert;
   4327 
   4328    return (trust);
   4329 }
   4330 
   4331 typedef struct {
   4332    PermCertCallback certfunc;
   4333    NSSLOWCERTCertDBHandle *handle;
   4334    void *data;
   4335 } PermCertCallbackState;
   4336 
   4337 /*
   4338 * traversal callback to decode certs and call callers callback
   4339 */
   4340 static SECStatus
   4341 certcallback(SECItem *dbdata, SECItem *dbkey, certDBEntryType type, void *data)
   4342 {
   4343    PermCertCallbackState *mystate;
   4344    SECStatus rv;
   4345    certDBEntryCert *entry;
   4346    SECItem entryitem;
   4347    NSSLOWCERTCertificate *cert;
   4348    PLArenaPool *arena = NULL;
   4349 
   4350    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   4351    if (arena == NULL) {
   4352        goto loser;
   4353    }
   4354 
   4355    entry = (certDBEntryCert *)PORT_ArenaAlloc(arena, sizeof(certDBEntryCert));
   4356    if (!entry) {
   4357        PORT_SetError(SEC_ERROR_NO_MEMORY);
   4358        goto loser;
   4359    }
   4360    mystate = (PermCertCallbackState *)data;
   4361    entry->common.version = (unsigned int)dbdata->data[0];
   4362    entry->common.type = (certDBEntryType)dbdata->data[1];
   4363    entry->common.flags = (unsigned int)dbdata->data[2];
   4364    entry->common.arena = arena;
   4365 
   4366    entryitem.len = dbdata->len - SEC_DB_ENTRY_HEADER_LEN;
   4367    entryitem.data = &dbdata->data[SEC_DB_ENTRY_HEADER_LEN];
   4368 
   4369    rv = DecodeDBCertEntry(entry, &entryitem);
   4370    if (rv != SECSuccess) {
   4371        goto loser;
   4372    }
   4373    entry->derCert.type = siBuffer;
   4374 
   4375    /* note: Entry is 'inheritted'.  */
   4376    cert = DecodeACert(mystate->handle, entry);
   4377 
   4378    rv = (*mystate->certfunc)(cert, dbkey, mystate->data);
   4379 
   4380    /* arena stored in entry destroyed by nsslowcert_DestroyCertificate */
   4381    nsslowcert_DestroyCertificateNoLocking(cert);
   4382 
   4383    return (rv);
   4384 
   4385 loser:
   4386    if (arena) {
   4387        PORT_FreeArena(arena, PR_FALSE);
   4388    }
   4389    return (SECFailure);
   4390 }
   4391 
   4392 /*
   4393 * Traverse all of the certificates in the permanent database and
   4394 * call the given function for each one; expect the caller to have lock.
   4395 */
   4396 static SECStatus
   4397 TraversePermCertsNoLocking(NSSLOWCERTCertDBHandle *handle,
   4398                           SECStatus (*certfunc)(NSSLOWCERTCertificate *cert,
   4399                                                 SECItem *k,
   4400                                                 void *pdata),
   4401                           void *udata)
   4402 {
   4403    SECStatus rv;
   4404    PermCertCallbackState mystate;
   4405 
   4406    mystate.certfunc = certfunc;
   4407    mystate.handle = handle;
   4408    mystate.data = udata;
   4409    rv = nsslowcert_TraverseDBEntries(handle, certDBEntryTypeCert, certcallback,
   4410                                      (void *)&mystate);
   4411 
   4412    return (rv);
   4413 }
   4414 
   4415 /*
   4416 * Traverse all of the certificates in the permanent database and
   4417 * call the given function for each one.
   4418 */
   4419 SECStatus
   4420 nsslowcert_TraversePermCerts(NSSLOWCERTCertDBHandle *handle,
   4421                             SECStatus (*certfunc)(NSSLOWCERTCertificate *cert, SECItem *k,
   4422                                                   void *pdata),
   4423                             void *udata)
   4424 {
   4425    SECStatus rv;
   4426 
   4427    nsslowcert_LockDB(handle);
   4428    rv = TraversePermCertsNoLocking(handle, certfunc, udata);
   4429    nsslowcert_UnlockDB(handle);
   4430 
   4431    return (rv);
   4432 }
   4433 
   4434 /*
   4435 * Close the database
   4436 */
   4437 void
   4438 nsslowcert_ClosePermCertDB(NSSLOWCERTCertDBHandle *handle)
   4439 {
   4440    if (handle) {
   4441        if (handle->permCertDB) {
   4442            certdb_Close(handle->permCertDB);
   4443            handle->permCertDB = NULL;
   4444        }
   4445        if (handle->dbMon) {
   4446            PZ_DestroyMonitor(handle->dbMon);
   4447            handle->dbMon = NULL;
   4448        }
   4449        PORT_Free(handle);
   4450    }
   4451    return;
   4452 }
   4453 
   4454 /*
   4455 * Get the trust attributes from a certificate
   4456 */
   4457 SECStatus
   4458 nsslowcert_GetCertTrust(NSSLOWCERTCertificate *cert, NSSLOWCERTCertTrust *trust)
   4459 {
   4460    SECStatus rv;
   4461 
   4462    nsslowcert_LockCertTrust(cert);
   4463 
   4464    if (cert->trust == NULL) {
   4465        rv = SECFailure;
   4466    } else {
   4467        *trust = *cert->trust;
   4468        rv = SECSuccess;
   4469    }
   4470 
   4471    nsslowcert_UnlockCertTrust(cert);
   4472    return (rv);
   4473 }
   4474 
   4475 /*
   4476 * Change the trust attributes of a certificate and make them permanent
   4477 * in the database.
   4478 */
   4479 SECStatus
   4480 nsslowcert_ChangeCertTrust(NSSLOWCERTCertDBHandle *handle,
   4481                           NSSLOWCERTCertificate *cert, NSSLOWCERTCertTrust *trust)
   4482 {
   4483    certDBEntryCert *entry;
   4484    int rv;
   4485    SECStatus ret;
   4486 
   4487    nsslowcert_LockDB(handle);
   4488    nsslowcert_LockCertTrust(cert);
   4489    /* only set the trust on permanent certs */
   4490    if (cert->trust == NULL) {
   4491        ret = SECFailure;
   4492        goto done;
   4493    }
   4494 
   4495    *cert->trust = *trust;
   4496    if (cert->dbEntry == NULL) {
   4497        ret = SECSuccess; /* not in permanent database */
   4498        goto done;
   4499    }
   4500 
   4501    entry = cert->dbEntry;
   4502    entry->trust = *trust;
   4503 
   4504    rv = WriteDBCertEntry(handle, entry);
   4505    if (rv) {
   4506        ret = SECFailure;
   4507        goto done;
   4508    }
   4509 
   4510    ret = SECSuccess;
   4511 
   4512 done:
   4513    nsslowcert_UnlockCertTrust(cert);
   4514    nsslowcert_UnlockDB(handle);
   4515    return (ret);
   4516 }
   4517 
   4518 static SECStatus
   4519 nsslowcert_UpdatePermCert(NSSLOWCERTCertDBHandle *dbhandle,
   4520                          NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust)
   4521 {
   4522    char *oldnn;
   4523    certDBEntryCert *entry;
   4524    PRBool conflict;
   4525    SECStatus ret;
   4526 
   4527    PORT_Assert(!cert->dbEntry);
   4528 
   4529    /* don't add a conflicting nickname */
   4530    conflict = nsslowcert_CertNicknameConflict(nickname, &cert->derSubject,
   4531                                               dbhandle);
   4532    if (conflict) {
   4533        ret = SECFailure;
   4534        goto done;
   4535    }
   4536 
   4537    /* save old nickname so that we can delete it */
   4538    oldnn = cert->nickname;
   4539 
   4540    entry = AddCertToPermDB(dbhandle, cert, nickname, trust);
   4541 
   4542    if (entry == NULL) {
   4543        ret = SECFailure;
   4544        goto done;
   4545    }
   4546 
   4547    pkcs11_freeNickname(oldnn, cert->nicknameSpace);
   4548 
   4549    cert->nickname = (entry->nickname) ? pkcs11_copyNickname(entry->nickname,
   4550                                                             cert->nicknameSpace, sizeof(cert->nicknameSpace))
   4551                                       : NULL;
   4552    cert->trust = &entry->trust;
   4553    cert->dbEntry = entry;
   4554 
   4555    ret = SECSuccess;
   4556 done:
   4557    return (ret);
   4558 }
   4559 
   4560 SECStatus
   4561 nsslowcert_AddPermCert(NSSLOWCERTCertDBHandle *dbhandle,
   4562                       NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust)
   4563 {
   4564    SECStatus ret;
   4565 
   4566    nsslowcert_LockDB(dbhandle);
   4567 
   4568    ret = nsslowcert_UpdatePermCert(dbhandle, cert, nickname, trust);
   4569 
   4570    nsslowcert_UnlockDB(dbhandle);
   4571    return (ret);
   4572 }
   4573 
   4574 /*
   4575 * Open the certificate database and index databases.  Create them if
   4576 * they are not there or bad.
   4577 */
   4578 SECStatus
   4579 nsslowcert_OpenCertDB(NSSLOWCERTCertDBHandle *handle, PRBool readOnly,
   4580                      const char *appName, const char *prefix,
   4581                      NSSLOWCERTDBNameFunc namecb, void *cbarg, PRBool openVolatile)
   4582 {
   4583    int rv;
   4584 
   4585    certdb_InitDBLock(handle);
   4586 
   4587    handle->dbMon = PZ_NewMonitor(nssILockCertDB);
   4588    PORT_Assert(handle->dbMon != NULL);
   4589    handle->dbVerify = PR_FALSE;
   4590 
   4591    rv = nsslowcert_OpenPermCertDB(handle, readOnly, appName, prefix,
   4592                                   namecb, cbarg);
   4593    if (rv) {
   4594        goto loser;
   4595    }
   4596 
   4597    return (SECSuccess);
   4598 
   4599 loser:
   4600    if (handle->dbMon) {
   4601        PZ_DestroyMonitor(handle->dbMon);
   4602        handle->dbMon = NULL;
   4603    }
   4604    PORT_SetError(SEC_ERROR_BAD_DATABASE);
   4605    return (SECFailure);
   4606 }
   4607 
   4608 PRBool
   4609 nsslowcert_needDBVerify(NSSLOWCERTCertDBHandle *handle)
   4610 {
   4611    if (!handle)
   4612        return PR_FALSE;
   4613    return handle->dbVerify;
   4614 }
   4615 
   4616 void
   4617 nsslowcert_setDBVerify(NSSLOWCERTCertDBHandle *handle, PRBool value)
   4618 {
   4619    handle->dbVerify = value;
   4620 }
   4621 
   4622 /*
   4623 * Lookup a certificate in the databases.
   4624 */
   4625 static NSSLOWCERTCertificate *
   4626 FindCertByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey, PRBool lockdb)
   4627 {
   4628    NSSLOWCERTCertificate *cert = NULL;
   4629    certDBEntryCert *entry;
   4630    PRBool locked = PR_FALSE;
   4631 
   4632    if (lockdb) {
   4633        locked = PR_TRUE;
   4634        nsslowcert_LockDB(handle);
   4635    }
   4636 
   4637    /* find in perm database */
   4638    entry = ReadDBCertEntry(handle, certKey);
   4639 
   4640    if (entry == NULL) {
   4641        goto loser;
   4642    }
   4643 
   4644    /* inherit entry */
   4645    cert = DecodeACert(handle, entry);
   4646 
   4647 loser:
   4648    if (cert == NULL) {
   4649        if (entry) {
   4650            DestroyDBEntry((certDBEntry *)entry);
   4651        }
   4652    }
   4653 
   4654    if (locked) {
   4655        nsslowcert_UnlockDB(handle);
   4656    }
   4657 
   4658    return (cert);
   4659 }
   4660 
   4661 /*
   4662 * Lookup a certificate in the databases.
   4663 */
   4664 static NSSLOWCERTTrust *
   4665 FindTrustByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey, PRBool lockdb)
   4666 {
   4667    NSSLOWCERTTrust *trust = NULL;
   4668    certDBEntryCert *entry;
   4669    PRBool locked = PR_FALSE;
   4670 
   4671    if (lockdb) {
   4672        locked = PR_TRUE;
   4673        nsslowcert_LockDB(handle);
   4674    }
   4675 
   4676    /* find in perm database */
   4677    entry = ReadDBCertEntry(handle, certKey);
   4678 
   4679    if (entry == NULL) {
   4680        goto loser;
   4681    }
   4682 
   4683    if (!nsslowcert_hasTrust(&entry->trust)) {
   4684        goto loser;
   4685    }
   4686 
   4687    /* inherit entry */
   4688    trust = DecodeTrustEntry(handle, entry, certKey);
   4689 
   4690 loser:
   4691    if (trust == NULL) {
   4692        if (entry) {
   4693            DestroyDBEntry((certDBEntry *)entry);
   4694        }
   4695    }
   4696 
   4697    if (locked) {
   4698        nsslowcert_UnlockDB(handle);
   4699    }
   4700 
   4701    return (trust);
   4702 }
   4703 
   4704 /*
   4705 * Lookup a certificate in the databases without locking
   4706 */
   4707 NSSLOWCERTCertificate *
   4708 nsslowcert_FindCertByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
   4709 {
   4710    return (FindCertByKey(handle, certKey, PR_FALSE));
   4711 }
   4712 
   4713 /*
   4714 * Lookup a trust object in the databases without locking
   4715 */
   4716 NSSLOWCERTTrust *
   4717 nsslowcert_FindTrustByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
   4718 {
   4719    return (FindTrustByKey(handle, certKey, PR_FALSE));
   4720 }
   4721 
   4722 /*
   4723 * Generate a key from an issuerAndSerialNumber, and find the
   4724 * associated cert in the database.
   4725 */
   4726 NSSLOWCERTCertificate *
   4727 nsslowcert_FindCertByIssuerAndSN(NSSLOWCERTCertDBHandle *handle, NSSLOWCERTIssuerAndSN *issuerAndSN)
   4728 {
   4729    SECItem certKey;
   4730    SECItem *sn = &issuerAndSN->serialNumber;
   4731    SECItem *issuer = &issuerAndSN->derIssuer;
   4732    NSSLOWCERTCertificate *cert;
   4733    int data_len = sn->len;
   4734    int index = 0;
   4735 
   4736    /* automatically detect DER encoded serial numbers and remove the der
   4737     * encoding since the database expects unencoded data.
   4738     * if it's DER encoded, there must be at least 3 bytes, tag, len, data */
   4739    if ((sn->len >= 3) && (sn->data[0] == 0x2)) {
   4740        /* remove the der encoding of the serial number before generating the
   4741         * key.. */
   4742        int data_left = sn->len - 2;
   4743        data_len = sn->data[1];
   4744        index = 2;
   4745 
   4746        /* extended length ? (not very likely for a serial number) */
   4747        if (data_len & 0x80) {
   4748            int len_count = data_len & 0x7f;
   4749 
   4750            data_len = 0;
   4751            data_left -= len_count;
   4752            if (data_left > 0) {
   4753                while (len_count--) {
   4754                    data_len = (data_len << 8) | sn->data[index++];
   4755                }
   4756            }
   4757        }
   4758        /* XXX leaving any leading zeros on the serial number for backwards
   4759         * compatibility
   4760         */
   4761        /* not a valid der, must be just an unlucky serial number value */
   4762        if (data_len != data_left) {
   4763            data_len = sn->len;
   4764            index = 0;
   4765        }
   4766    }
   4767 
   4768    certKey.type = 0;
   4769    certKey.data = (unsigned char *)PORT_Alloc(sn->len + issuer->len);
   4770    certKey.len = data_len + issuer->len;
   4771 
   4772    if (certKey.data == NULL) {
   4773        return (0);
   4774    }
   4775 
   4776    /* first try the serial number as hand-decoded above*/
   4777    /* copy the serialNumber */
   4778    PORT_Memcpy(certKey.data, &sn->data[index], data_len);
   4779 
   4780    /* copy the issuer */
   4781    PORT_Memcpy(&certKey.data[data_len], issuer->data, issuer->len);
   4782 
   4783    cert = nsslowcert_FindCertByKey(handle, &certKey);
   4784    if (cert) {
   4785        PORT_Free(certKey.data);
   4786        return (cert);
   4787    }
   4788 
   4789    /* didn't find it, try by der encoded serial number */
   4790    /* copy the serialNumber */
   4791    PORT_Memcpy(certKey.data, sn->data, sn->len);
   4792 
   4793    /* copy the issuer */
   4794    PORT_Memcpy(&certKey.data[sn->len], issuer->data, issuer->len);
   4795    certKey.len = sn->len + issuer->len;
   4796 
   4797    cert = nsslowcert_FindCertByKey(handle, &certKey);
   4798 
   4799    PORT_Free(certKey.data);
   4800 
   4801    return (cert);
   4802 }
   4803 
   4804 /*
   4805 * Generate a key from an issuerAndSerialNumber, and find the
   4806 * associated cert in the database.
   4807 */
   4808 NSSLOWCERTTrust *
   4809 nsslowcert_FindTrustByIssuerAndSN(NSSLOWCERTCertDBHandle *handle,
   4810                                  NSSLOWCERTIssuerAndSN *issuerAndSN)
   4811 {
   4812    SECItem certKey;
   4813    SECItem *sn = &issuerAndSN->serialNumber;
   4814    SECItem *issuer = &issuerAndSN->derIssuer;
   4815    NSSLOWCERTTrust *trust;
   4816    unsigned char keyBuf[512];
   4817    int data_len = sn->len;
   4818    int index = 0;
   4819    int len;
   4820 
   4821    /* automatically detect DER encoded serial numbers and remove the der
   4822     * encoding since the database expects unencoded data.
   4823     * if it's DER encoded, there must be at least 3 bytes, tag, len, data */
   4824    if ((sn->len >= 3) && (sn->data[0] == 0x2)) {
   4825        /* remove the der encoding of the serial number before generating the
   4826         * key.. */
   4827        int data_left = sn->len - 2;
   4828        data_len = sn->data[1];
   4829        index = 2;
   4830 
   4831        /* extended length ? (not very likely for a serial number) */
   4832        if (data_len & 0x80) {
   4833            int len_count = data_len & 0x7f;
   4834 
   4835            data_len = 0;
   4836            data_left -= len_count;
   4837            if (data_left > 0) {
   4838                while (len_count--) {
   4839                    data_len = (data_len << 8) | sn->data[index++];
   4840                }
   4841            }
   4842        }
   4843        /* XXX leaving any leading zeros on the serial number for backwards
   4844         * compatibility
   4845         */
   4846        /* not a valid der, must be just an unlucky serial number value */
   4847        if (data_len != data_left) {
   4848            data_len = sn->len;
   4849            index = 0;
   4850        }
   4851    }
   4852 
   4853    certKey.type = 0;
   4854    certKey.len = data_len + issuer->len;
   4855    len = sn->len + issuer->len;
   4856    if (len > sizeof(keyBuf)) {
   4857        certKey.data = (unsigned char *)PORT_Alloc(len);
   4858    } else {
   4859        certKey.data = keyBuf;
   4860    }
   4861 
   4862    if (certKey.data == NULL) {
   4863        return (0);
   4864    }
   4865 
   4866    /* first try the serial number as hand-decoded above*/
   4867    /* copy the serialNumber */
   4868    PORT_Memcpy(certKey.data, &sn->data[index], data_len);
   4869 
   4870    /* copy the issuer */
   4871    PORT_Memcpy(&certKey.data[data_len], issuer->data, issuer->len);
   4872 
   4873    trust = nsslowcert_FindTrustByKey(handle, &certKey);
   4874    if (trust) {
   4875        pkcs11_freeStaticData(certKey.data, keyBuf);
   4876        return (trust);
   4877    }
   4878 
   4879    if (index == 0) {
   4880        pkcs11_freeStaticData(certKey.data, keyBuf);
   4881        return NULL;
   4882    }
   4883 
   4884    /* didn't find it, try by der encoded serial number */
   4885    /* copy the serialNumber */
   4886    PORT_Memcpy(certKey.data, sn->data, sn->len);
   4887 
   4888    /* copy the issuer */
   4889    PORT_Memcpy(&certKey.data[sn->len], issuer->data, issuer->len);
   4890    certKey.len = sn->len + issuer->len;
   4891 
   4892    trust = nsslowcert_FindTrustByKey(handle, &certKey);
   4893 
   4894    pkcs11_freeStaticData(certKey.data, keyBuf);
   4895 
   4896    return (trust);
   4897 }
   4898 
   4899 /*
   4900 * look for the given DER certificate in the database
   4901 */
   4902 NSSLOWCERTCertificate *
   4903 nsslowcert_FindCertByDERCert(NSSLOWCERTCertDBHandle *handle, SECItem *derCert)
   4904 {
   4905    PLArenaPool *arena;
   4906    SECItem certKey;
   4907    SECStatus rv;
   4908    NSSLOWCERTCertificate *cert = NULL;
   4909 
   4910    /* create a scratch arena */
   4911    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   4912    if (arena == NULL) {
   4913        return (NULL);
   4914    }
   4915 
   4916    /* extract the database key from the cert */
   4917    rv = nsslowcert_KeyFromDERCert(arena, derCert, &certKey);
   4918    if (rv != SECSuccess) {
   4919        goto loser;
   4920    }
   4921 
   4922    /* find the certificate */
   4923    cert = nsslowcert_FindCertByKey(handle, &certKey);
   4924 
   4925 loser:
   4926    PORT_FreeArena(arena, PR_FALSE);
   4927    return (cert);
   4928 }
   4929 
   4930 static void
   4931 DestroyCertificate(NSSLOWCERTCertificate *cert, PRBool lockdb)
   4932 {
   4933    int refCount;
   4934    NSSLOWCERTCertDBHandle *handle;
   4935 
   4936    if (cert) {
   4937 
   4938        handle = cert->dbhandle;
   4939 
   4940        /*
   4941         * handle may be NULL, for example if the cert was created with
   4942         * nsslowcert_DecodeDERCertificate.
   4943         */
   4944        if (lockdb && handle) {
   4945            nsslowcert_LockDB(handle);
   4946        }
   4947 
   4948        nsslowcert_LockCertRefCount(cert);
   4949        PORT_Assert(cert->referenceCount > 0);
   4950        refCount = --cert->referenceCount;
   4951        nsslowcert_UnlockCertRefCount(cert);
   4952 
   4953        if (refCount == 0) {
   4954            certDBEntryCert *entry = cert->dbEntry;
   4955 
   4956            if (entry) {
   4957                DestroyDBEntry((certDBEntry *)entry);
   4958            }
   4959 
   4960            pkcs11_freeNickname(cert->nickname, cert->nicknameSpace);
   4961            pkcs11_freeNickname(cert->emailAddr, cert->emailAddrSpace);
   4962            pkcs11_freeStaticData(cert->certKey.data, cert->certKeySpace);
   4963            cert->certKey.data = NULL;
   4964            cert->nickname = NULL;
   4965 
   4966            /* zero cert before freeing. Any stale references to this cert
   4967             * after this point will probably cause an exception.  */
   4968            PORT_Memset(cert, 0, sizeof *cert);
   4969 
   4970            /* use reflock to protect the free list */
   4971            nsslowcert_LockFreeList();
   4972            if (certListCount > MAX_CERT_LIST_COUNT) {
   4973                PORT_Free(cert);
   4974            } else {
   4975                certListCount++;
   4976                cert->next = certListHead;
   4977                certListHead = cert;
   4978            }
   4979            nsslowcert_UnlockFreeList();
   4980            cert = NULL;
   4981        }
   4982        if (lockdb && handle) {
   4983            nsslowcert_UnlockDB(handle);
   4984        }
   4985    }
   4986 
   4987    return;
   4988 }
   4989 
   4990 NSSLOWCERTCertificate *
   4991 nsslowcert_CreateCert(void)
   4992 {
   4993    NSSLOWCERTCertificate *cert;
   4994    nsslowcert_LockFreeList();
   4995    cert = certListHead;
   4996    if (cert) {
   4997        certListHead = cert->next;
   4998        certListCount--;
   4999    }
   5000    PORT_Assert(certListCount >= 0);
   5001    nsslowcert_UnlockFreeList();
   5002    if (cert) {
   5003        return cert;
   5004    }
   5005    return PORT_ZNew(NSSLOWCERTCertificate);
   5006 }
   5007 
   5008 static void
   5009 DestroyCertFreeList(void)
   5010 {
   5011    NSSLOWCERTCertificate *cert;
   5012 
   5013    nsslowcert_LockFreeList();
   5014    while (NULL != (cert = certListHead)) {
   5015        certListCount--;
   5016        certListHead = cert->next;
   5017        PORT_Free(cert);
   5018    }
   5019    PORT_Assert(!certListCount);
   5020    certListCount = 0;
   5021    nsslowcert_UnlockFreeList();
   5022 }
   5023 
   5024 void
   5025 nsslowcert_DestroyTrust(NSSLOWCERTTrust *trust)
   5026 {
   5027    certDBEntryCert *entry = trust->dbEntry;
   5028 
   5029    if (entry) {
   5030        DestroyDBEntry((certDBEntry *)entry);
   5031    }
   5032    pkcs11_freeStaticData(trust->dbKey.data, trust->dbKeySpace);
   5033    PORT_Memset(trust, 0, sizeof(*trust));
   5034 
   5035    nsslowcert_LockFreeList();
   5036    if (trustListCount > MAX_TRUST_LIST_COUNT) {
   5037        PORT_Free(trust);
   5038    } else {
   5039        trustListCount++;
   5040        trust->next = trustListHead;
   5041        trustListHead = trust;
   5042    }
   5043    nsslowcert_UnlockFreeList();
   5044 
   5045    return;
   5046 }
   5047 
   5048 void
   5049 nsslowcert_DestroyCertificate(NSSLOWCERTCertificate *cert)
   5050 {
   5051    DestroyCertificate(cert, PR_TRUE);
   5052    return;
   5053 }
   5054 
   5055 static void
   5056 nsslowcert_DestroyCertificateNoLocking(NSSLOWCERTCertificate *cert)
   5057 {
   5058    DestroyCertificate(cert, PR_FALSE);
   5059    return;
   5060 }
   5061 
   5062 /*
   5063 * Lookup a CRL in the databases. We mirror the same fast caching data base
   5064 *  caching stuff used by certificates....?
   5065 */
   5066 certDBEntryRevocation *
   5067 nsslowcert_FindCrlByKey(NSSLOWCERTCertDBHandle *handle,
   5068                        SECItem *crlKey, PRBool isKRL)
   5069 {
   5070    SECItem keyitem;
   5071    SECStatus rv;
   5072    PLArenaPool *arena = NULL;
   5073    certDBEntryRevocation *entry = NULL;
   5074    certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation
   5075                                    : certDBEntryTypeRevocation;
   5076 
   5077    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   5078    if (arena == NULL) {
   5079        goto loser;
   5080    }
   5081 
   5082    rv = EncodeDBGenericKey(crlKey, arena, &keyitem, crlType);
   5083    if (rv != SECSuccess) {
   5084        goto loser;
   5085    }
   5086 
   5087    /* find in perm database */
   5088    entry = ReadDBCrlEntry(handle, crlKey, crlType);
   5089 
   5090    if (entry == NULL) {
   5091        goto loser;
   5092    }
   5093 
   5094 loser:
   5095    if (arena) {
   5096        PORT_FreeArena(arena, PR_FALSE);
   5097    }
   5098 
   5099    return entry;
   5100 }
   5101 
   5102 /*
   5103 * replace the existing URL in the data base with a new one
   5104 */
   5105 static SECStatus
   5106 nsslowcert_UpdateCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl,
   5107                     SECItem *crlKey, char *url, PRBool isKRL)
   5108 {
   5109    SECStatus rv = SECFailure;
   5110    certDBEntryRevocation *entry = NULL;
   5111    certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation
   5112                                    : certDBEntryTypeRevocation;
   5113    DeleteDBCrlEntry(handle, crlKey, crlType);
   5114 
   5115    /* Write the new entry into the data base */
   5116    entry = NewDBCrlEntry(derCrl, url, crlType, 0);
   5117    if (entry == NULL)
   5118        goto done;
   5119 
   5120    rv = WriteDBCrlEntry(handle, entry, crlKey);
   5121    if (rv != SECSuccess)
   5122        goto done;
   5123 
   5124 done:
   5125    if (entry) {
   5126        DestroyDBEntry((certDBEntry *)entry);
   5127    }
   5128    return rv;
   5129 }
   5130 
   5131 SECStatus
   5132 nsslowcert_AddCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl,
   5133                  SECItem *crlKey, char *url, PRBool isKRL)
   5134 {
   5135    SECStatus rv;
   5136 
   5137    rv = nsslowcert_UpdateCrl(handle, derCrl, crlKey, url, isKRL);
   5138 
   5139    return rv;
   5140 }
   5141 
   5142 SECStatus
   5143 nsslowcert_DeletePermCRL(NSSLOWCERTCertDBHandle *handle, const SECItem *derName,
   5144                         PRBool isKRL)
   5145 {
   5146    SECStatus rv;
   5147    certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation
   5148                                    : certDBEntryTypeRevocation;
   5149 
   5150    rv = DeleteDBCrlEntry(handle, derName, crlType);
   5151    if (rv != SECSuccess)
   5152        goto done;
   5153 
   5154 done:
   5155    return rv;
   5156 }
   5157 
   5158 PRBool
   5159 nsslowcert_hasTrust(NSSLOWCERTCertTrust *trust)
   5160 {
   5161    if (trust == NULL) {
   5162        return PR_FALSE;
   5163    }
   5164    /* if we only have CERTDB__USER and CERTDB_TRUSTED_UNKNOWN bits, then
   5165     * we don't have a trust record. */
   5166    return !(((trust->sslFlags & ~(CERTDB_USER | CERTDB_TRUSTED_UNKNOWN)) == 0) &&
   5167             ((trust->emailFlags & ~(CERTDB_USER | CERTDB_TRUSTED_UNKNOWN)) == 0) &&
   5168             ((trust->objectSigningFlags & ~(CERTDB_USER | CERTDB_TRUSTED_UNKNOWN)) == 0));
   5169 }
   5170 
   5171 /*
   5172 * This function has the logic that decides if another person's cert and
   5173 * email profile from an S/MIME message should be saved.  It can deal with
   5174 * the case when there is no profile.
   5175 */
   5176 static SECStatus
   5177 nsslowcert_UpdateSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle,
   5178                              char *emailAddr, SECItem *derSubject, SECItem *emailProfile,
   5179                              SECItem *profileTime)
   5180 {
   5181    certDBEntrySMime *entry = NULL;
   5182    SECStatus rv = SECFailure;
   5183    ;
   5184 
   5185    /* find our existing entry */
   5186    entry = nsslowcert_ReadDBSMimeEntry(dbhandle, emailAddr);
   5187 
   5188    if (entry) {
   5189        /* keep our old db entry consistant for old applications. */
   5190        if (!SECITEM_ItemsAreEqual(derSubject, &entry->subjectName)) {
   5191            nsslowcert_UpdateSubjectEmailAddr(dbhandle, &entry->subjectName,
   5192                                              emailAddr, nsslowcert_remove);
   5193        }
   5194        DestroyDBEntry((certDBEntry *)entry);
   5195        entry = NULL;
   5196    }
   5197 
   5198    /* now save the entry */
   5199    entry = NewDBSMimeEntry(emailAddr, derSubject, emailProfile,
   5200                            profileTime, 0);
   5201    if (entry == NULL) {
   5202        rv = SECFailure;
   5203        goto loser;
   5204    }
   5205 
   5206    nsslowcert_LockDB(dbhandle);
   5207 
   5208    rv = DeleteDBSMimeEntry(dbhandle, emailAddr);
   5209    /* if delete fails, try to write new entry anyway... */
   5210 
   5211    /* link subject entry back here */
   5212    rv = nsslowcert_UpdateSubjectEmailAddr(dbhandle, derSubject, emailAddr,
   5213                                           nsslowcert_add);
   5214    if (rv != SECSuccess) {
   5215        nsslowcert_UnlockDB(dbhandle);
   5216        goto loser;
   5217    }
   5218 
   5219    rv = WriteDBSMimeEntry(dbhandle, entry);
   5220    if (rv != SECSuccess) {
   5221        nsslowcert_UnlockDB(dbhandle);
   5222        goto loser;
   5223    }
   5224 
   5225    nsslowcert_UnlockDB(dbhandle);
   5226 
   5227    rv = SECSuccess;
   5228 
   5229 loser:
   5230    if (entry) {
   5231        DestroyDBEntry((certDBEntry *)entry);
   5232    }
   5233    return (rv);
   5234 }
   5235 
   5236 SECStatus
   5237 nsslowcert_SaveSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle, char *emailAddr,
   5238                            SECItem *derSubject, SECItem *emailProfile, SECItem *profileTime)
   5239 {
   5240    SECStatus rv = SECFailure;
   5241    ;
   5242 
   5243    rv = nsslowcert_UpdateSMimeProfile(dbhandle, emailAddr,
   5244                                       derSubject, emailProfile, profileTime);
   5245 
   5246    return (rv);
   5247 }
   5248 
   5249 void
   5250 nsslowcert_DestroyFreeLists(void)
   5251 {
   5252    if (freeListLock == NULL) {
   5253        return;
   5254    }
   5255    DestroyCertEntryFreeList();
   5256    DestroyTrustFreeList();
   5257    DestroyCertFreeList();
   5258    SKIP_AFTER_FORK(PZ_DestroyLock(freeListLock));
   5259    freeListLock = NULL;
   5260 }
   5261 
   5262 void
   5263 nsslowcert_DestroyGlobalLocks(void)
   5264 {
   5265    if (dbLock) {
   5266        SKIP_AFTER_FORK(PZ_DestroyLock(dbLock));
   5267        dbLock = NULL;
   5268    }
   5269    if (certRefCountLock) {
   5270        SKIP_AFTER_FORK(PZ_DestroyLock(certRefCountLock));
   5271        certRefCountLock = NULL;
   5272    }
   5273    if (certTrustLock) {
   5274        SKIP_AFTER_FORK(PZ_DestroyLock(certTrustLock));
   5275        certTrustLock = NULL;
   5276    }
   5277 }
   5278 
   5279 certDBEntry *
   5280 nsslowcert_DecodeAnyDBEntry(SECItem *dbData, const SECItem *dbKey,
   5281                            certDBEntryType entryType, void *pdata)
   5282 {
   5283    PLArenaPool *arena = NULL;
   5284    certDBEntry *entry;
   5285    SECStatus rv;
   5286    SECItem dbEntry;
   5287 
   5288    if ((dbData->len < SEC_DB_ENTRY_HEADER_LEN) || (dbKey->len == 0)) {
   5289        PORT_SetError(SEC_ERROR_INVALID_ARGS);
   5290        goto loser;
   5291    }
   5292    dbEntry.data = &dbData->data[SEC_DB_ENTRY_HEADER_LEN];
   5293    dbEntry.len = dbData->len - SEC_DB_ENTRY_HEADER_LEN;
   5294 
   5295    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   5296    if (arena == NULL) {
   5297        goto loser;
   5298    }
   5299    entry = PORT_ArenaZNew(arena, certDBEntry);
   5300    if (!entry)
   5301        goto loser;
   5302 
   5303    entry->common.version = (unsigned int)dbData->data[0];
   5304    entry->common.flags = (unsigned int)dbData->data[2];
   5305    entry->common.type = entryType;
   5306    entry->common.arena = arena;
   5307 
   5308    switch (entryType) {
   5309        case certDBEntryTypeContentVersion: /* This type appears to be unused */
   5310        case certDBEntryTypeVersion:        /* This type has only the common hdr */
   5311            rv = SECSuccess;
   5312            break;
   5313 
   5314        case certDBEntryTypeSubject:
   5315            rv = DecodeDBSubjectEntry(&entry->subject, &dbEntry, dbKey);
   5316            break;
   5317 
   5318        case certDBEntryTypeNickname:
   5319            rv = DecodeDBNicknameEntry(&entry->nickname, &dbEntry,
   5320                                       (char *)dbKey->data);
   5321            break;
   5322 
   5323        /* smime profiles need entries created after the certs have
   5324         * been imported, loop over them in a second run */
   5325        case certDBEntryTypeSMimeProfile:
   5326            rv = DecodeDBSMimeEntry(&entry->smime, &dbEntry, (char *)dbKey->data);
   5327            break;
   5328 
   5329        case certDBEntryTypeCert:
   5330            rv = DecodeDBCertEntry(&entry->cert, &dbEntry);
   5331            break;
   5332 
   5333        case certDBEntryTypeKeyRevocation:
   5334        case certDBEntryTypeRevocation:
   5335            rv = DecodeDBCrlEntry(&entry->revocation, &dbEntry);
   5336            break;
   5337 
   5338        default:
   5339            PORT_SetError(SEC_ERROR_INVALID_ARGS);
   5340            rv = SECFailure;
   5341    }
   5342 
   5343    if (rv == SECSuccess)
   5344        return entry;
   5345 
   5346 loser:
   5347    if (arena)
   5348        PORT_FreeArena(arena, PR_FALSE);
   5349    return NULL;
   5350 }