RetroZilla/security/nss/lib/softoken/legacydb/pcertdb.c
Roy Tam 5c0160b5fb cherry-picked mozilla NSS upstream changes (to rev a245a4cc):
bug1201704, bug1171631, bug572412, bug1119618, bug1177770, bug1148374, bug1208243(part-of), bug1117022, bug1205688, bug1209443, bug1208508, bug1208503, bug1209435, bug1209451, bug1209456, bug1209541, bug1208503, bug1209546
2020-01-07 17:30:06 +08:00

5341 lines
128 KiB
C

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Permanent Certificate database handling code
*/
#include "lowkeyti.h"
#include "pcert.h"
#include "mcom_db.h"
#include "pcert.h"
#include "secitem.h"
#include "secder.h"
#include "secerr.h"
#include "lgdb.h"
/* forward declaration */
NSSLOWCERTCertificate *
nsslowcert_FindCertByDERCertNoLocking(NSSLOWCERTCertDBHandle *handle, SECItem *derCert);
static SECStatus
nsslowcert_UpdateSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle,
char *emailAddr, SECItem *derSubject, SECItem *emailProfile,
SECItem *profileTime);
static SECStatus
nsslowcert_UpdatePermCert(NSSLOWCERTCertDBHandle *dbhandle,
NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust);
static SECStatus
nsslowcert_UpdateCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl,
SECItem *crlKey, char *url, PRBool isKRL);
static NSSLOWCERTCertificate *certListHead = NULL;
static NSSLOWCERTTrust *trustListHead = NULL;
static certDBEntryCert *entryListHead = NULL;
static int certListCount = 0;
static int trustListCount = 0;
static int entryListCount = 0;
#define MAX_CERT_LIST_COUNT 10
#define MAX_TRUST_LIST_COUNT 10
#define MAX_ENTRY_LIST_COUNT 10
/*
* the following functions are wrappers for the db library that implement
* a global lock to make the database thread safe.
*/
static PZLock *dbLock = NULL;
static PZLock *certRefCountLock = NULL;
static PZLock *certTrustLock = NULL;
static PZLock *freeListLock = NULL;
void
certdb_InitDBLock(NSSLOWCERTCertDBHandle *handle)
{
if (dbLock == NULL) {
dbLock = PZ_NewLock(nssILockCertDB);
PORT_Assert(dbLock != NULL);
}
}
SECStatus
nsslowcert_InitLocks(void)
{
if (freeListLock == NULL) {
freeListLock = PZ_NewLock(nssILockRefLock);
if (freeListLock == NULL) {
return SECFailure;
}
}
if (certRefCountLock == NULL) {
certRefCountLock = PZ_NewLock(nssILockRefLock);
if (certRefCountLock == NULL) {
return SECFailure;
}
}
if (certTrustLock == NULL ) {
certTrustLock = PZ_NewLock(nssILockCertDB);
if (certTrustLock == NULL) {
return SECFailure;
}
}
return SECSuccess;
}
/*
* Acquire the global lock on the cert database.
* This lock is currently used for the following operations:
* adding or deleting a cert to either the temp or perm databases
* converting a temp to perm or perm to temp
* changing (maybe just adding!?) the trust of a cert
* chaning the DB status checking Configuration
*/
static void
nsslowcert_LockDB(NSSLOWCERTCertDBHandle *handle)
{
PZ_EnterMonitor(handle->dbMon);
return;
}
/*
* Free the global cert database lock.
*/
static void
nsslowcert_UnlockDB(NSSLOWCERTCertDBHandle *handle)
{
#ifdef DEBUG
PRStatus prstat = PZ_ExitMonitor(handle->dbMon);
PORT_Assert(prstat == PR_SUCCESS);
#else
PZ_ExitMonitor(handle->dbMon);
#endif
}
/*
* Acquire the cert reference count lock
* There is currently one global lock for all certs, but I'm putting a cert
* arg here so that it will be easy to make it per-cert in the future if
* that turns out to be necessary.
*/
static void
nsslowcert_LockCertRefCount(NSSLOWCERTCertificate *cert)
{
PORT_Assert(certRefCountLock != NULL);
PZ_Lock(certRefCountLock);
return;
}
/*
* Free the cert reference count lock
*/
static void
nsslowcert_UnlockCertRefCount(NSSLOWCERTCertificate *cert)
{
PORT_Assert(certRefCountLock != NULL);
#ifdef DEBUG
{
PRStatus prstat = PZ_Unlock(certRefCountLock);
PORT_Assert(prstat == PR_SUCCESS);
}
#else
PZ_Unlock(certRefCountLock);
#endif
}
/*
* Acquire the cert trust lock
* There is currently one global lock for all certs, but I'm putting a cert
* arg here so that it will be easy to make it per-cert in the future if
* that turns out to be necessary.
*/
static void
nsslowcert_LockCertTrust(NSSLOWCERTCertificate *cert)
{
PORT_Assert(certTrustLock != NULL);
PZ_Lock(certTrustLock);
return;
}
/*
* Free the cert trust lock
*/
static void
nsslowcert_UnlockCertTrust(NSSLOWCERTCertificate *cert)
{
PORT_Assert(certTrustLock != NULL);
#ifdef DEBUG
{
PRStatus prstat = PZ_Unlock(certTrustLock);
PORT_Assert(prstat == PR_SUCCESS);
}
#else
PZ_Unlock(certTrustLock);
#endif
}
/*
* Acquire the cert reference count lock
* There is currently one global lock for all certs, but I'm putting a cert
* arg here so that it will be easy to make it per-cert in the future if
* that turns out to be necessary.
*/
static void
nsslowcert_LockFreeList(void)
{
PORT_Assert(freeListLock != NULL);
SKIP_AFTER_FORK(PZ_Lock(freeListLock));
return;
}
/*
* Free the cert reference count lock
*/
static void
nsslowcert_UnlockFreeList(void)
{
PORT_Assert(freeListLock != NULL);
#ifdef DEBUG
{
PRStatus prstat = PR_SUCCESS;
SKIP_AFTER_FORK(prstat = PZ_Unlock(freeListLock));
PORT_Assert(prstat == PR_SUCCESS);
}
#else
SKIP_AFTER_FORK(PZ_Unlock(freeListLock));
#endif
}
NSSLOWCERTCertificate *
nsslowcert_DupCertificate(NSSLOWCERTCertificate *c)
{
if (c) {
nsslowcert_LockCertRefCount(c);
++c->referenceCount;
nsslowcert_UnlockCertRefCount(c);
}
return c;
}
static int
certdb_Get(DB *db, DBT *key, DBT *data, unsigned int flags)
{
int ret;
PORT_Assert(dbLock != NULL);
PZ_Lock(dbLock);
ret = (* db->get)(db, key, data, flags);
(void)PZ_Unlock(dbLock);
return(ret);
}
static int
certdb_Put(DB *db, DBT *key, DBT *data, unsigned int flags)
{
int ret = 0;
PORT_Assert(dbLock != NULL);
PZ_Lock(dbLock);
ret = (* db->put)(db, key, data, flags);
(void)PZ_Unlock(dbLock);
return(ret);
}
static int
certdb_Sync(DB *db, unsigned int flags)
{
int ret;
PORT_Assert(dbLock != NULL);
PZ_Lock(dbLock);
ret = (* db->sync)(db, flags);
(void)PZ_Unlock(dbLock);
return(ret);
}
#define DB_NOT_FOUND -30991 /* from DBM 3.2 */
static int
certdb_Del(DB *db, DBT *key, unsigned int flags)
{
int ret;
PORT_Assert(dbLock != NULL);
PZ_Lock(dbLock);
ret = (* db->del)(db, key, flags);
(void)PZ_Unlock(dbLock);
/* don't fail if the record is already deleted */
if (ret == DB_NOT_FOUND) {
ret = 0;
}
return(ret);
}
static int
certdb_Seq(DB *db, DBT *key, DBT *data, unsigned int flags)
{
int ret;
PORT_Assert(dbLock != NULL);
PZ_Lock(dbLock);
ret = (* db->seq)(db, key, data, flags);
(void)PZ_Unlock(dbLock);
return(ret);
}
static void
certdb_Close(DB *db)
{
PORT_Assert(dbLock != NULL);
SKIP_AFTER_FORK(PZ_Lock(dbLock));
(* db->close)(db);
SKIP_AFTER_FORK(PZ_Unlock(dbLock));
return;
}
void
pkcs11_freeNickname(char *nickname, char *space)
{
if (nickname && nickname != space) {
PORT_Free(nickname);
}
}
char *
pkcs11_copyNickname(char *nickname,char *space, int spaceLen)
{
int len;
char *copy = NULL;
len = PORT_Strlen(nickname)+1;
if (len <= spaceLen) {
copy = space;
PORT_Memcpy(copy,nickname,len);
} else {
copy = PORT_Strdup(nickname);
}
return copy;
}
void
pkcs11_freeStaticData (unsigned char *data, unsigned char *space)
{
if (data && data != space) {
PORT_Free(data);
}
}
unsigned char *
pkcs11_allocStaticData(int len, unsigned char *space, int spaceLen)
{
unsigned char *data = NULL;
if (len <= spaceLen) {
data = space;
} else {
data = (unsigned char *) PORT_Alloc(len);
}
return data;
}
unsigned char *
pkcs11_copyStaticData(unsigned char *data, int len,
unsigned char *space, int spaceLen)
{
unsigned char *copy = pkcs11_allocStaticData(len, space, spaceLen);
if (copy) {
PORT_Memcpy(copy,data,len);
}
return copy;
}
/*
* destroy a database entry
*/
static void
DestroyDBEntry(certDBEntry *entry)
{
PLArenaPool *arena = entry->common.arena;
/* must be one of our certDBEntry from the free list */
if (arena == NULL) {
certDBEntryCert *certEntry;
if ( entry->common.type != certDBEntryTypeCert) {
return;
}
certEntry = (certDBEntryCert *)entry;
pkcs11_freeStaticData(certEntry->derCert.data, certEntry->derCertSpace);
pkcs11_freeNickname(certEntry->nickname, certEntry->nicknameSpace);
nsslowcert_LockFreeList();
if (entryListCount > MAX_ENTRY_LIST_COUNT) {
PORT_Free(certEntry);
} else {
entryListCount++;
PORT_Memset(certEntry, 0, sizeof( *certEntry));
certEntry->next = entryListHead;
entryListHead = certEntry;
}
nsslowcert_UnlockFreeList();
return;
}
/* Zero out the entry struct, so that any further attempts to use it
* will cause an exception (e.g. null pointer reference). */
PORT_Memset(&entry->common, 0, sizeof entry->common);
PORT_FreeArena(arena, PR_FALSE);
return;
}
/* forward references */
static void nsslowcert_DestroyCertificateNoLocking(NSSLOWCERTCertificate *cert);
static SECStatus
DeleteDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryType type, SECItem *dbkey)
{
DBT key;
int ret;
/* init the database key */
key.data = dbkey->data;
key.size = dbkey->len;
dbkey->data[0] = (unsigned char)type;
/* delete entry from database */
ret = certdb_Del(handle->permCertDB, &key, 0 );
if ( ret != 0 ) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
ret = certdb_Sync(handle->permCertDB, 0);
if ( ret ) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
return(SECSuccess);
loser:
return(SECFailure);
}
static SECStatus
ReadDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCommon *entry,
SECItem *dbkey, SECItem *dbentry, PLArenaPool *arena)
{
DBT data, key;
int ret;
unsigned char *buf;
/* init the database key */
key.data = dbkey->data;
key.size = dbkey->len;
dbkey->data[0] = (unsigned char)entry->type;
/* read entry from database */
ret = certdb_Get(handle->permCertDB, &key, &data, 0 );
if ( ret != 0 ) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* validate the entry */
if ( data.size < SEC_DB_ENTRY_HEADER_LEN ) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
buf = (unsigned char *)data.data;
/* version 7 has the same schema, we may be using a v7 db if we openned
* the databases readonly. */
if (!((buf[0] == (unsigned char)CERT_DB_FILE_VERSION)
|| (buf[0] == (unsigned char) CERT_DB_V7_FILE_VERSION))) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
if ( buf[1] != (unsigned char)entry->type ) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* copy out header information */
entry->version = (unsigned int)buf[0];
entry->type = (certDBEntryType)buf[1];
entry->flags = (unsigned int)buf[2];
/* format body of entry for return to caller */
dbentry->len = data.size - SEC_DB_ENTRY_HEADER_LEN;
if ( dbentry->len ) {
if (arena) {
dbentry->data = (unsigned char *)
PORT_ArenaAlloc(arena, dbentry->len);
if ( dbentry->data == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(dbentry->data, &buf[SEC_DB_ENTRY_HEADER_LEN],
dbentry->len);
} else {
dbentry->data = &buf[SEC_DB_ENTRY_HEADER_LEN];
}
} else {
dbentry->data = NULL;
}
return(SECSuccess);
loser:
return(SECFailure);
}
/**
** Implement low level database access
**/
static SECStatus
WriteDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCommon *entry,
SECItem *dbkey, SECItem *dbentry)
{
int ret;
DBT data, key;
unsigned char *buf;
data.data = dbentry->data;
data.size = dbentry->len;
buf = (unsigned char*)data.data;
buf[0] = (unsigned char)entry->version;
buf[1] = (unsigned char)entry->type;
buf[2] = (unsigned char)entry->flags;
key.data = dbkey->data;
key.size = dbkey->len;
dbkey->data[0] = (unsigned char)entry->type;
/* put the record into the database now */
ret = certdb_Put(handle->permCertDB, &key, &data, 0);
if ( ret != 0 ) {
goto loser;
}
ret = certdb_Sync( handle->permCertDB, 0 );
if ( ret ) {
goto loser;
}
return(SECSuccess);
loser:
return(SECFailure);
}
/*
* encode a database cert record
*/
static SECStatus
EncodeDBCertEntry(certDBEntryCert *entry, PLArenaPool *arena, SECItem *dbitem)
{
unsigned int nnlen;
unsigned char *buf;
char *nn;
char zbuf = 0;
if ( entry->nickname ) {
nn = entry->nickname;
} else {
nn = &zbuf;
}
nnlen = PORT_Strlen(nn) + 1;
/* allocate space for encoded database record, including space
* for low level header
*/
dbitem->len = entry->derCert.len + nnlen + DB_CERT_ENTRY_HEADER_LEN +
SEC_DB_ENTRY_HEADER_LEN;
dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
if ( dbitem->data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/* fill in database record */
buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
buf[0] = (PRUint8)( entry->trust.sslFlags >> 8 );
buf[1] = (PRUint8)( entry->trust.sslFlags );
buf[2] = (PRUint8)( entry->trust.emailFlags >> 8 );
buf[3] = (PRUint8)( entry->trust.emailFlags );
buf[4] = (PRUint8)( entry->trust.objectSigningFlags >> 8 );
buf[5] = (PRUint8)( entry->trust.objectSigningFlags );
buf[6] = (PRUint8)( entry->derCert.len >> 8 );
buf[7] = (PRUint8)( entry->derCert.len );
buf[8] = (PRUint8)( nnlen >> 8 );
buf[9] = (PRUint8)( nnlen );
PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN], entry->derCert.data,
entry->derCert.len);
PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN + entry->derCert.len],
nn, nnlen);
return(SECSuccess);
loser:
return(SECFailure);
}
/*
* encode a database key for a cert record
*/
static SECStatus
EncodeDBCertKey(const SECItem *certKey, PLArenaPool *arena, SECItem *dbkey)
{
unsigned int len = certKey->len + SEC_DB_KEY_HEADER_LEN;
if (len > NSS_MAX_LEGACY_DB_KEY_SIZE)
goto loser;
if (arena) {
dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, len);
} else {
if (dbkey->len < len) {
dbkey->data = (unsigned char *)PORT_Alloc(len);
}
}
dbkey->len = len;
if ( dbkey->data == NULL ) {
goto loser;
}
PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],
certKey->data, certKey->len);
dbkey->data[0] = certDBEntryTypeCert;
return(SECSuccess);
loser:
return(SECFailure);
}
static SECStatus
EncodeDBGenericKey(const SECItem *certKey, PLArenaPool *arena, SECItem *dbkey,
certDBEntryType entryType)
{
/*
* we only allow _one_ KRL key!
*/
if (entryType == certDBEntryTypeKeyRevocation) {
dbkey->len = SEC_DB_KEY_HEADER_LEN;
dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
if ( dbkey->data == NULL ) {
goto loser;
}
dbkey->data[0] = (unsigned char) entryType;
return(SECSuccess);
}
dbkey->len = certKey->len + SEC_DB_KEY_HEADER_LEN;
if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
goto loser;
dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
if ( dbkey->data == NULL ) {
goto loser;
}
PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],
certKey->data, certKey->len);
dbkey->data[0] = (unsigned char) entryType;
return(SECSuccess);
loser:
return(SECFailure);
}
static SECStatus
DecodeDBCertEntry(certDBEntryCert *entry, SECItem *dbentry)
{
unsigned int nnlen;
unsigned int headerlen;
int lenoff;
/* allow updates of old versions of the database */
switch ( entry->common.version ) {
case 5:
headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
lenoff = 3;
break;
case 6:
/* should not get here */
PORT_Assert(0);
headerlen = DB_CERT_V6_ENTRY_HEADER_LEN;
lenoff = 3;
break;
case 7:
case 8:
headerlen = DB_CERT_ENTRY_HEADER_LEN;
lenoff = 6;
break;
default:
/* better not get here */
PORT_Assert(0);
headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
lenoff = 3;
break;
}
/* is record long enough for header? */
if ( dbentry->len < headerlen ) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* is database entry correct length? */
entry->derCert.len = ( ( dbentry->data[lenoff] << 8 ) |
dbentry->data[lenoff+1] );
nnlen = ( ( dbentry->data[lenoff+2] << 8 ) | dbentry->data[lenoff+3] );
lenoff = dbentry->len - ( entry->derCert.len + nnlen + headerlen );
if ( lenoff ) {
if ( lenoff < 0 || (lenoff & 0xffff) != 0 ) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* The cert size exceeded 64KB. Reconstruct the correct length. */
entry->derCert.len += lenoff;
}
/* copy the dercert */
entry->derCert.data = pkcs11_copyStaticData(&dbentry->data[headerlen],
entry->derCert.len,entry->derCertSpace,sizeof(entry->derCertSpace));
if ( entry->derCert.data == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/* copy the nickname */
if ( nnlen > 1 ) {
entry->nickname = (char *)pkcs11_copyStaticData(
&dbentry->data[headerlen+entry->derCert.len], nnlen,
(unsigned char *)entry->nicknameSpace,
sizeof(entry->nicknameSpace));
if ( entry->nickname == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
} else {
entry->nickname = NULL;
}
if ( entry->common.version < 7 ) {
/* allow updates of v5 db */
entry->trust.sslFlags = dbentry->data[0];
entry->trust.emailFlags = dbentry->data[1];
entry->trust.objectSigningFlags = dbentry->data[2];
} else {
entry->trust.sslFlags = ( dbentry->data[0] << 8 ) | dbentry->data[1];
entry->trust.emailFlags = ( dbentry->data[2] << 8 ) | dbentry->data[3];
entry->trust.objectSigningFlags =
( dbentry->data[4] << 8 ) | dbentry->data[5];
}
return(SECSuccess);
loser:
return(SECFailure);
}
/*
* Create a new certDBEntryCert from existing data
*/
static certDBEntryCert *
NewDBCertEntry(SECItem *derCert, char *nickname,
NSSLOWCERTCertTrust *trust, int flags)
{
certDBEntryCert *entry;
PLArenaPool *arena = NULL;
int nnlen;
arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
if ( !arena ) {
goto loser;
}
entry = PORT_ArenaZNew(arena, certDBEntryCert);
if ( entry == NULL ) {
goto loser;
}
/* fill in the dbCert */
entry->common.arena = arena;
entry->common.type = certDBEntryTypeCert;
entry->common.version = CERT_DB_FILE_VERSION;
entry->common.flags = flags;
if ( trust ) {
entry->trust = *trust;
}
entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, derCert->len);
if ( !entry->derCert.data ) {
goto loser;
}
entry->derCert.len = derCert->len;
PORT_Memcpy(entry->derCert.data, derCert->data, derCert->len);
nnlen = ( nickname ? strlen(nickname) + 1 : 0 );
if ( nnlen ) {
entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
if ( !entry->nickname ) {
goto loser;
}
PORT_Memcpy(entry->nickname, nickname, nnlen);
} else {
entry->nickname = 0;
}
return(entry);
loser:
/* allocation error, free arena and return */
if ( arena ) {
PORT_FreeArena(arena, PR_FALSE);
}
PORT_SetError(SEC_ERROR_NO_MEMORY);
return(0);
}
/*
* Decode a version 4 DBCert from the byte stream database format
* and construct a current database entry struct
*/
static certDBEntryCert *
DecodeV4DBCertEntry(unsigned char *buf, int len)
{
certDBEntryCert *entry;
int certlen;
int nnlen;
PLArenaPool *arena;
/* make sure length is at least long enough for the header */
if ( len < DBCERT_V4_HEADER_LEN ) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
return(0);
}
/* get other lengths */
certlen = buf[3] << 8 | buf[4];
nnlen = buf[5] << 8 | buf[6];
/* make sure DB entry is the right size */
if ( ( certlen + nnlen + DBCERT_V4_HEADER_LEN ) != len ) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
return(0);
}
/* allocate arena */
arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
if ( !arena ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
return(0);
}
/* allocate structure and members */
entry = (certDBEntryCert *) PORT_ArenaAlloc(arena, sizeof(certDBEntryCert));
if ( !entry ) {
goto loser;
}
entry->common.arena = arena;
entry->common.version = CERT_DB_FILE_VERSION;
entry->common.type = certDBEntryTypeCert;
entry->common.flags = 0;
entry->trust.sslFlags = buf[0];
entry->trust.emailFlags = buf[1];
entry->trust.objectSigningFlags = buf[2];
entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, certlen);
if ( !entry->derCert.data ) {
goto loser;
}
entry->derCert.len = certlen;
PORT_Memcpy(entry->derCert.data, &buf[DBCERT_V4_HEADER_LEN], certlen);
if ( nnlen ) {
entry->nickname = (char *) PORT_ArenaAlloc(arena, nnlen);
if ( !entry->nickname ) {
goto loser;
}
PORT_Memcpy(entry->nickname, &buf[DBCERT_V4_HEADER_LEN + certlen], nnlen);
if (PORT_Strcmp(entry->nickname, "Server-Cert") == 0) {
entry->trust.sslFlags |= CERTDB_USER;
}
} else {
entry->nickname = 0;
}
return(entry);
loser:
PORT_FreeArena(arena, PR_FALSE);
PORT_SetError(SEC_ERROR_NO_MEMORY);
return(0);
}
/*
* Encode a Certificate database entry into byte stream suitable for
* the database
*/
static SECStatus
WriteDBCertEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry)
{
SECItem dbitem, dbkey;
PLArenaPool *tmparena = NULL;
SECItem tmpitem;
SECStatus rv;
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( tmparena == NULL ) {
goto loser;
}
rv = EncodeDBCertEntry(entry, tmparena, &dbitem);
if ( rv != SECSuccess ) {
goto loser;
}
/* get the database key and format it */
rv = nsslowcert_KeyFromDERCert(tmparena, &entry->derCert, &tmpitem);
if ( rv == SECFailure ) {
goto loser;
}
rv = EncodeDBCertKey(&tmpitem, tmparena, &dbkey);
if ( rv == SECFailure ) {
goto loser;
}
/* now write it to the database */
rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
if ( rv != SECSuccess ) {
goto loser;
}
PORT_FreeArena(tmparena, PR_FALSE);
return(SECSuccess);
loser:
if ( tmparena ) {
PORT_FreeArena(tmparena, PR_FALSE);
}
return(SECFailure);
}
/*
* delete a certificate entry
*/
static SECStatus
DeleteDBCertEntry(NSSLOWCERTCertDBHandle *handle, SECItem *certKey)
{
SECItem dbkey;
SECStatus rv;
dbkey.data= NULL;
dbkey.len = 0;
rv = EncodeDBCertKey(certKey, NULL, &dbkey);
if ( rv != SECSuccess ) {
goto loser;
}
rv = DeleteDBEntry(handle, certDBEntryTypeCert, &dbkey);
if ( rv == SECFailure ) {
goto loser;
}
PORT_Free(dbkey.data);
return(SECSuccess);
loser:
if (dbkey.data) {
PORT_Free(dbkey.data);
}
return(SECFailure);
}
static certDBEntryCert *
CreateCertEntry(void)
{
certDBEntryCert *entry;
nsslowcert_LockFreeList();
entry = entryListHead;
if (entry) {
entryListCount--;
entryListHead = entry->next;
}
PORT_Assert(entryListCount >= 0);
nsslowcert_UnlockFreeList();
if (entry) {
return entry;
}
return PORT_ZNew(certDBEntryCert);
}
static void
DestroyCertEntryFreeList(void)
{
certDBEntryCert *entry;
nsslowcert_LockFreeList();
while (NULL != (entry = entryListHead)) {
entryListCount--;
entryListHead = entry->next;
PORT_Free(entry);
}
PORT_Assert(!entryListCount);
entryListCount = 0;
nsslowcert_UnlockFreeList();
}
/*
* Read a certificate entry
*/
static certDBEntryCert *
ReadDBCertEntry(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
{
certDBEntryCert *entry;
SECItem dbkey;
SECItem dbentry;
SECStatus rv;
unsigned char buf[512];
dbkey.data = buf;
dbkey.len = sizeof(buf);
entry = CreateCertEntry();
if ( entry == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry->common.arena = NULL;
entry->common.type = certDBEntryTypeCert;
rv = EncodeDBCertKey(certKey, NULL, &dbkey);
if ( rv != SECSuccess ) {
goto loser;
}
rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, NULL);
if ( rv == SECFailure ) {
goto loser;
}
rv = DecodeDBCertEntry(entry, &dbentry);
if ( rv != SECSuccess ) {
goto loser;
}
pkcs11_freeStaticData(dbkey.data,buf);
dbkey.data = NULL;
return(entry);
loser:
pkcs11_freeStaticData(dbkey.data,buf);
dbkey.data = NULL;
if ( entry ) {
DestroyDBEntry((certDBEntry *)entry);
}
return(NULL);
}
/*
* encode a database cert record
*/
static SECStatus
EncodeDBCrlEntry(certDBEntryRevocation *entry, PLArenaPool *arena, SECItem *dbitem)
{
unsigned int nnlen = 0;
unsigned char *buf;
if (entry->url) {
nnlen = PORT_Strlen(entry->url) + 1;
}
/* allocate space for encoded database record, including space
* for low level header
*/
dbitem->len = entry->derCrl.len + nnlen
+ SEC_DB_ENTRY_HEADER_LEN + DB_CRL_ENTRY_HEADER_LEN;
dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
if ( dbitem->data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/* fill in database record */
buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
buf[0] = (PRUint8)( entry->derCrl.len >> 8 );
buf[1] = (PRUint8)( entry->derCrl.len );
buf[2] = (PRUint8)( nnlen >> 8 );
buf[3] = (PRUint8)( nnlen );
PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN], entry->derCrl.data,
entry->derCrl.len);
if (nnlen != 0) {
PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN + entry->derCrl.len],
entry->url, nnlen);
}
return(SECSuccess);
loser:
return(SECFailure);
}
static SECStatus
DecodeDBCrlEntry(certDBEntryRevocation *entry, SECItem *dbentry)
{
unsigned int urlLen;
int lenDiff;
/* is record long enough for header? */
if ( dbentry->len < DB_CRL_ENTRY_HEADER_LEN ) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* is database entry correct length? */
entry->derCrl.len = ( ( dbentry->data[0] << 8 ) | dbentry->data[1] );
urlLen = ( ( dbentry->data[2] << 8 ) | dbentry->data[3] );
lenDiff = dbentry->len -
(entry->derCrl.len + urlLen + DB_CRL_ENTRY_HEADER_LEN);
if (lenDiff) {
if (lenDiff < 0 || (lenDiff & 0xffff) != 0) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* CRL entry is greater than 64 K. Hack to make this continue to work */
entry->derCrl.len += lenDiff;
}
/* copy the der CRL */
entry->derCrl.data = (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
entry->derCrl.len);
if ( entry->derCrl.data == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->derCrl.data, &dbentry->data[DB_CRL_ENTRY_HEADER_LEN],
entry->derCrl.len);
/* copy the url */
entry->url = NULL;
if (urlLen != 0) {
entry->url = (char *)PORT_ArenaAlloc(entry->common.arena, urlLen);
if ( entry->url == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->url,
&dbentry->data[DB_CRL_ENTRY_HEADER_LEN + entry->derCrl.len],
urlLen);
}
return(SECSuccess);
loser:
return(SECFailure);
}
/*
* Create a new certDBEntryRevocation from existing data
*/
static certDBEntryRevocation *
NewDBCrlEntry(SECItem *derCrl, char * url, certDBEntryType crlType, int flags)
{
certDBEntryRevocation *entry;
PLArenaPool *arena = NULL;
int nnlen;
arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
if ( !arena ) {
goto loser;
}
entry = PORT_ArenaZNew(arena, certDBEntryRevocation);
if ( entry == NULL ) {
goto loser;
}
/* fill in the dbRevolcation */
entry->common.arena = arena;
entry->common.type = crlType;
entry->common.version = CERT_DB_FILE_VERSION;
entry->common.flags = flags;
entry->derCrl.data = (unsigned char *)PORT_ArenaAlloc(arena, derCrl->len);
if ( !entry->derCrl.data ) {
goto loser;
}
if (url) {
nnlen = PORT_Strlen(url) + 1;
entry->url = (char *)PORT_ArenaAlloc(arena, nnlen);
if ( !entry->url ) {
goto loser;
}
PORT_Memcpy(entry->url, url, nnlen);
} else {
entry->url = NULL;
}
entry->derCrl.len = derCrl->len;
PORT_Memcpy(entry->derCrl.data, derCrl->data, derCrl->len);
return(entry);
loser:
/* allocation error, free arena and return */
if ( arena ) {
PORT_FreeArena(arena, PR_FALSE);
}
PORT_SetError(SEC_ERROR_NO_MEMORY);
return(0);
}
static SECStatus
WriteDBCrlEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryRevocation *entry,
SECItem *crlKey )
{
SECItem dbkey;
PLArenaPool *tmparena = NULL;
SECItem encodedEntry;
SECStatus rv;
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( tmparena == NULL ) {
goto loser;
}
rv = EncodeDBCrlEntry(entry, tmparena, &encodedEntry);
if ( rv == SECFailure ) {
goto loser;
}
rv = EncodeDBGenericKey(crlKey, tmparena, &dbkey, entry->common.type);
if ( rv == SECFailure ) {
goto loser;
}
/* now write it to the database */
rv = WriteDBEntry(handle, &entry->common, &dbkey, &encodedEntry);
if ( rv != SECSuccess ) {
goto loser;
}
PORT_FreeArena(tmparena, PR_FALSE);
return(SECSuccess);
loser:
if ( tmparena ) {
PORT_FreeArena(tmparena, PR_FALSE);
}
return(SECFailure);
}
/*
* delete a crl entry
*/
static SECStatus
DeleteDBCrlEntry(NSSLOWCERTCertDBHandle *handle, const SECItem *crlKey,
certDBEntryType crlType)
{
SECItem dbkey;
PLArenaPool *arena = NULL;
SECStatus rv;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL ) {
goto loser;
}
rv = EncodeDBGenericKey(crlKey, arena, &dbkey, crlType);
if ( rv != SECSuccess ) {
goto loser;
}
rv = DeleteDBEntry(handle, crlType, &dbkey);
if ( rv == SECFailure ) {
goto loser;
}
PORT_FreeArena(arena, PR_FALSE);
return(SECSuccess);
loser:
if ( arena ) {
PORT_FreeArena(arena, PR_FALSE);
}
return(SECFailure);
}
/*
* Read a certificate entry
*/
static certDBEntryRevocation *
ReadDBCrlEntry(NSSLOWCERTCertDBHandle *handle, SECItem *certKey,
certDBEntryType crlType)
{
PLArenaPool *arena = NULL;
PLArenaPool *tmparena = NULL;
certDBEntryRevocation *entry;
SECItem dbkey;
SECItem dbentry;
SECStatus rv;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( tmparena == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry = (certDBEntryRevocation *)
PORT_ArenaAlloc(arena, sizeof(certDBEntryRevocation));
if ( entry == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry->common.arena = arena;
entry->common.type = crlType;
rv = EncodeDBGenericKey(certKey, tmparena, &dbkey, crlType);
if ( rv != SECSuccess ) {
goto loser;
}
rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, NULL);
if ( rv == SECFailure ) {
goto loser;
}
rv = DecodeDBCrlEntry(entry, &dbentry);
if ( rv != SECSuccess ) {
goto loser;
}
PORT_FreeArena(tmparena, PR_FALSE);
return(entry);
loser:
if ( tmparena ) {
PORT_FreeArena(tmparena, PR_FALSE);
}
if ( arena ) {
PORT_FreeArena(arena, PR_FALSE);
}
return(NULL);
}
void
nsslowcert_DestroyDBEntry(certDBEntry *entry)
{
DestroyDBEntry(entry);
return;
}
/*
* Encode a database nickname record
*/
static SECStatus
EncodeDBNicknameEntry(certDBEntryNickname *entry, PLArenaPool *arena,
SECItem *dbitem)
{
unsigned char *buf;
/* allocate space for encoded database record, including space
* for low level header
*/
dbitem->len = entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN +
SEC_DB_ENTRY_HEADER_LEN;
dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
if ( dbitem->data == NULL) {
goto loser;
}
/* fill in database record */
buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
buf[0] = (PRUint8)( entry->subjectName.len >> 8 );
buf[1] = (PRUint8)( entry->subjectName.len );
PORT_Memcpy(&buf[DB_NICKNAME_ENTRY_HEADER_LEN], entry->subjectName.data,
entry->subjectName.len);
return(SECSuccess);
loser:
return(SECFailure);
}
/*
* Encode a database key for a nickname record
*/
static SECStatus
EncodeDBNicknameKey(char *nickname, PLArenaPool *arena,
SECItem *dbkey)
{
unsigned int nnlen;
nnlen = PORT_Strlen(nickname) + 1; /* includes null */
/* now get the database key and format it */
dbkey->len = nnlen + SEC_DB_KEY_HEADER_LEN;
if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
goto loser;
dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
if ( dbkey->data == NULL ) {
goto loser;
}
PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], nickname, nnlen);
dbkey->data[0] = certDBEntryTypeNickname;
return(SECSuccess);
loser:
return(SECFailure);
}
static SECStatus
DecodeDBNicknameEntry(certDBEntryNickname *entry, SECItem *dbentry,
char *nickname)
{
int lenDiff;
/* is record long enough for header? */
if ( dbentry->len < DB_NICKNAME_ENTRY_HEADER_LEN ) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* is database entry correct length? */
entry->subjectName.len = ( ( dbentry->data[0] << 8 ) | dbentry->data[1] );
lenDiff = dbentry->len -
(entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN);
if (lenDiff) {
if (lenDiff < 0 || (lenDiff & 0xffff) != 0 ) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* The entry size exceeded 64KB. Reconstruct the correct length. */
entry->subjectName.len += lenDiff;
}
/* copy the certkey */
entry->subjectName.data =
(unsigned char *)PORT_ArenaAlloc(entry->common.arena,
entry->subjectName.len);
if ( entry->subjectName.data == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->subjectName.data,
&dbentry->data[DB_NICKNAME_ENTRY_HEADER_LEN],
entry->subjectName.len);
entry->subjectName.type = siBuffer;
entry->nickname = (char *)PORT_ArenaAlloc(entry->common.arena,
PORT_Strlen(nickname)+1);
if ( entry->nickname ) {
PORT_Strcpy(entry->nickname, nickname);
}
return(SECSuccess);
loser:
return(SECFailure);
}
/*
* create a new nickname entry
*/
static certDBEntryNickname *
NewDBNicknameEntry(char *nickname, SECItem *subjectName, unsigned int flags)
{
PLArenaPool *arena = NULL;
certDBEntryNickname *entry;
int nnlen;
SECStatus rv;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry = (certDBEntryNickname *)PORT_ArenaAlloc(arena,
sizeof(certDBEntryNickname));
if ( entry == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/* init common fields */
entry->common.arena = arena;
entry->common.type = certDBEntryTypeNickname;
entry->common.version = CERT_DB_FILE_VERSION;
entry->common.flags = flags;
/* copy the nickname */
nnlen = PORT_Strlen(nickname) + 1;
entry->nickname = (char*)PORT_ArenaAlloc(arena, nnlen);
if ( entry->nickname == NULL ) {
goto loser;
}
PORT_Memcpy(entry->nickname, nickname, nnlen);
rv = SECITEM_CopyItem(arena, &entry->subjectName, subjectName);
if ( rv != SECSuccess ) {
goto loser;
}
return(entry);
loser:
if ( arena ) {
PORT_FreeArena(arena, PR_FALSE);
}
return(NULL);
}
/*
* delete a nickname entry
*/
static SECStatus
DeleteDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, char *nickname)
{
PLArenaPool *arena = NULL;
SECStatus rv;
SECItem dbkey;
if ( nickname == NULL ) {
return(SECSuccess);
}
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL ) {
goto loser;
}
rv = EncodeDBNicknameKey(nickname, arena, &dbkey);
if ( rv != SECSuccess ) {
goto loser;
}
rv = DeleteDBEntry(handle, certDBEntryTypeNickname, &dbkey);
if ( rv == SECFailure ) {
goto loser;
}
PORT_FreeArena(arena, PR_FALSE);
return(SECSuccess);
loser:
if ( arena ) {
PORT_FreeArena(arena, PR_FALSE);
}
return(SECFailure);
}
/*
* Read a nickname entry
*/
static certDBEntryNickname *
ReadDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, char *nickname)
{
PLArenaPool *arena = NULL;
PLArenaPool *tmparena = NULL;
certDBEntryNickname *entry;
SECItem dbkey;
SECItem dbentry;
SECStatus rv;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( tmparena == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry = (certDBEntryNickname *)PORT_ArenaAlloc(arena,
sizeof(certDBEntryNickname));
if ( entry == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry->common.arena = arena;
entry->common.type = certDBEntryTypeNickname;
rv = EncodeDBNicknameKey(nickname, tmparena, &dbkey);
if ( rv != SECSuccess ) {
goto loser;
}
rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
if ( rv == SECFailure ) {
goto loser;
}
/* is record long enough for header? */
if ( dbentry.len < DB_NICKNAME_ENTRY_HEADER_LEN ) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
rv = DecodeDBNicknameEntry(entry, &dbentry, nickname);
if ( rv != SECSuccess ) {
goto loser;
}
PORT_FreeArena(tmparena, PR_FALSE);
return(entry);
loser:
if ( tmparena ) {
PORT_FreeArena(tmparena, PR_FALSE);
}
if ( arena ) {
PORT_FreeArena(arena, PR_FALSE);
}
return(NULL);
}
/*
* Encode a nickname entry into byte stream suitable for
* the database
*/
static SECStatus
WriteDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryNickname *entry)
{
SECItem dbitem, dbkey;
PLArenaPool *tmparena = NULL;
SECStatus rv;
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( tmparena == NULL ) {
goto loser;
}
rv = EncodeDBNicknameEntry(entry, tmparena, &dbitem);
if ( rv != SECSuccess ) {
goto loser;
}
rv = EncodeDBNicknameKey(entry->nickname, tmparena, &dbkey);
if ( rv != SECSuccess ) {
goto loser;
}
/* now write it to the database */
rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
if ( rv != SECSuccess ) {
goto loser;
}
PORT_FreeArena(tmparena, PR_FALSE);
return(SECSuccess);
loser:
if ( tmparena ) {
PORT_FreeArena(tmparena, PR_FALSE);
}
return(SECFailure);
}
static SECStatus
EncodeDBSMimeEntry(certDBEntrySMime *entry, PLArenaPool *arena,
SECItem *dbitem)
{
unsigned char *buf;
/* allocate space for encoded database record, including space
* for low level header
*/
dbitem->len = entry->subjectName.len + entry->smimeOptions.len +
entry->optionsDate.len +
DB_SMIME_ENTRY_HEADER_LEN + SEC_DB_ENTRY_HEADER_LEN;
dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
if ( dbitem->data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/* fill in database record */
buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
buf[0] = (PRUint8)( entry->subjectName.len >> 8 );
buf[1] = (PRUint8)( entry->subjectName.len );
buf[2] = (PRUint8)( entry->smimeOptions.len >> 8 );
buf[3] = (PRUint8)( entry->smimeOptions.len );
buf[4] = (PRUint8)( entry->optionsDate.len >> 8 );
buf[5] = (PRUint8)( entry->optionsDate.len );
/* if no smime options, then there should not be an options date either */
PORT_Assert( ! ( ( entry->smimeOptions.len == 0 ) &&
( entry->optionsDate.len != 0 ) ) );
PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN], entry->subjectName.data,
entry->subjectName.len);
if ( entry->smimeOptions.len ) {
PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN+entry->subjectName.len],
entry->smimeOptions.data,
entry->smimeOptions.len);
PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN + entry->subjectName.len +
entry->smimeOptions.len],
entry->optionsDate.data,
entry->optionsDate.len);
}
return(SECSuccess);
loser:
return(SECFailure);
}
/*
* Encode a database key for a SMIME record
*/
static SECStatus
EncodeDBSMimeKey(char *emailAddr, PLArenaPool *arena,
SECItem *dbkey)
{
unsigned int addrlen;
addrlen = PORT_Strlen(emailAddr) + 1; /* includes null */
/* now get the database key and format it */
dbkey->len = addrlen + SEC_DB_KEY_HEADER_LEN;
if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
goto loser;
dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
if ( dbkey->data == NULL ) {
goto loser;
}
PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], emailAddr, addrlen);
dbkey->data[0] = certDBEntryTypeSMimeProfile;
return(SECSuccess);
loser:
return(SECFailure);
}
/*
* Decode a database SMIME record
*/
static SECStatus
DecodeDBSMimeEntry(certDBEntrySMime *entry, SECItem *dbentry, char *emailAddr)
{
int lenDiff;
/* is record long enough for header? */
if ( dbentry->len < DB_SMIME_ENTRY_HEADER_LEN ) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* is database entry correct length? */
entry->subjectName.len = (( dbentry->data[0] << 8 ) | dbentry->data[1] );
entry->smimeOptions.len = (( dbentry->data[2] << 8 ) | dbentry->data[3] );
entry->optionsDate.len = (( dbentry->data[4] << 8 ) | dbentry->data[5] );
lenDiff = dbentry->len - (entry->subjectName.len +
entry->smimeOptions.len +
entry->optionsDate.len +
DB_SMIME_ENTRY_HEADER_LEN);
if (lenDiff) {
if (lenDiff < 0 || (lenDiff & 0xffff) != 0 ) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* The entry size exceeded 64KB. Reconstruct the correct length. */
entry->subjectName.len += lenDiff;
}
/* copy the subject name */
entry->subjectName.data =
(unsigned char *)PORT_ArenaAlloc(entry->common.arena,
entry->subjectName.len);
if ( entry->subjectName.data == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->subjectName.data,
&dbentry->data[DB_SMIME_ENTRY_HEADER_LEN],
entry->subjectName.len);
/* copy the smime options */
if ( entry->smimeOptions.len ) {
entry->smimeOptions.data =
(unsigned char *)PORT_ArenaAlloc(entry->common.arena,
entry->smimeOptions.len);
if ( entry->smimeOptions.data == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->smimeOptions.data,
&dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
entry->subjectName.len],
entry->smimeOptions.len);
}
if ( entry->optionsDate.len ) {
entry->optionsDate.data =
(unsigned char *)PORT_ArenaAlloc(entry->common.arena,
entry->optionsDate.len);
if ( entry->optionsDate.data == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->optionsDate.data,
&dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
entry->subjectName.len +
entry->smimeOptions.len],
entry->optionsDate.len);
}
/* both options and options date must either exist or not exist */
if ( ( ( entry->optionsDate.len == 0 ) ||
( entry->smimeOptions.len == 0 ) ) &&
entry->smimeOptions.len != entry->optionsDate.len ) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
entry->emailAddr = (char *)PORT_ArenaAlloc(entry->common.arena,
PORT_Strlen(emailAddr)+1);
if ( entry->emailAddr ) {
PORT_Strcpy(entry->emailAddr, emailAddr);
}
return(SECSuccess);
loser:
return(SECFailure);
}
/*
* create a new SMIME entry
*/
static certDBEntrySMime *
NewDBSMimeEntry(char *emailAddr, SECItem *subjectName, SECItem *smimeOptions,
SECItem *optionsDate, unsigned int flags)
{
PLArenaPool *arena = NULL;
certDBEntrySMime *entry;
int addrlen;
SECStatus rv;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry = (certDBEntrySMime *)PORT_ArenaAlloc(arena,
sizeof(certDBEntrySMime));
if ( entry == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/* init common fields */
entry->common.arena = arena;
entry->common.type = certDBEntryTypeSMimeProfile;
entry->common.version = CERT_DB_FILE_VERSION;
entry->common.flags = flags;
/* copy the email addr */
addrlen = PORT_Strlen(emailAddr) + 1;
entry->emailAddr = (char*)PORT_ArenaAlloc(arena, addrlen);
if ( entry->emailAddr == NULL ) {
goto loser;
}
PORT_Memcpy(entry->emailAddr, emailAddr, addrlen);
/* copy the subject name */
rv = SECITEM_CopyItem(arena, &entry->subjectName, subjectName);
if ( rv != SECSuccess ) {
goto loser;
}
/* copy the smime options */
if ( smimeOptions ) {
rv = SECITEM_CopyItem(arena, &entry->smimeOptions, smimeOptions);
if ( rv != SECSuccess ) {
goto loser;
}
} else {
PORT_Assert(optionsDate == NULL);
entry->smimeOptions.data = NULL;
entry->smimeOptions.len = 0;
}
/* copy the options date */
if ( optionsDate ) {
rv = SECITEM_CopyItem(arena, &entry->optionsDate, optionsDate);
if ( rv != SECSuccess ) {
goto loser;
}
} else {
PORT_Assert(smimeOptions == NULL);
entry->optionsDate.data = NULL;
entry->optionsDate.len = 0;
}
return(entry);
loser:
if ( arena ) {
PORT_FreeArena(arena, PR_FALSE);
}
return(NULL);
}
/*
* delete a SMIME entry
*/
static SECStatus
DeleteDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, char *emailAddr)
{
PLArenaPool *arena = NULL;
SECStatus rv;
SECItem dbkey;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL ) {
goto loser;
}
rv = EncodeDBSMimeKey(emailAddr, arena, &dbkey);
if ( rv != SECSuccess ) {
goto loser;
}
rv = DeleteDBEntry(handle, certDBEntryTypeSMimeProfile, &dbkey);
if ( rv == SECFailure ) {
goto loser;
}
PORT_FreeArena(arena, PR_FALSE);
return(SECSuccess);
loser:
if ( arena ) {
PORT_FreeArena(arena, PR_FALSE);
}
return(SECFailure);
}
/*
* Read a SMIME entry
*/
certDBEntrySMime *
nsslowcert_ReadDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, char *emailAddr)
{
PLArenaPool *arena = NULL;
PLArenaPool *tmparena = NULL;
certDBEntrySMime *entry;
SECItem dbkey;
SECItem dbentry;
SECStatus rv;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( tmparena == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry = (certDBEntrySMime *)PORT_ArenaAlloc(arena,
sizeof(certDBEntrySMime));
if ( entry == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry->common.arena = arena;
entry->common.type = certDBEntryTypeSMimeProfile;
rv = EncodeDBSMimeKey(emailAddr, tmparena, &dbkey);
if ( rv != SECSuccess ) {
goto loser;
}
rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
if ( rv == SECFailure ) {
goto loser;
}
/* is record long enough for header? */
if ( dbentry.len < DB_SMIME_ENTRY_HEADER_LEN ) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
rv = DecodeDBSMimeEntry(entry, &dbentry, emailAddr);
if ( rv != SECSuccess ) {
goto loser;
}
PORT_FreeArena(tmparena, PR_FALSE);
return(entry);
loser:
if ( tmparena ) {
PORT_FreeArena(tmparena, PR_FALSE);
}
if ( arena ) {
PORT_FreeArena(arena, PR_FALSE);
}
return(NULL);
}
/*
* Encode a SMIME entry into byte stream suitable for
* the database
*/
static SECStatus
WriteDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, certDBEntrySMime *entry)
{
SECItem dbitem, dbkey;
PLArenaPool *tmparena = NULL;
SECStatus rv;
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( tmparena == NULL ) {
goto loser;
}
rv = EncodeDBSMimeEntry(entry, tmparena, &dbitem);
if ( rv != SECSuccess ) {
goto loser;
}
rv = EncodeDBSMimeKey(entry->emailAddr, tmparena, &dbkey);
if ( rv != SECSuccess ) {
goto loser;
}
/* now write it to the database */
rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
if ( rv != SECSuccess ) {
goto loser;
}
PORT_FreeArena(tmparena, PR_FALSE);
return(SECSuccess);
loser:
if ( tmparena ) {
PORT_FreeArena(tmparena, PR_FALSE);
}
return(SECFailure);
}
/*
* Encode a database subject record
*/
static SECStatus
EncodeDBSubjectEntry(certDBEntrySubject *entry, PLArenaPool *arena,
SECItem *dbitem)
{
unsigned char *buf;
int len;
unsigned int ncerts;
unsigned int i;
unsigned char *tmpbuf;
unsigned int nnlen = 0;
unsigned int eaddrslen = 0;
int keyidoff;
SECItem *certKeys = entry->certKeys;
SECItem *keyIDs = entry->keyIDs;;
if ( entry->nickname ) {
nnlen = PORT_Strlen(entry->nickname) + 1;
}
if ( entry->emailAddrs ) {
eaddrslen = 2;
for (i=0; i < entry->nemailAddrs; i++) {
eaddrslen += PORT_Strlen(entry->emailAddrs[i]) + 1 + 2;
}
}
ncerts = entry->ncerts;
/* compute the length of the entry */
keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen ;
len = keyidoff + (4 * ncerts) + eaddrslen;
for ( i = 0; i < ncerts; i++ ) {
if (keyIDs[i].len > 0xffff ||
(certKeys[i].len > 0xffff)) {
PORT_SetError(SEC_ERROR_INPUT_LEN);
goto loser;
}
len += certKeys[i].len;
len += keyIDs[i].len;
}
/* allocate space for encoded database record, including space
* for low level header
*/
dbitem->len = len + SEC_DB_ENTRY_HEADER_LEN;
dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
if ( dbitem->data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/* fill in database record */
buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
buf[0] = (PRUint8)( ncerts >> 8 );
buf[1] = (PRUint8)( ncerts );
buf[2] = (PRUint8)( nnlen >> 8 );
buf[3] = (PRUint8)( nnlen );
/* v7 email field is NULL in v8 */
buf[4] = 0;
buf[5] = 0;
PORT_Memcpy(&buf[DB_SUBJECT_ENTRY_HEADER_LEN], entry->nickname, nnlen);
tmpbuf = &buf[keyidoff];
for ( i = 0; i < ncerts; i++ ) {
tmpbuf[0] = (PRUint8)( certKeys[i].len >> 8 );
tmpbuf[1] = (PRUint8)( certKeys[i].len );
tmpbuf += 2;
}
for ( i = 0; i < ncerts; i++ ) {
tmpbuf[0] = (PRUint8)( keyIDs[i].len >> 8 );
tmpbuf[1] = (PRUint8)( keyIDs[i].len );
tmpbuf += 2;
}
for ( i = 0; i < ncerts; i++ ) {
PORT_Memcpy(tmpbuf, certKeys[i].data, certKeys[i].len);
tmpbuf += certKeys[i].len;
}
for ( i = 0; i < ncerts; i++ ) {
PORT_Memcpy(tmpbuf, keyIDs[i].data, keyIDs[i].len);
tmpbuf += keyIDs[i].len;
}
if (entry->emailAddrs) {
tmpbuf[0] = (PRUint8)( entry->nemailAddrs >> 8 );
tmpbuf[1] = (PRUint8)( entry->nemailAddrs );
tmpbuf += 2;
for (i=0; i < entry->nemailAddrs; i++) {
int nameLen = PORT_Strlen(entry->emailAddrs[i]) + 1;
tmpbuf[0] = (PRUint8)( nameLen >> 8 );
tmpbuf[1] = (PRUint8)( nameLen );
tmpbuf += 2;
PORT_Memcpy(tmpbuf,entry->emailAddrs[i],nameLen);
tmpbuf +=nameLen;
}
}
PORT_Assert(tmpbuf == &buf[len]);
return(SECSuccess);
loser:
return(SECFailure);
}
/*
* Encode a database key for a subject record
*/
static SECStatus
EncodeDBSubjectKey(SECItem *derSubject, PLArenaPool *arena,
SECItem *dbkey)
{
dbkey->len = derSubject->len + SEC_DB_KEY_HEADER_LEN;
if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
goto loser;
dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
if ( dbkey->data == NULL ) {
goto loser;
}
PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], derSubject->data,
derSubject->len);
dbkey->data[0] = certDBEntryTypeSubject;
return(SECSuccess);
loser:
return(SECFailure);
}
static SECStatus
DecodeDBSubjectEntry(certDBEntrySubject *entry, SECItem *dbentry,
const SECItem *derSubject)
{
PLArenaPool *arena = entry->common.arena;
unsigned char *tmpbuf;
unsigned char *end;
void *mark = PORT_ArenaMark(arena);
unsigned int eaddrlen;
unsigned int i;
unsigned int keyidoff;
unsigned int len;
unsigned int ncerts = 0;
unsigned int nnlen;
SECStatus rv;
rv = SECITEM_CopyItem(arena, &entry->derSubject, derSubject);
if ( rv != SECSuccess ) {
goto loser;
}
/* is record long enough for header? */
if ( dbentry->len < DB_SUBJECT_ENTRY_HEADER_LEN ) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
entry->ncerts = ncerts = (( dbentry->data[0] << 8 ) | dbentry->data[1] );
nnlen = (( dbentry->data[2] << 8 ) | dbentry->data[3] );
eaddrlen = (( dbentry->data[4] << 8 ) | dbentry->data[5] );
keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen + eaddrlen;
len = keyidoff + (4 * ncerts);
if ( dbentry->len < len) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
entry->certKeys = PORT_ArenaNewArray(arena, SECItem, ncerts);
entry->keyIDs = PORT_ArenaNewArray(arena, SECItem, ncerts);
if ( ( entry->certKeys == NULL ) || ( entry->keyIDs == NULL ) ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
if ( nnlen > 1 ) { /* null terminator is stored */
entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
if ( entry->nickname == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->nickname,
&dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN],
nnlen);
} else {
entry->nickname = NULL;
}
/* if we have an old style email entry, there is only one */
entry->nemailAddrs = 0;
if ( eaddrlen > 1 ) { /* null terminator is stored */
entry->emailAddrs = PORT_ArenaNewArray(arena, char *, 2);
if ( entry->emailAddrs == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry->emailAddrs[0] = (char *)PORT_ArenaAlloc(arena, eaddrlen);
if ( entry->emailAddrs[0] == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->emailAddrs[0],
&dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN+nnlen],
eaddrlen);
entry->nemailAddrs = 1;
} else {
entry->emailAddrs = NULL;
}
/* collect the lengths of the certKeys and keyIDs, and total the
* overall length.
*/
tmpbuf = &dbentry->data[keyidoff];
for ( i = 0; i < ncerts; i++ ) {
unsigned int itemlen = ( tmpbuf[0] << 8 ) | tmpbuf[1];
entry->certKeys[i].len = itemlen;
len += itemlen;
tmpbuf += 2;
}
for ( i = 0; i < ncerts; i++ ) {
unsigned int itemlen = ( tmpbuf[0] << 8 ) | tmpbuf[1] ;
entry->keyIDs[i].len = itemlen;
len += itemlen;
tmpbuf += 2;
}
/* is encoded entry large enough ? */
if ( len > dbentry->len ){
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
for ( i = 0; i < ncerts; i++ ) {
unsigned int kLen = entry->certKeys[i].len;
entry->certKeys[i].data = (unsigned char *)PORT_ArenaAlloc(arena, kLen);
if ( entry->certKeys[i].data == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->certKeys[i].data, tmpbuf, kLen);
tmpbuf += kLen;
}
for ( i = 0; i < ncerts; i++ ) {
unsigned int iLen = entry->keyIDs[i].len;
entry->keyIDs[i].data = (unsigned char *)PORT_ArenaAlloc(arena, iLen);
if ( entry->keyIDs[i].data == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->keyIDs[i].data, tmpbuf, iLen);
tmpbuf += iLen;
}
end = dbentry->data + dbentry->len;
if ((eaddrlen == 0) && (end - tmpbuf > 1)) {
/* read in the additional email addresses */
entry->nemailAddrs = (((unsigned int)tmpbuf[0]) << 8) | tmpbuf[1];
tmpbuf += 2;
if (end - tmpbuf < 2 * (int)entry->nemailAddrs)
goto loser;
entry->emailAddrs = PORT_ArenaNewArray(arena, char *, entry->nemailAddrs);
if (entry->emailAddrs == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
for (i=0; i < entry->nemailAddrs; i++) {
int nameLen;
if (end - tmpbuf < 2) {
goto loser;
}
nameLen = (((int)tmpbuf[0]) << 8) | tmpbuf[1];
tmpbuf += 2;
if (end - tmpbuf < nameLen) {
goto loser;
}
entry->emailAddrs[i] = PORT_ArenaAlloc(arena,nameLen);
if (entry->emailAddrs == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->emailAddrs[i], tmpbuf, nameLen);
tmpbuf += nameLen;
}
if (tmpbuf != end)
goto loser;
}
PORT_ArenaUnmark(arena, mark);
return(SECSuccess);
loser:
PORT_ArenaRelease(arena, mark); /* discard above allocations */
return(SECFailure);
}
/*
* create a new subject entry with a single cert
*/
static certDBEntrySubject *
NewDBSubjectEntry(SECItem *derSubject, SECItem *certKey,
SECItem *keyID, char *nickname, char *emailAddr,
unsigned int flags)
{
PLArenaPool *arena = NULL;
certDBEntrySubject *entry;
SECStatus rv;
unsigned int nnlen;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry = (certDBEntrySubject *)PORT_ArenaAlloc(arena,
sizeof(certDBEntrySubject));
if ( entry == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/* init common fields */
entry->common.arena = arena;
entry->common.type = certDBEntryTypeSubject;
entry->common.version = CERT_DB_FILE_VERSION;
entry->common.flags = flags;
/* copy the subject */
rv = SECITEM_CopyItem(arena, &entry->derSubject, derSubject);
if ( rv != SECSuccess ) {
goto loser;
}
entry->ncerts = 1;
entry->nemailAddrs = 0;
/* copy nickname */
if ( nickname && ( *nickname != '\0' ) ) {
nnlen = PORT_Strlen(nickname) + 1;
entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
if ( entry->nickname == NULL ) {
goto loser;
}
PORT_Memcpy(entry->nickname, nickname, nnlen);
} else {
entry->nickname = NULL;
}
/* copy email addr */
if ( emailAddr && ( *emailAddr != '\0' ) ) {
emailAddr = nsslowcert_FixupEmailAddr(emailAddr);
if ( emailAddr == NULL ) {
entry->emailAddrs = NULL;
goto loser;
}
entry->emailAddrs = (char **)PORT_ArenaAlloc(arena, sizeof(char *));
if ( entry->emailAddrs == NULL ) {
PORT_Free(emailAddr);
goto loser;
}
entry->emailAddrs[0] = PORT_ArenaStrdup(arena,emailAddr);
if (entry->emailAddrs[0]) {
entry->nemailAddrs = 1;
}
PORT_Free(emailAddr);
} else {
entry->emailAddrs = NULL;
}
/* allocate space for certKeys and keyIDs */
entry->certKeys = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem));
entry->keyIDs = (SECItem *)PORT_ArenaAlloc(arena, sizeof(SECItem));
if ( ( entry->certKeys == NULL ) || ( entry->keyIDs == NULL ) ) {
goto loser;
}
/* copy the certKey and keyID */
rv = SECITEM_CopyItem(arena, &entry->certKeys[0], certKey);
if ( rv != SECSuccess ) {
goto loser;
}
rv = SECITEM_CopyItem(arena, &entry->keyIDs[0], keyID);
if ( rv != SECSuccess ) {
goto loser;
}
return(entry);
loser:
if ( arena ) {
PORT_FreeArena(arena, PR_FALSE);
}
return(NULL);
}
/*
* delete a subject entry
*/
static SECStatus
DeleteDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, SECItem *derSubject)
{
SECItem dbkey;
PLArenaPool *arena = NULL;
SECStatus rv;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL ) {
goto loser;
}
rv = EncodeDBSubjectKey(derSubject, arena, &dbkey);
if ( rv != SECSuccess ) {
goto loser;
}
rv = DeleteDBEntry(handle, certDBEntryTypeSubject, &dbkey);
if ( rv == SECFailure ) {
goto loser;
}
PORT_FreeArena(arena, PR_FALSE);
return(SECSuccess);
loser:
if ( arena ) {
PORT_FreeArena(arena, PR_FALSE);
}
return(SECFailure);
}
/*
* Read the subject entry
*/
static certDBEntrySubject *
ReadDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, SECItem *derSubject)
{
PLArenaPool *arena = NULL;
PLArenaPool *tmparena = NULL;
certDBEntrySubject *entry;
SECItem dbkey;
SECItem dbentry;
SECStatus rv;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( tmparena == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry = (certDBEntrySubject *)PORT_ArenaAlloc(arena,
sizeof(certDBEntrySubject));
if ( entry == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry->common.arena = arena;
entry->common.type = certDBEntryTypeSubject;
rv = EncodeDBSubjectKey(derSubject, tmparena, &dbkey);
if ( rv != SECSuccess ) {
goto loser;
}
rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
if ( rv == SECFailure ) {
goto loser;
}
rv = DecodeDBSubjectEntry(entry, &dbentry, derSubject);
if ( rv == SECFailure ) {
goto loser;
}
PORT_FreeArena(tmparena, PR_FALSE);
return(entry);
loser:
if ( tmparena ) {
PORT_FreeArena(tmparena, PR_FALSE);
}
if ( arena ) {
PORT_FreeArena(arena, PR_FALSE);
}
return(NULL);
}
/*
* Encode a subject name entry into byte stream suitable for
* the database
*/
static SECStatus
WriteDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, certDBEntrySubject *entry)
{
SECItem dbitem, dbkey;
PLArenaPool *tmparena = NULL;
SECStatus rv;
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( tmparena == NULL ) {
goto loser;
}
rv = EncodeDBSubjectEntry(entry, tmparena, &dbitem);
if ( rv != SECSuccess ) {
goto loser;
}
rv = EncodeDBSubjectKey(&entry->derSubject, tmparena, &dbkey);
if ( rv != SECSuccess ) {
goto loser;
}
/* now write it to the database */
rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
if ( rv != SECSuccess ) {
goto loser;
}
PORT_FreeArena(tmparena, PR_FALSE);
return(SECSuccess);
loser:
if ( tmparena ) {
PORT_FreeArena(tmparena, PR_FALSE);
}
return(SECFailure);
}
typedef enum { nsslowcert_remove, nsslowcert_add } nsslowcertUpdateType;
static SECStatus
nsslowcert_UpdateSubjectEmailAddr(NSSLOWCERTCertDBHandle *dbhandle,
SECItem *derSubject, char *emailAddr, nsslowcertUpdateType updateType)
{
certDBEntrySubject *entry = NULL;
int index = -1, i;
SECStatus rv;
if (emailAddr) {
emailAddr = nsslowcert_FixupEmailAddr(emailAddr);
if (emailAddr == NULL) {
return SECFailure;
}
} else {
return SECSuccess;
}
entry = ReadDBSubjectEntry(dbhandle,derSubject);
if (entry == NULL) {
rv = SECFailure;
goto done;
}
for (i=0; i < (int)(entry->nemailAddrs); i++) {
if (PORT_Strcmp(entry->emailAddrs[i],emailAddr) == 0) {
index = i;
}
}
if (updateType == nsslowcert_remove) {
if (index == -1) {
rv = SECSuccess;
goto done;
}
entry->nemailAddrs--;
for (i=index; i < (int)(entry->nemailAddrs); i++) {
entry->emailAddrs[i] = entry->emailAddrs[i+1];
}
} else {
char **newAddrs = NULL;
if (index != -1) {
rv = SECSuccess;
goto done;
}
newAddrs = (char **)PORT_ArenaAlloc(entry->common.arena,
(entry->nemailAddrs+1)* sizeof(char *));
if (!newAddrs) {
rv = SECFailure;
goto done;
}
for (i=0; i < (int)(entry->nemailAddrs); i++) {
newAddrs[i] = entry->emailAddrs[i];
}
newAddrs[entry->nemailAddrs] =
PORT_ArenaStrdup(entry->common.arena,emailAddr);
if (!newAddrs[entry->nemailAddrs]) {
rv = SECFailure;
goto done;
}
entry->emailAddrs = newAddrs;
entry->nemailAddrs++;
}
/* delete the subject entry */
DeleteDBSubjectEntry(dbhandle, derSubject);
/* write the new one */
rv = WriteDBSubjectEntry(dbhandle, entry);
done:
if (entry) DestroyDBEntry((certDBEntry *)entry);
if (emailAddr) PORT_Free(emailAddr);
return rv;
}
/*
* writes a nickname to an existing subject entry that does not currently
* have one
*/
static SECStatus
AddNicknameToSubject(NSSLOWCERTCertDBHandle *dbhandle,
NSSLOWCERTCertificate *cert, char *nickname)
{
certDBEntrySubject *entry;
SECStatus rv;
if ( nickname == NULL ) {
return(SECFailure);
}
entry = ReadDBSubjectEntry(dbhandle,&cert->derSubject);
PORT_Assert(entry != NULL);
if ( entry == NULL ) {
goto loser;
}
PORT_Assert(entry->nickname == NULL);
if ( entry->nickname != NULL ) {
goto loser;
}
entry->nickname = PORT_ArenaStrdup(entry->common.arena, nickname);
if ( entry->nickname == NULL ) {
goto loser;
}
/* delete the subject entry */
DeleteDBSubjectEntry(dbhandle, &cert->derSubject);
/* write the new one */
rv = WriteDBSubjectEntry(dbhandle, entry);
if ( rv != SECSuccess ) {
goto loser;
}
return(SECSuccess);
loser:
return(SECFailure);
}
/*
* create a new version entry
*/
static certDBEntryVersion *
NewDBVersionEntry(unsigned int flags)
{
PLArenaPool *arena = NULL;
certDBEntryVersion *entry;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry = (certDBEntryVersion *)PORT_ArenaAlloc(arena,
sizeof(certDBEntryVersion));
if ( entry == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry->common.arena = arena;
entry->common.type = certDBEntryTypeVersion;
entry->common.version = CERT_DB_FILE_VERSION;
entry->common.flags = flags;
return(entry);
loser:
if ( arena ) {
PORT_FreeArena(arena, PR_FALSE);
}
return(NULL);
}
/*
* Read the version entry
*/
static certDBEntryVersion *
ReadDBVersionEntry(NSSLOWCERTCertDBHandle *handle)
{
PLArenaPool *arena = NULL;
PLArenaPool *tmparena = NULL;
certDBEntryVersion *entry;
SECItem dbkey;
SECItem dbentry;
SECStatus rv;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( tmparena == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry = PORT_ArenaZNew(arena, certDBEntryVersion);
if ( entry == NULL ) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry->common.arena = arena;
entry->common.type = certDBEntryTypeVersion;
/* now get the database key and format it */
dbkey.len = SEC_DB_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
dbkey.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbkey.len);
if ( dbkey.data == NULL ) {
goto loser;
}
PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_VERSION_KEY,
SEC_DB_VERSION_KEY_LEN);
rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
if (rv != SECSuccess) {
goto loser;
}
PORT_FreeArena(tmparena, PR_FALSE);
return(entry);
loser:
if ( tmparena ) {
PORT_FreeArena(tmparena, PR_FALSE);
}
if ( arena ) {
PORT_FreeArena(arena, PR_FALSE);
}
return(NULL);
}
/*
* Encode a version entry into byte stream suitable for
* the database
*/
static SECStatus
WriteDBVersionEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryVersion *entry)
{
SECItem dbitem, dbkey;
PLArenaPool *tmparena = NULL;
SECStatus rv;
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( tmparena == NULL ) {
goto loser;
}
/* allocate space for encoded database record, including space
* for low level header
*/
dbitem.len = SEC_DB_ENTRY_HEADER_LEN;
dbitem.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbitem.len);
if ( dbitem.data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/* now get the database key and format it */
dbkey.len = SEC_DB_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
dbkey.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbkey.len);
if ( dbkey.data == NULL ) {
goto loser;
}
PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_VERSION_KEY,
SEC_DB_VERSION_KEY_LEN);
/* now write it to the database */
rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
if ( rv != SECSuccess ) {
goto loser;
}
PORT_FreeArena(tmparena, PR_FALSE);
return(SECSuccess);
loser:
if ( tmparena ) {
PORT_FreeArena(tmparena, PR_FALSE);
}
return(SECFailure);
}
/*
* cert is no longer a perm cert, but will remain a temp cert
*/
static SECStatus
RemovePermSubjectNode(NSSLOWCERTCertificate *cert)
{
certDBEntrySubject *entry;
unsigned int i;
SECStatus rv;
entry = ReadDBSubjectEntry(cert->dbhandle,&cert->derSubject);
if ( entry == NULL ) {
return(SECFailure);
}
PORT_Assert(entry->ncerts);
rv = SECFailure;
if ( entry->ncerts > 1 ) {
for ( i = 0; i < entry->ncerts; i++ ) {
if ( SECITEM_CompareItem(&entry->certKeys[i], &cert->certKey) ==
SECEqual ) {
/* copy rest of list forward one entry */
for ( i = i + 1; i < entry->ncerts; i++ ) {
entry->certKeys[i-1] = entry->certKeys[i];
entry->keyIDs[i-1] = entry->keyIDs[i];
}
entry->ncerts--;
DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
rv = WriteDBSubjectEntry(cert->dbhandle, entry);
break;
}
}
} else {
/* no entries left, delete the perm entry in the DB */
if ( entry->emailAddrs ) {
/* if the subject had an email record, then delete it too */
for (i=0; i < entry->nemailAddrs; i++) {
DeleteDBSMimeEntry(cert->dbhandle, entry->emailAddrs[i]);
}
}
if ( entry->nickname ) {
DeleteDBNicknameEntry(cert->dbhandle, entry->nickname);
}
DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
}
DestroyDBEntry((certDBEntry *)entry);
return(rv);
}
/*
* add a cert to the perm subject list
*/
static SECStatus
AddPermSubjectNode(certDBEntrySubject *entry, NSSLOWCERTCertificate *cert,
char *nickname)
{
SECItem *newCertKeys, *newKeyIDs;
unsigned int i, new_i;
SECStatus rv;
unsigned int ncerts;
PORT_Assert(entry);
ncerts = entry->ncerts;
if ( nickname && entry->nickname ) {
/* nicknames must be the same */
PORT_Assert(PORT_Strcmp(nickname, entry->nickname) == 0);
}
if ( ( entry->nickname == NULL ) && ( nickname != NULL ) ) {
/* copy nickname into the entry */
entry->nickname = PORT_ArenaStrdup(entry->common.arena, nickname);
if ( entry->nickname == NULL ) {
return(SECFailure);
}
}
/* a DB entry already exists, so add this cert */
newCertKeys = PORT_ArenaZNewArray(entry->common.arena, SECItem, ncerts + 1);
newKeyIDs = PORT_ArenaZNewArray(entry->common.arena, SECItem, ncerts + 1);
if ( ( newCertKeys == NULL ) || ( newKeyIDs == NULL ) ) {
return(SECFailure);
}
/* Step 1: copy certs older than "cert" into new entry. */
for ( i = 0, new_i=0; i < ncerts; i++ ) {
NSSLOWCERTCertificate *cmpcert;
PRBool isNewer;
cmpcert = nsslowcert_FindCertByKey(cert->dbhandle,
&entry->certKeys[i]);
/* The entry has been corrupted, remove it from the list */
if (!cmpcert) {
continue;
}
isNewer = nsslowcert_IsNewer(cert, cmpcert);
nsslowcert_DestroyCertificate(cmpcert);
if ( isNewer )
break;
/* copy this cert entry */
newCertKeys[new_i] = entry->certKeys[i];
newKeyIDs[new_i] = entry->keyIDs[i];
new_i++;
}
/* Step 2: Add "cert" to the entry. */
rv = SECITEM_CopyItem(entry->common.arena, &newCertKeys[new_i],
&cert->certKey);
if ( rv != SECSuccess ) {
return(SECFailure);
}
rv = SECITEM_CopyItem(entry->common.arena, &newKeyIDs[new_i],
&cert->subjectKeyID);
if ( rv != SECSuccess ) {
return(SECFailure);
}
new_i++;
/* Step 3: copy remaining certs (if any) from old entry to new. */
for ( ; i < ncerts; i++ ,new_i++) {
newCertKeys[new_i] = entry->certKeys[i];
newKeyIDs[new_i] = entry->keyIDs[i];
}
/* update certKeys and keyIDs */
entry->certKeys = newCertKeys;
entry->keyIDs = newKeyIDs;
/* set new count value */
entry->ncerts = new_i;
DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
rv = WriteDBSubjectEntry(cert->dbhandle, entry);
return(rv);
}
SECStatus
nsslowcert_TraversePermCertsForSubject(NSSLOWCERTCertDBHandle *handle,
SECItem *derSubject,
NSSLOWCERTCertCallback cb, void *cbarg)
{
certDBEntrySubject *entry;
unsigned int i;
NSSLOWCERTCertificate *cert;
SECStatus rv = SECSuccess;
entry = ReadDBSubjectEntry(handle, derSubject);
if ( entry == NULL ) {
return(SECFailure);
}
for( i = 0; i < entry->ncerts; i++ ) {
cert = nsslowcert_FindCertByKey(handle, &entry->certKeys[i]);
if (!cert) {
continue;
}
rv = (* cb)(cert, cbarg);
nsslowcert_DestroyCertificate(cert);
if ( rv == SECFailure ) {
break;
}
}
DestroyDBEntry((certDBEntry *)entry);
return(rv);
}
int
nsslowcert_NumPermCertsForSubject(NSSLOWCERTCertDBHandle *handle,
SECItem *derSubject)
{
certDBEntrySubject *entry;
int ret;
entry = ReadDBSubjectEntry(handle, derSubject);
if ( entry == NULL ) {
return(SECFailure);
}
ret = entry->ncerts;
DestroyDBEntry((certDBEntry *)entry);
return(ret);
}
SECStatus
nsslowcert_TraversePermCertsForNickname(NSSLOWCERTCertDBHandle *handle,
char *nickname, NSSLOWCERTCertCallback cb, void *cbarg)
{
certDBEntryNickname *nnentry = NULL;
certDBEntrySMime *smentry = NULL;
SECStatus rv;
SECItem *derSubject = NULL;
nnentry = ReadDBNicknameEntry(handle, nickname);
if ( nnentry ) {
derSubject = &nnentry->subjectName;
} else {
smentry = nsslowcert_ReadDBSMimeEntry(handle, nickname);
if ( smentry ) {
derSubject = &smentry->subjectName;
}
}
if ( derSubject ) {
rv = nsslowcert_TraversePermCertsForSubject(handle, derSubject,
cb, cbarg);
} else {
rv = SECFailure;
}
if ( nnentry ) {
DestroyDBEntry((certDBEntry *)nnentry);
}
if ( smentry ) {
DestroyDBEntry((certDBEntry *)smentry);
}
return(rv);
}
int
nsslowcert_NumPermCertsForNickname(NSSLOWCERTCertDBHandle *handle,
char *nickname)
{
certDBEntryNickname *entry;
int ret;
entry = ReadDBNicknameEntry(handle, nickname);
if ( entry ) {
ret = nsslowcert_NumPermCertsForSubject(handle, &entry->subjectName);
DestroyDBEntry((certDBEntry *)entry);
} else {
ret = 0;
}
return(ret);
}
/*
* add a nickname to a cert that doesn't have one
*/
static SECStatus
AddNicknameToPermCert(NSSLOWCERTCertDBHandle *dbhandle,
NSSLOWCERTCertificate *cert, char *nickname)
{
certDBEntryCert *entry;
int rv;
entry = cert->dbEntry;
PORT_Assert(entry != NULL);
if ( entry == NULL ) {
goto loser;
}
pkcs11_freeNickname(entry->nickname,entry->nicknameSpace);
entry->nickname = NULL;
entry->nickname = pkcs11_copyNickname(nickname,entry->nicknameSpace,
sizeof(entry->nicknameSpace));
rv = WriteDBCertEntry(dbhandle, entry);
if ( rv ) {
goto loser;
}
pkcs11_freeNickname(cert->nickname,cert->nicknameSpace);
cert->nickname = NULL;
cert->nickname = pkcs11_copyNickname(nickname,cert->nicknameSpace,
sizeof(cert->nicknameSpace));
return(SECSuccess);
loser:
return(SECFailure);
}
/*
* add a nickname to a cert that is already in the perm database, but doesn't
* have one yet (it is probably an e-mail cert).
*/
SECStatus
nsslowcert_AddPermNickname(NSSLOWCERTCertDBHandle *dbhandle,
NSSLOWCERTCertificate *cert, char *nickname)
{
SECStatus rv = SECFailure;
certDBEntrySubject *entry = NULL;
certDBEntryNickname *nicknameEntry = NULL;
nsslowcert_LockDB(dbhandle);
entry = ReadDBSubjectEntry(dbhandle, &cert->derSubject);
if (entry == NULL) goto loser;
if ( entry->nickname == NULL ) {
/* no nickname for subject */
rv = AddNicknameToSubject(dbhandle, cert, nickname);
if ( rv != SECSuccess ) {
goto loser;
}
rv = AddNicknameToPermCert(dbhandle, cert, nickname);
if ( rv != SECSuccess ) {
goto loser;
}
nicknameEntry = NewDBNicknameEntry(nickname, &cert->derSubject, 0);
if ( nicknameEntry == NULL ) {
goto loser;
}
rv = WriteDBNicknameEntry(dbhandle, nicknameEntry);
if ( rv != SECSuccess ) {
goto loser;
}
} else {
/* subject already has a nickname */
rv = AddNicknameToPermCert(dbhandle, cert, entry->nickname);
if ( rv != SECSuccess ) {
goto loser;
}
/* make sure nickname entry exists. If the database was corrupted,
* we may have lost the nickname entry. Add it back now */
nicknameEntry = ReadDBNicknameEntry(dbhandle, entry->nickname);
if (nicknameEntry == NULL ) {
nicknameEntry = NewDBNicknameEntry(entry->nickname,
&cert->derSubject, 0);
if ( nicknameEntry == NULL ) {
goto loser;
}
rv = WriteDBNicknameEntry(dbhandle, nicknameEntry);
if ( rv != SECSuccess ) {
goto loser;
}
}
}
rv = SECSuccess;
loser:
if (entry) {
DestroyDBEntry((certDBEntry *)entry);
}
if (nicknameEntry) {
DestroyDBEntry((certDBEntry *)nicknameEntry);
}
nsslowcert_UnlockDB(dbhandle);
return(rv);
}
static certDBEntryCert *
AddCertToPermDB(NSSLOWCERTCertDBHandle *handle, NSSLOWCERTCertificate *cert,
char *nickname, NSSLOWCERTCertTrust *trust)
{
certDBEntryCert *certEntry = NULL;
certDBEntryNickname *nicknameEntry = NULL;
certDBEntrySubject *subjectEntry = NULL;
int state = 0;
SECStatus rv;
PRBool donnentry = PR_FALSE;
if ( nickname ) {
donnentry = PR_TRUE;
}
subjectEntry = ReadDBSubjectEntry(handle, &cert->derSubject);
if ( subjectEntry && subjectEntry->nickname ) {
donnentry = PR_FALSE;
nickname = subjectEntry->nickname;
}
certEntry = NewDBCertEntry(&cert->derCert, nickname, trust, 0);
if ( certEntry == NULL ) {
goto loser;
}
if ( donnentry ) {
nicknameEntry = NewDBNicknameEntry(nickname, &cert->derSubject, 0);
if ( nicknameEntry == NULL ) {
goto loser;
}
}
rv = WriteDBCertEntry(handle, certEntry);
if ( rv != SECSuccess ) {
goto loser;
}
state = 1;
if ( nicknameEntry ) {
rv = WriteDBNicknameEntry(handle, nicknameEntry);
if ( rv != SECSuccess ) {
goto loser;
}
}
state = 2;
/* "Change" handles if necessary */
cert->dbhandle = handle;
/* add to or create new subject entry */
if ( subjectEntry ) {
/* REWRITE BASED ON SUBJECT ENTRY */
rv = AddPermSubjectNode(subjectEntry, cert, nickname);
if ( rv != SECSuccess ) {
goto loser;
}
} else {
/* make a new subject entry - this case is only used when updating
* an old version of the database. This is OK because the oldnickname
* db format didn't allow multiple certs with the same subject.
*/
/* where does subjectKeyID and certKey come from? */
subjectEntry = NewDBSubjectEntry(&cert->derSubject, &cert->certKey,
&cert->subjectKeyID, nickname,
NULL, 0);
if ( subjectEntry == NULL ) {
goto loser;
}
rv = WriteDBSubjectEntry(handle, subjectEntry);
if ( rv != SECSuccess ) {
goto loser;
}
}
state = 3;
if ( nicknameEntry ) {
DestroyDBEntry((certDBEntry *)nicknameEntry);
}
if ( subjectEntry ) {
DestroyDBEntry((certDBEntry *)subjectEntry);
}
return(certEntry);
loser:
/* don't leave partial entry in the database */
if ( state > 0 ) {
DeleteDBCertEntry(handle, &cert->certKey);
}
if ( ( state > 1 ) && donnentry ) {
DeleteDBNicknameEntry(handle, nickname);
}
if ( certEntry ) {
DestroyDBEntry((certDBEntry *)certEntry);
}
if ( nicknameEntry ) {
DestroyDBEntry((certDBEntry *)nicknameEntry);
}
if ( subjectEntry ) {
DestroyDBEntry((certDBEntry *)subjectEntry);
}
return(NULL);
}
/* forward declaration */
static SECStatus
UpdateV7DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb);
/*
* version 8 uses the same schema as version 7. The only differences are
* 1) version 8 db uses the blob shim to store data entries > 32k.
* 2) version 8 db sets the db block size to 32k.
* both of these are dealt with by the handle.
*/
static SECStatus
UpdateV8DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
{
return UpdateV7DB(handle,updatedb);
}
/*
* we could just blindly sequence through reading key data pairs and writing
* them back out, but some cert.db's have gotten quite large and may have some
* subtle corruption problems, so instead we cycle through the certs and
* CRL's and S/MIME profiles and rebuild our subject lists from those records.
*/
static SECStatus
UpdateV7DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
{
DBT key, data;
int ret;
NSSLOWCERTCertificate *cert;
PRBool isKRL = PR_FALSE;
certDBEntryType entryType;
SECItem dbEntry, dbKey;
certDBEntryRevocation crlEntry;
certDBEntryCert certEntry;
certDBEntrySMime smimeEntry;
SECStatus rv;
ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
if ( ret ) {
return(SECFailure);
}
do {
unsigned char *dataBuf = (unsigned char *)data.data;
unsigned char *keyBuf = (unsigned char *)key.data;
dbEntry.data = &dataBuf[SEC_DB_ENTRY_HEADER_LEN];
dbEntry.len = data.size - SEC_DB_ENTRY_HEADER_LEN;
entryType = (certDBEntryType) keyBuf[0];
dbKey.data = &keyBuf[SEC_DB_KEY_HEADER_LEN];
dbKey.len = key.size - SEC_DB_KEY_HEADER_LEN;
if ((dbEntry.len <= 0) || (dbKey.len <= 0)) {
continue;
}
switch (entryType) {
/* these entries will get regenerated as we read the
* rest of the data from the database */
case certDBEntryTypeVersion:
case certDBEntryTypeSubject:
case certDBEntryTypeContentVersion:
case certDBEntryTypeNickname:
/* smime profiles need entries created after the certs have
* been imported, loop over them in a second run */
case certDBEntryTypeSMimeProfile:
break;
case certDBEntryTypeCert:
/* decode Entry */
certEntry.common.version = (unsigned int)dataBuf[0];
certEntry.common.type = entryType;
certEntry.common.flags = (unsigned int)dataBuf[2];
rv = DecodeDBCertEntry(&certEntry,&dbEntry);
if (rv != SECSuccess) {
break;
}
/* should we check for existing duplicates? */
cert = nsslowcert_DecodeDERCertificate(&certEntry.derCert,
certEntry.nickname);
if (cert) {
nsslowcert_UpdatePermCert(handle, cert, certEntry.nickname,
&certEntry.trust);
nsslowcert_DestroyCertificate(cert);
}
/* free any data the decode may have allocated. */
pkcs11_freeStaticData(certEntry.derCert.data,
certEntry.derCertSpace);
pkcs11_freeNickname(certEntry.nickname, certEntry.nicknameSpace);
break;
case certDBEntryTypeKeyRevocation:
isKRL = PR_TRUE;
/* fall through */
case certDBEntryTypeRevocation:
crlEntry.common.version = (unsigned int)dataBuf[0];
crlEntry.common.type = entryType;
crlEntry.common.flags = (unsigned int)dataBuf[2];
crlEntry.common.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (crlEntry.common.arena == NULL) {
break;
}
rv = DecodeDBCrlEntry(&crlEntry,&dbEntry);
if (rv != SECSuccess) {
break;
}
nsslowcert_UpdateCrl(handle, &crlEntry.derCrl, &dbKey,
crlEntry.url, isKRL);
/* free data allocated by the decode */
PORT_FreeArena(crlEntry.common.arena, PR_FALSE);
crlEntry.common.arena = NULL;
break;
default:
break;
}
} while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
/* now loop again updating just the SMimeProfile. */
ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
if ( ret ) {
return(SECFailure);
}
do {
unsigned char *dataBuf = (unsigned char *)data.data;
unsigned char *keyBuf = (unsigned char *)key.data;
dbEntry.data = &dataBuf[SEC_DB_ENTRY_HEADER_LEN];
dbEntry.len = data.size - SEC_DB_ENTRY_HEADER_LEN;
entryType = (certDBEntryType) keyBuf[0];
if (entryType != certDBEntryTypeSMimeProfile) {
continue;
}
dbKey.data = &keyBuf[SEC_DB_KEY_HEADER_LEN];
dbKey.len = key.size - SEC_DB_KEY_HEADER_LEN;
if ((dbEntry.len <= 0) || (dbKey.len <= 0)) {
continue;
}
smimeEntry.common.version = (unsigned int)dataBuf[0];
smimeEntry.common.type = entryType;
smimeEntry.common.flags = (unsigned int)dataBuf[2];
smimeEntry.common.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
/* decode entry */
rv = DecodeDBSMimeEntry(&smimeEntry,&dbEntry,(char *)dbKey.data);
if (rv == SECSuccess) {
nsslowcert_UpdateSMimeProfile(handle, smimeEntry.emailAddr,
&smimeEntry.subjectName, &smimeEntry.smimeOptions,
&smimeEntry.optionsDate);
}
PORT_FreeArena(smimeEntry.common.arena, PR_FALSE);
smimeEntry.common.arena = NULL;
} while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
(* updatedb->close)(updatedb);
/* a database update is a good time to go back and verify the integrity of
* the keys and certs */
handle->dbVerify = PR_TRUE;
return(SECSuccess);
}
/*
* NOTE - Version 6 DB did not go out to the real world in a release,
* so we can remove this function in a later release.
*/
static SECStatus
UpdateV6DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
{
int ret;
DBT key, data;
unsigned char *buf, *tmpbuf = NULL;
certDBEntryType type;
certDBEntryNickname *nnEntry = NULL;
certDBEntrySubject *subjectEntry = NULL;
certDBEntrySMime *emailEntry = NULL;
char *nickname;
char *emailAddr;
/*
* Sequence through the old database and copy all of the entries
* to the new database. Subject name entries will have the new
* fields inserted into them (with zero length).
*/
ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
if ( ret ) {
return(SECFailure);
}
do {
buf = (unsigned char *)data.data;
if ( data.size >= 3 ) {
if ( buf[0] == 6 ) { /* version number */
type = (certDBEntryType)buf[1];
if ( type == certDBEntryTypeSubject ) {
/* expando subjecto entrieo */
tmpbuf = (unsigned char *)PORT_Alloc(data.size + 4);
if ( tmpbuf ) {
/* copy header stuff */
PORT_Memcpy(tmpbuf, buf, SEC_DB_ENTRY_HEADER_LEN + 2);
/* insert 4 more bytes of zero'd header */
PORT_Memset(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 2],
0, 4);
/* copy rest of the data */
PORT_Memcpy(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 6],
&buf[SEC_DB_ENTRY_HEADER_LEN + 2],
data.size - (SEC_DB_ENTRY_HEADER_LEN + 2));
data.data = (void *)tmpbuf;
data.size += 4;
buf = tmpbuf;
}
} else if ( type == certDBEntryTypeCert ) {
/* expando certo entrieo */
tmpbuf = (unsigned char *)PORT_Alloc(data.size + 3);
if ( tmpbuf ) {
/* copy header stuff */
PORT_Memcpy(tmpbuf, buf, SEC_DB_ENTRY_HEADER_LEN);
/* copy trust flage, setting msb's to 0 */
tmpbuf[SEC_DB_ENTRY_HEADER_LEN] = 0;
tmpbuf[SEC_DB_ENTRY_HEADER_LEN+1] =
buf[SEC_DB_ENTRY_HEADER_LEN];
tmpbuf[SEC_DB_ENTRY_HEADER_LEN+2] = 0;
tmpbuf[SEC_DB_ENTRY_HEADER_LEN+3] =
buf[SEC_DB_ENTRY_HEADER_LEN+1];
tmpbuf[SEC_DB_ENTRY_HEADER_LEN+4] = 0;
tmpbuf[SEC_DB_ENTRY_HEADER_LEN+5] =
buf[SEC_DB_ENTRY_HEADER_LEN+2];
/* copy rest of the data */
PORT_Memcpy(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 6],
&buf[SEC_DB_ENTRY_HEADER_LEN + 3],
data.size - (SEC_DB_ENTRY_HEADER_LEN + 3));
data.data = (void *)tmpbuf;
data.size += 3;
buf = tmpbuf;
}
}
/* update the record version number */
buf[0] = CERT_DB_FILE_VERSION;
/* copy to the new database */
ret = certdb_Put(handle->permCertDB, &key, &data, 0);
if ( tmpbuf ) {
PORT_Free(tmpbuf);
tmpbuf = NULL;
}
}
}
} while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
ret = certdb_Sync(handle->permCertDB, 0);
ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
if ( ret ) {
return(SECFailure);
}
do {
buf = (unsigned char *)data.data;
if ( data.size >= 3 ) {
if ( buf[0] == CERT_DB_FILE_VERSION ) { /* version number */
type = (certDBEntryType)buf[1];
if ( type == certDBEntryTypeNickname ) {
nickname = &((char *)key.data)[1];
/* get the matching nickname entry in the new DB */
nnEntry = ReadDBNicknameEntry(handle, nickname);
if ( nnEntry == NULL ) {
goto endloop;
}
/* find the subject entry pointed to by nickname */
subjectEntry = ReadDBSubjectEntry(handle,
&nnEntry->subjectName);
if ( subjectEntry == NULL ) {
goto endloop;
}
subjectEntry->nickname =
(char *)PORT_ArenaAlloc(subjectEntry->common.arena,
key.size - 1);
if ( subjectEntry->nickname ) {
PORT_Memcpy(subjectEntry->nickname, nickname,
key.size - 1);
(void)WriteDBSubjectEntry(handle, subjectEntry);
}
} else if ( type == certDBEntryTypeSMimeProfile ) {
emailAddr = &((char *)key.data)[1];
/* get the matching smime entry in the new DB */
emailEntry = nsslowcert_ReadDBSMimeEntry(handle, emailAddr);
if ( emailEntry == NULL ) {
goto endloop;
}
/* find the subject entry pointed to by nickname */
subjectEntry = ReadDBSubjectEntry(handle,
&emailEntry->subjectName);
if ( subjectEntry == NULL ) {
goto endloop;
}
subjectEntry->emailAddrs = (char **)
PORT_ArenaAlloc(subjectEntry->common.arena,
sizeof(char *));
if ( subjectEntry->emailAddrs ) {
subjectEntry->emailAddrs[0] =
(char *)PORT_ArenaAlloc(subjectEntry->common.arena,
key.size - 1);
if ( subjectEntry->emailAddrs[0] ) {
PORT_Memcpy(subjectEntry->emailAddrs[0], emailAddr,
key.size - 1);
subjectEntry->nemailAddrs = 1;
(void)WriteDBSubjectEntry(handle, subjectEntry);
}
}
}
endloop:
if ( subjectEntry ) {
DestroyDBEntry((certDBEntry *)subjectEntry);
subjectEntry = NULL;
}
if ( nnEntry ) {
DestroyDBEntry((certDBEntry *)nnEntry);
nnEntry = NULL;
}
if ( emailEntry ) {
DestroyDBEntry((certDBEntry *)emailEntry);
emailEntry = NULL;
}
}
}
} while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
ret = certdb_Sync(handle->permCertDB, 0);
(* updatedb->close)(updatedb);
return(SECSuccess);
}
static SECStatus
updateV5Callback(NSSLOWCERTCertificate *cert, SECItem *k, void *pdata)
{
NSSLOWCERTCertDBHandle *handle;
certDBEntryCert *entry;
NSSLOWCERTCertTrust *trust;
handle = (NSSLOWCERTCertDBHandle *)pdata;
trust = &cert->dbEntry->trust;
/* SSL user certs can be used for email if they have an email addr */
if ( cert->emailAddr && ( trust->sslFlags & CERTDB_USER ) &&
( trust->emailFlags == 0 ) ) {
trust->emailFlags = CERTDB_USER;
}
/* servers didn't set the user flags on the server cert.. */
if (PORT_Strcmp(cert->dbEntry->nickname,"Server-Cert") == 0) {
trust->sslFlags |= CERTDB_USER;
}
entry = AddCertToPermDB(handle, cert, cert->dbEntry->nickname,
&cert->dbEntry->trust);
if ( entry ) {
DestroyDBEntry((certDBEntry *)entry);
}
return(SECSuccess);
}
static SECStatus
UpdateV5DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
{
NSSLOWCERTCertDBHandle updatehandle;
updatehandle.permCertDB = updatedb;
updatehandle.dbMon = PZ_NewMonitor(nssILockCertDB);
updatehandle.dbVerify = 0;
updatehandle.ref = 1; /* prevent premature close */
(void)nsslowcert_TraversePermCerts(&updatehandle, updateV5Callback,
(void *)handle);
PZ_DestroyMonitor(updatehandle.dbMon);
(* updatedb->close)(updatedb);
return(SECSuccess);
}
static PRBool
isV4DB(DB *db) {
DBT key,data;
int ret;
key.data = "Version";
key.size = 7;
ret = (*db->get)(db, &key, &data, 0);
if (ret) {
return PR_FALSE;
}
if ((data.size == 1) && (*(unsigned char *)data.data <= 4)) {
return PR_TRUE;
}
return PR_FALSE;
}
static SECStatus
UpdateV4DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
{
DBT key, data;
certDBEntryCert *entry, *entry2;
int ret;
PLArenaPool *arena = NULL;
NSSLOWCERTCertificate *cert;
ret = (* updatedb->seq)(updatedb, &key, &data, R_FIRST);
if ( ret ) {
return(SECFailure);
}
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) {
return(SECFailure);
}
do {
if ( data.size != 1 ) { /* skip version number */
/* decode the old DB entry */
entry = (certDBEntryCert *)
DecodeV4DBCertEntry((unsigned char*)data.data, data.size);
if ( entry ) {
cert = nsslowcert_DecodeDERCertificate(&entry->derCert,
entry->nickname);
if ( cert != NULL ) {
/* add to new database */
entry2 = AddCertToPermDB(handle, cert, entry->nickname,
&entry->trust);
nsslowcert_DestroyCertificate(cert);
if ( entry2 ) {
DestroyDBEntry((certDBEntry *)entry2);
}
}
DestroyDBEntry((certDBEntry *)entry);
}
}
} while ( (* updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0 );
PORT_FreeArena(arena, PR_FALSE);
(* updatedb->close)(updatedb);
return(SECSuccess);
}
/*
* return true if a database key conflict exists
*/
PRBool
nsslowcert_CertDBKeyConflict(SECItem *derCert, NSSLOWCERTCertDBHandle *handle)
{
SECStatus rv;
DBT tmpdata;
DBT namekey;
int ret;
SECItem keyitem;
PLArenaPool *arena = NULL;
SECItem derKey;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL ) {
goto loser;
}
/* get the db key of the cert */
rv = nsslowcert_KeyFromDERCert(arena, derCert, &derKey);
if ( rv != SECSuccess ) {
goto loser;
}
rv = EncodeDBCertKey(&derKey, arena, &keyitem);
if ( rv != SECSuccess ) {
goto loser;
}
namekey.data = keyitem.data;
namekey.size = keyitem.len;
ret = certdb_Get(handle->permCertDB, &namekey, &tmpdata, 0);
if ( ret == 0 ) {
goto loser;
}
PORT_FreeArena(arena, PR_FALSE);
return(PR_FALSE);
loser:
if ( arena ) {
PORT_FreeArena(arena, PR_FALSE);
}
return(PR_TRUE);
}
/*
* return true if a nickname conflict exists
* NOTE: caller must have already made sure that this exact cert
* doesn't exist in the DB
*/
static PRBool
nsslowcert_CertNicknameConflict(char *nickname, SECItem *derSubject,
NSSLOWCERTCertDBHandle *handle)
{
PRBool rv;
certDBEntryNickname *entry;
if ( nickname == NULL ) {
return(PR_FALSE);
}
entry = ReadDBNicknameEntry(handle, nickname);
if ( entry == NULL ) {
/* no entry for this nickname, so no conflict */
return(PR_FALSE);
}
rv = PR_TRUE;
if ( SECITEM_CompareItem(derSubject, &entry->subjectName) == SECEqual ) {
/* if subject names are the same, then no conflict */
rv = PR_FALSE;
}
DestroyDBEntry((certDBEntry *)entry);
return(rv);
}
#ifdef DBM_USING_NSPR
#define NO_RDONLY PR_RDONLY
#define NO_RDWR PR_RDWR
#define NO_CREATE (PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE)
#else
#define NO_RDONLY O_RDONLY
#define NO_RDWR O_RDWR
#define NO_CREATE (O_RDWR | O_CREAT | O_TRUNC)
#endif
/*
* open an old database that needs to be updated
*/
static DB *
nsslowcert_openolddb(NSSLOWCERTDBNameFunc namecb, void *cbarg, int version)
{
char * tmpname;
DB *updatedb = NULL;
tmpname = (* namecb)(cbarg, version); /* get v6 db name */
if ( tmpname ) {
updatedb = dbopen( tmpname, NO_RDONLY, 0600, DB_HASH, 0 );
PORT_Free(tmpname);
}
return updatedb;
}
static SECStatus
openNewCertDB(const char *appName, const char *prefix, const char *certdbname,
NSSLOWCERTCertDBHandle *handle, NSSLOWCERTDBNameFunc namecb, void *cbarg)
{
SECStatus rv;
certDBEntryVersion *versionEntry = NULL;
DB *updatedb = NULL;
int status = RDB_FAIL;
if (appName) {
handle->permCertDB=rdbopen( appName, prefix, "cert", NO_CREATE, &status);
} else {
handle->permCertDB=dbsopen(certdbname, NO_CREATE, 0600, DB_HASH, 0);
}
/* if create fails then we lose */
if ( handle->permCertDB == 0 ) {
return status == RDB_RETRY ? SECWouldBlock : SECFailure;
}
/* Verify version number; */
versionEntry = NewDBVersionEntry(0);
if ( versionEntry == NULL ) {
rv = SECFailure;
goto loser;
}
rv = WriteDBVersionEntry(handle, versionEntry);
DestroyDBEntry((certDBEntry *)versionEntry);
if ( rv != SECSuccess ) {
goto loser;
}
/* rv must already be Success here because of previous if statement */
/* try to upgrade old db here */
if (appName &&
(updatedb = dbsopen(certdbname, NO_RDONLY, 0600, DB_HASH, 0)) != NULL) {
rv = UpdateV8DB(handle, updatedb);
} else if ((updatedb = nsslowcert_openolddb(namecb,cbarg,7)) != NULL) {
rv = UpdateV7DB(handle, updatedb);
} else if ((updatedb = nsslowcert_openolddb(namecb,cbarg,6)) != NULL) {
rv = UpdateV6DB(handle, updatedb);
} else if ((updatedb = nsslowcert_openolddb(namecb,cbarg,5)) != NULL) {
rv = UpdateV5DB(handle, updatedb);
} else if ((updatedb = nsslowcert_openolddb(namecb,cbarg,4)) != NULL) {
/* NES has v5 format db's with v4 db names! */
if (isV4DB(updatedb)) {
rv = UpdateV4DB(handle,updatedb);
} else {
rv = UpdateV5DB(handle,updatedb);
}
}
loser:
db_InitComplete(handle->permCertDB);
return rv;
}
static int
nsslowcert_GetVersionNumber( NSSLOWCERTCertDBHandle *handle)
{
certDBEntryVersion *versionEntry = NULL;
int version = 0;
versionEntry = ReadDBVersionEntry(handle);
if ( versionEntry == NULL ) {
return 0;
}
version = versionEntry->common.version;
DestroyDBEntry((certDBEntry *)versionEntry);
return version;
}
/*
* Open the certificate database and index databases. Create them if
* they are not there or bad.
*/
static SECStatus
nsslowcert_OpenPermCertDB(NSSLOWCERTCertDBHandle *handle, PRBool readOnly,
const char *appName, const char *prefix,
NSSLOWCERTDBNameFunc namecb, void *cbarg)
{
SECStatus rv;
int openflags;
char *certdbname;
int version = 0;
certdbname = (* namecb)(cbarg, CERT_DB_FILE_VERSION);
if ( certdbname == NULL ) {
return(SECFailure);
}
openflags = readOnly ? NO_RDONLY : NO_RDWR;
/*
* first open the permanent file based database.
*/
if (appName) {
handle->permCertDB = rdbopen( appName, prefix, "cert", openflags, NULL);
} else {
handle->permCertDB = dbsopen( certdbname, openflags, 0600, DB_HASH, 0 );
}
/* check for correct version number */
if ( handle->permCertDB ) {
version = nsslowcert_GetVersionNumber(handle);
if ((version != CERT_DB_FILE_VERSION) &&
!(appName && version == CERT_DB_V7_FILE_VERSION)) {
goto loser;
}
} else if ( readOnly ) {
/* don't create if readonly */
/* Try openning a version 7 database */
handle->permCertDB = nsslowcert_openolddb(namecb,cbarg, 7);
if (!handle->permCertDB) {
goto loser;
}
if (nsslowcert_GetVersionNumber(handle) != 7) {
goto loser;
}
} else {
/* if first open fails, try to create a new DB */
rv = openNewCertDB(appName,prefix,certdbname,handle,namecb,cbarg);
if (rv == SECWouldBlock) {
/* only the rdb version can fail with wouldblock */
handle->permCertDB =
rdbopen( appName, prefix, "cert", openflags, NULL);
/* check for correct version number */
if ( !handle->permCertDB ) {
goto loser;
}
version = nsslowcert_GetVersionNumber(handle);
if ((version != CERT_DB_FILE_VERSION) &&
!(appName && version == CERT_DB_V7_FILE_VERSION)) {
goto loser;
}
} else if (rv != SECSuccess) {
goto loser;
}
}
PORT_Free(certdbname);
return (SECSuccess);
loser:
PORT_SetError(SEC_ERROR_BAD_DATABASE);
if ( handle->permCertDB ) {
certdb_Close(handle->permCertDB);
handle->permCertDB = 0;
}
PORT_Free(certdbname);
return(SECFailure);
}
/*
* delete all DB records associated with a particular certificate
*/
static SECStatus
DeletePermCert(NSSLOWCERTCertificate *cert)
{
SECStatus rv;
SECStatus ret;
ret = SECSuccess;
rv = DeleteDBCertEntry(cert->dbhandle, &cert->certKey);
if ( rv != SECSuccess ) {
ret = SECFailure;
}
rv = RemovePermSubjectNode(cert);
return(ret);
}
/*
* Delete a certificate from the permanent database.
*/
SECStatus
nsslowcert_DeletePermCertificate(NSSLOWCERTCertificate *cert)
{
SECStatus rv;
nsslowcert_LockDB(cert->dbhandle);
/* delete the records from the permanent database */
rv = DeletePermCert(cert);
/* get rid of dbcert and stuff pointing to it */
DestroyDBEntry((certDBEntry *)cert->dbEntry);
cert->dbEntry = NULL;
cert->trust = NULL;
nsslowcert_UnlockDB(cert->dbhandle);
return(rv);
}
/*
* Traverse all of the entries in the database of a particular type
* call the given function for each one.
*/
SECStatus
nsslowcert_TraverseDBEntries(NSSLOWCERTCertDBHandle *handle,
certDBEntryType type,
SECStatus (* callback)(SECItem *data, SECItem *key,
certDBEntryType type, void *pdata),
void *udata )
{
DBT data;
DBT key;
SECStatus rv = SECSuccess;
int ret;
SECItem dataitem;
SECItem keyitem;
unsigned char *buf;
unsigned char *keybuf;
ret = certdb_Seq(handle->permCertDB, &key, &data, R_FIRST);
if ( ret ) {
return(SECFailure);
}
/* here, ret is zero and rv is SECSuccess.
* Below here, ret is a count of successful calls to the callback function.
*/
do {
buf = (unsigned char *)data.data;
if ( buf[1] == (unsigned char)type ) {
dataitem.len = data.size;
dataitem.data = buf;
dataitem.type = siBuffer;
keyitem.len = key.size - SEC_DB_KEY_HEADER_LEN;
keybuf = (unsigned char *)key.data;
keyitem.data = &keybuf[SEC_DB_KEY_HEADER_LEN];
keyitem.type = siBuffer;
/* type should equal keybuf[0]. */
rv = (* callback)(&dataitem, &keyitem, type, udata);
if ( rv == SECSuccess ) {
++ret;
}
}
} while ( certdb_Seq(handle->permCertDB, &key, &data, R_NEXT) == 0 );
/* If any callbacks succeeded, or no calls to callbacks were made,
* then report success. Otherwise, report failure.
*/
return (ret ? SECSuccess : rv);
}
/*
* Decode a certificate and enter it into the temporary certificate database.
* Deal with nicknames correctly
*
* This is the private entry point.
*/
static NSSLOWCERTCertificate *
DecodeACert(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry)
{
NSSLOWCERTCertificate *cert = NULL;
cert = nsslowcert_DecodeDERCertificate(&entry->derCert, entry->nickname );
if ( cert == NULL ) {
goto loser;
}
cert->dbhandle = handle;
cert->dbEntry = entry;
cert->trust = &entry->trust;
return(cert);
loser:
return(0);
}
static NSSLOWCERTTrust *
CreateTrust(void)
{
NSSLOWCERTTrust *trust = NULL;
nsslowcert_LockFreeList();
trust = trustListHead;
if (trust) {
trustListCount--;
trustListHead = trust->next;
}
PORT_Assert(trustListCount >= 0);
nsslowcert_UnlockFreeList();
if (trust) {
return trust;
}
return PORT_ZNew(NSSLOWCERTTrust);
}
static void
DestroyTrustFreeList(void)
{
NSSLOWCERTTrust *trust;
nsslowcert_LockFreeList();
while (NULL != (trust = trustListHead)) {
trustListCount--;
trustListHead = trust->next;
PORT_Free(trust);
}
PORT_Assert(!trustListCount);
trustListCount = 0;
nsslowcert_UnlockFreeList();
}
static NSSLOWCERTTrust *
DecodeTrustEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry,
const SECItem *dbKey)
{
NSSLOWCERTTrust *trust = CreateTrust();
if (trust == NULL) {
return trust;
}
trust->dbhandle = handle;
trust->dbEntry = entry;
trust->dbKey.data = pkcs11_copyStaticData(dbKey->data,dbKey->len,
trust->dbKeySpace, sizeof(trust->dbKeySpace));
if (!trust->dbKey.data) {
PORT_Free(trust);
return NULL;
}
trust->dbKey.len = dbKey->len;
trust->trust = &entry->trust;
trust->derCert = &entry->derCert;
return(trust);
}
typedef struct {
PermCertCallback certfunc;
NSSLOWCERTCertDBHandle *handle;
void *data;
} PermCertCallbackState;
/*
* traversal callback to decode certs and call callers callback
*/
static SECStatus
certcallback(SECItem *dbdata, SECItem *dbkey, certDBEntryType type, void *data)
{
PermCertCallbackState *mystate;
SECStatus rv;
certDBEntryCert *entry;
SECItem entryitem;
NSSLOWCERTCertificate *cert;
PLArenaPool *arena = NULL;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL ) {
goto loser;
}
entry = (certDBEntryCert *)PORT_ArenaAlloc(arena, sizeof(certDBEntryCert));
mystate = (PermCertCallbackState *)data;
entry->common.version = (unsigned int)dbdata->data[0];
entry->common.type = (certDBEntryType)dbdata->data[1];
entry->common.flags = (unsigned int)dbdata->data[2];
entry->common.arena = arena;
entryitem.len = dbdata->len - SEC_DB_ENTRY_HEADER_LEN;
entryitem.data = &dbdata->data[SEC_DB_ENTRY_HEADER_LEN];
rv = DecodeDBCertEntry(entry, &entryitem);
if (rv != SECSuccess ) {
goto loser;
}
entry->derCert.type = siBuffer;
/* note: Entry is 'inheritted'. */
cert = DecodeACert(mystate->handle, entry);
rv = (* mystate->certfunc)(cert, dbkey, mystate->data);
/* arena stored in entry destroyed by nsslowcert_DestroyCertificate */
nsslowcert_DestroyCertificateNoLocking(cert);
return(rv);
loser:
if ( arena ) {
PORT_FreeArena(arena, PR_FALSE);
}
return(SECFailure);
}
/*
* Traverse all of the certificates in the permanent database and
* call the given function for each one; expect the caller to have lock.
*/
static SECStatus
TraversePermCertsNoLocking(NSSLOWCERTCertDBHandle *handle,
SECStatus (* certfunc)(NSSLOWCERTCertificate *cert,
SECItem *k,
void *pdata),
void *udata )
{
SECStatus rv;
PermCertCallbackState mystate;
mystate.certfunc = certfunc;
mystate.handle = handle;
mystate.data = udata;
rv = nsslowcert_TraverseDBEntries(handle, certDBEntryTypeCert, certcallback,
(void *)&mystate);
return(rv);
}
/*
* Traverse all of the certificates in the permanent database and
* call the given function for each one.
*/
SECStatus
nsslowcert_TraversePermCerts(NSSLOWCERTCertDBHandle *handle,
SECStatus (* certfunc)(NSSLOWCERTCertificate *cert, SECItem *k,
void *pdata),
void *udata )
{
SECStatus rv;
nsslowcert_LockDB(handle);
rv = TraversePermCertsNoLocking(handle, certfunc, udata);
nsslowcert_UnlockDB(handle);
return(rv);
}
/*
* Close the database
*/
void
nsslowcert_ClosePermCertDB(NSSLOWCERTCertDBHandle *handle)
{
if ( handle ) {
if ( handle->permCertDB ) {
certdb_Close( handle->permCertDB );
handle->permCertDB = NULL;
}
if (handle->dbMon) {
PZ_DestroyMonitor(handle->dbMon);
handle->dbMon = NULL;
}
PORT_Free(handle);
}
return;
}
/*
* Get the trust attributes from a certificate
*/
SECStatus
nsslowcert_GetCertTrust(NSSLOWCERTCertificate *cert, NSSLOWCERTCertTrust *trust)
{
SECStatus rv;
nsslowcert_LockCertTrust(cert);
if ( cert->trust == NULL ) {
rv = SECFailure;
} else {
*trust = *cert->trust;
rv = SECSuccess;
}
nsslowcert_UnlockCertTrust(cert);
return(rv);
}
/*
* Change the trust attributes of a certificate and make them permanent
* in the database.
*/
SECStatus
nsslowcert_ChangeCertTrust(NSSLOWCERTCertDBHandle *handle,
NSSLOWCERTCertificate *cert, NSSLOWCERTCertTrust *trust)
{
certDBEntryCert *entry;
int rv;
SECStatus ret;
nsslowcert_LockDB(handle);
nsslowcert_LockCertTrust(cert);
/* only set the trust on permanent certs */
if ( cert->trust == NULL ) {
ret = SECFailure;
goto done;
}
*cert->trust = *trust;
if ( cert->dbEntry == NULL ) {
ret = SECSuccess; /* not in permanent database */
goto done;
}
entry = cert->dbEntry;
entry->trust = *trust;
rv = WriteDBCertEntry(handle, entry);
if ( rv ) {
ret = SECFailure;
goto done;
}
ret = SECSuccess;
done:
nsslowcert_UnlockCertTrust(cert);
nsslowcert_UnlockDB(handle);
return(ret);
}
static SECStatus
nsslowcert_UpdatePermCert(NSSLOWCERTCertDBHandle *dbhandle,
NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust)
{
char *oldnn;
certDBEntryCert *entry;
PRBool conflict;
SECStatus ret;
PORT_Assert(!cert->dbEntry);
/* don't add a conflicting nickname */
conflict = nsslowcert_CertNicknameConflict(nickname, &cert->derSubject,
dbhandle);
if ( conflict ) {
ret = SECFailure;
goto done;
}
/* save old nickname so that we can delete it */
oldnn = cert->nickname;
entry = AddCertToPermDB(dbhandle, cert, nickname, trust);
if ( entry == NULL ) {
ret = SECFailure;
goto done;
}
pkcs11_freeNickname(oldnn,cert->nicknameSpace);
cert->nickname = (entry->nickname) ? pkcs11_copyNickname(entry->nickname,
cert->nicknameSpace, sizeof(cert->nicknameSpace)) : NULL;
cert->trust = &entry->trust;
cert->dbEntry = entry;
ret = SECSuccess;
done:
return(ret);
}
SECStatus
nsslowcert_AddPermCert(NSSLOWCERTCertDBHandle *dbhandle,
NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust)
{
SECStatus ret;
nsslowcert_LockDB(dbhandle);
ret = nsslowcert_UpdatePermCert(dbhandle, cert, nickname, trust);
nsslowcert_UnlockDB(dbhandle);
return(ret);
}
/*
* Open the certificate database and index databases. Create them if
* they are not there or bad.
*/
SECStatus
nsslowcert_OpenCertDB(NSSLOWCERTCertDBHandle *handle, PRBool readOnly,
const char *appName, const char *prefix,
NSSLOWCERTDBNameFunc namecb, void *cbarg, PRBool openVolatile)
{
int rv;
certdb_InitDBLock(handle);
handle->dbMon = PZ_NewMonitor(nssILockCertDB);
PORT_Assert(handle->dbMon != NULL);
handle->dbVerify = PR_FALSE;
rv = nsslowcert_OpenPermCertDB(handle, readOnly, appName, prefix,
namecb, cbarg);
if ( rv ) {
goto loser;
}
return (SECSuccess);
loser:
if (handle->dbMon) {
PZ_DestroyMonitor(handle->dbMon);
handle->dbMon = NULL;
}
PORT_SetError(SEC_ERROR_BAD_DATABASE);
return(SECFailure);
}
PRBool
nsslowcert_needDBVerify(NSSLOWCERTCertDBHandle *handle)
{
if (!handle) return PR_FALSE;
return handle->dbVerify;
}
void
nsslowcert_setDBVerify(NSSLOWCERTCertDBHandle *handle, PRBool value)
{
handle->dbVerify = value;
}
/*
* Lookup a certificate in the databases.
*/
static NSSLOWCERTCertificate *
FindCertByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey, PRBool lockdb)
{
NSSLOWCERTCertificate *cert = NULL;
certDBEntryCert *entry;
PRBool locked = PR_FALSE;
if ( lockdb ) {
locked = PR_TRUE;
nsslowcert_LockDB(handle);
}
/* find in perm database */
entry = ReadDBCertEntry(handle, certKey);
if ( entry == NULL ) {
goto loser;
}
/* inherit entry */
cert = DecodeACert(handle, entry);
loser:
if (cert == NULL) {
if (entry) {
DestroyDBEntry((certDBEntry *)entry);
}
}
if ( locked ) {
nsslowcert_UnlockDB(handle);
}
return(cert);
}
/*
* Lookup a certificate in the databases.
*/
static NSSLOWCERTTrust *
FindTrustByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey, PRBool lockdb)
{
NSSLOWCERTTrust *trust = NULL;
certDBEntryCert *entry;
PRBool locked = PR_FALSE;
if ( lockdb ) {
locked = PR_TRUE;
nsslowcert_LockDB(handle);
}
/* find in perm database */
entry = ReadDBCertEntry(handle, certKey);
if ( entry == NULL ) {
goto loser;
}
if (!nsslowcert_hasTrust(&entry->trust)) {
goto loser;
}
/* inherit entry */
trust = DecodeTrustEntry(handle, entry, certKey);
loser:
if (trust == NULL) {
if (entry) {
DestroyDBEntry((certDBEntry *)entry);
}
}
if ( locked ) {
nsslowcert_UnlockDB(handle);
}
return(trust);
}
/*
* Lookup a certificate in the databases without locking
*/
NSSLOWCERTCertificate *
nsslowcert_FindCertByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
{
return(FindCertByKey(handle, certKey, PR_FALSE));
}
/*
* Lookup a trust object in the databases without locking
*/
NSSLOWCERTTrust *
nsslowcert_FindTrustByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
{
return(FindTrustByKey(handle, certKey, PR_FALSE));
}
/*
* Generate a key from an issuerAndSerialNumber, and find the
* associated cert in the database.
*/
NSSLOWCERTCertificate *
nsslowcert_FindCertByIssuerAndSN(NSSLOWCERTCertDBHandle *handle, NSSLOWCERTIssuerAndSN *issuerAndSN)
{
SECItem certKey;
SECItem *sn = &issuerAndSN->serialNumber;
SECItem *issuer = &issuerAndSN->derIssuer;
NSSLOWCERTCertificate *cert;
int data_len = sn->len;
int index = 0;
/* automatically detect DER encoded serial numbers and remove the der
* encoding since the database expects unencoded data.
* if it's DER encoded, there must be at least 3 bytes, tag, len, data */
if ((sn->len >= 3) && (sn->data[0] == 0x2)) {
/* remove the der encoding of the serial number before generating the
* key.. */
int data_left = sn->len-2;
data_len = sn->data[1];
index = 2;
/* extended length ? (not very likely for a serial number) */
if (data_len & 0x80) {
int len_count = data_len & 0x7f;
data_len = 0;
data_left -= len_count;
if (data_left > 0) {
while (len_count --) {
data_len = (data_len << 8) | sn->data[index++];
}
}
}
/* XXX leaving any leading zeros on the serial number for backwards
* compatibility
*/
/* not a valid der, must be just an unlucky serial number value */
if (data_len != data_left) {
data_len = sn->len;
index = 0;
}
}
certKey.type = 0;
certKey.data = (unsigned char*)PORT_Alloc(sn->len + issuer->len);
certKey.len = data_len + issuer->len;
if ( certKey.data == NULL ) {
return(0);
}
/* first try the serial number as hand-decoded above*/
/* copy the serialNumber */
PORT_Memcpy(certKey.data, &sn->data[index], data_len);
/* copy the issuer */
PORT_Memcpy( &certKey.data[data_len],issuer->data,issuer->len);
cert = nsslowcert_FindCertByKey(handle, &certKey);
if (cert) {
PORT_Free(certKey.data);
return (cert);
}
/* didn't find it, try by der encoded serial number */
/* copy the serialNumber */
PORT_Memcpy(certKey.data, sn->data, sn->len);
/* copy the issuer */
PORT_Memcpy( &certKey.data[sn->len], issuer->data, issuer->len);
certKey.len = sn->len + issuer->len;
cert = nsslowcert_FindCertByKey(handle, &certKey);
PORT_Free(certKey.data);
return(cert);
}
/*
* Generate a key from an issuerAndSerialNumber, and find the
* associated cert in the database.
*/
NSSLOWCERTTrust *
nsslowcert_FindTrustByIssuerAndSN(NSSLOWCERTCertDBHandle *handle,
NSSLOWCERTIssuerAndSN *issuerAndSN)
{
SECItem certKey;
SECItem *sn = &issuerAndSN->serialNumber;
SECItem *issuer = &issuerAndSN->derIssuer;
NSSLOWCERTTrust *trust;
unsigned char keyBuf[512];
int data_len = sn->len;
int index = 0;
int len;
/* automatically detect DER encoded serial numbers and remove the der
* encoding since the database expects unencoded data.
* if it's DER encoded, there must be at least 3 bytes, tag, len, data */
if ((sn->len >= 3) && (sn->data[0] == 0x2)) {
/* remove the der encoding of the serial number before generating the
* key.. */
int data_left = sn->len-2;
data_len = sn->data[1];
index = 2;
/* extended length ? (not very likely for a serial number) */
if (data_len & 0x80) {
int len_count = data_len & 0x7f;
data_len = 0;
data_left -= len_count;
if (data_left > 0) {
while (len_count --) {
data_len = (data_len << 8) | sn->data[index++];
}
}
}
/* XXX leaving any leading zeros on the serial number for backwards
* compatibility
*/
/* not a valid der, must be just an unlucky serial number value */
if (data_len != data_left) {
data_len = sn->len;
index = 0;
}
}
certKey.type = 0;
certKey.len = data_len + issuer->len;
len = sn->len + issuer->len;
if (len > sizeof (keyBuf)) {
certKey.data = (unsigned char*)PORT_Alloc(len);
} else {
certKey.data = keyBuf;
}
if ( certKey.data == NULL ) {
return(0);
}
/* first try the serial number as hand-decoded above*/
/* copy the serialNumber */
PORT_Memcpy(certKey.data, &sn->data[index], data_len);
/* copy the issuer */
PORT_Memcpy( &certKey.data[data_len],issuer->data,issuer->len);
trust = nsslowcert_FindTrustByKey(handle, &certKey);
if (trust) {
pkcs11_freeStaticData(certKey.data, keyBuf);
return (trust);
}
if (index == 0) {
pkcs11_freeStaticData(certKey.data, keyBuf);
return NULL;
}
/* didn't find it, try by der encoded serial number */
/* copy the serialNumber */
PORT_Memcpy(certKey.data, sn->data, sn->len);
/* copy the issuer */
PORT_Memcpy( &certKey.data[sn->len], issuer->data, issuer->len);
certKey.len = sn->len + issuer->len;
trust = nsslowcert_FindTrustByKey(handle, &certKey);
pkcs11_freeStaticData(certKey.data, keyBuf);
return(trust);
}
/*
* look for the given DER certificate in the database
*/
NSSLOWCERTCertificate *
nsslowcert_FindCertByDERCert(NSSLOWCERTCertDBHandle *handle, SECItem *derCert)
{
PLArenaPool *arena;
SECItem certKey;
SECStatus rv;
NSSLOWCERTCertificate *cert = NULL;
/* create a scratch arena */
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL ) {
return(NULL);
}
/* extract the database key from the cert */
rv = nsslowcert_KeyFromDERCert(arena, derCert, &certKey);
if ( rv != SECSuccess ) {
goto loser;
}
/* find the certificate */
cert = nsslowcert_FindCertByKey(handle, &certKey);
loser:
PORT_FreeArena(arena, PR_FALSE);
return(cert);
}
static void
DestroyCertificate(NSSLOWCERTCertificate *cert, PRBool lockdb)
{
int refCount;
NSSLOWCERTCertDBHandle *handle;
if ( cert ) {
handle = cert->dbhandle;
/*
* handle may be NULL, for example if the cert was created with
* nsslowcert_DecodeDERCertificate.
*/
if ( lockdb && handle ) {
nsslowcert_LockDB(handle);
}
nsslowcert_LockCertRefCount(cert);
PORT_Assert(cert->referenceCount > 0);
refCount = --cert->referenceCount;
nsslowcert_UnlockCertRefCount(cert);
if ( refCount == 0 ) {
certDBEntryCert *entry = cert->dbEntry;
if ( entry ) {
DestroyDBEntry((certDBEntry *)entry);
}
pkcs11_freeNickname(cert->nickname,cert->nicknameSpace);
pkcs11_freeNickname(cert->emailAddr,cert->emailAddrSpace);
pkcs11_freeStaticData(cert->certKey.data,cert->certKeySpace);
cert->certKey.data = NULL;
cert->nickname = NULL;
/* zero cert before freeing. Any stale references to this cert
* after this point will probably cause an exception. */
PORT_Memset(cert, 0, sizeof *cert);
/* use reflock to protect the free list */
nsslowcert_LockFreeList();
if (certListCount > MAX_CERT_LIST_COUNT) {
PORT_Free(cert);
} else {
certListCount++;
cert->next = certListHead;
certListHead = cert;
}
nsslowcert_UnlockFreeList();
cert = NULL;
}
if ( lockdb && handle ) {
nsslowcert_UnlockDB(handle);
}
}
return;
}
NSSLOWCERTCertificate *
nsslowcert_CreateCert(void)
{
NSSLOWCERTCertificate *cert;
nsslowcert_LockFreeList();
cert = certListHead;
if (cert) {
certListHead = cert->next;
certListCount--;
}
PORT_Assert(certListCount >= 0);
nsslowcert_UnlockFreeList();
if (cert) {
return cert;
}
return PORT_ZNew(NSSLOWCERTCertificate);
}
static void
DestroyCertFreeList(void)
{
NSSLOWCERTCertificate *cert;
nsslowcert_LockFreeList();
while (NULL != (cert = certListHead)) {
certListCount--;
certListHead = cert->next;
PORT_Free(cert);
}
PORT_Assert(!certListCount);
certListCount = 0;
nsslowcert_UnlockFreeList();
}
void
nsslowcert_DestroyTrust(NSSLOWCERTTrust *trust)
{
certDBEntryCert *entry = trust->dbEntry;
if ( entry ) {
DestroyDBEntry((certDBEntry *)entry);
}
pkcs11_freeStaticData(trust->dbKey.data,trust->dbKeySpace);
PORT_Memset(trust, 0, sizeof(*trust));
nsslowcert_LockFreeList();
if (trustListCount > MAX_TRUST_LIST_COUNT) {
PORT_Free(trust);
} else {
trustListCount++;
trust->next = trustListHead;
trustListHead = trust;
}
nsslowcert_UnlockFreeList();
return;
}
void
nsslowcert_DestroyCertificate(NSSLOWCERTCertificate *cert)
{
DestroyCertificate(cert, PR_TRUE);
return;
}
static void
nsslowcert_DestroyCertificateNoLocking(NSSLOWCERTCertificate *cert)
{
DestroyCertificate(cert, PR_FALSE);
return;
}
/*
* Lookup a CRL in the databases. We mirror the same fast caching data base
* caching stuff used by certificates....?
*/
certDBEntryRevocation *
nsslowcert_FindCrlByKey(NSSLOWCERTCertDBHandle *handle,
SECItem *crlKey, PRBool isKRL)
{
SECItem keyitem;
SECStatus rv;
PLArenaPool *arena = NULL;
certDBEntryRevocation *entry = NULL;
certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation
: certDBEntryTypeRevocation;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if ( arena == NULL ) {
goto loser;
}
rv = EncodeDBGenericKey(crlKey, arena, &keyitem, crlType);
if ( rv != SECSuccess ) {
goto loser;
}
/* find in perm database */
entry = ReadDBCrlEntry(handle, crlKey, crlType);
if ( entry == NULL ) {
goto loser;
}
loser:
if ( arena ) {
PORT_FreeArena(arena, PR_FALSE);
}
return entry;
}
/*
* replace the existing URL in the data base with a new one
*/
static SECStatus
nsslowcert_UpdateCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl,
SECItem *crlKey, char *url, PRBool isKRL)
{
SECStatus rv = SECFailure;
certDBEntryRevocation *entry = NULL;
certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation
: certDBEntryTypeRevocation;
DeleteDBCrlEntry(handle, crlKey, crlType);
/* Write the new entry into the data base */
entry = NewDBCrlEntry(derCrl, url, crlType, 0);
if (entry == NULL) goto done;
rv = WriteDBCrlEntry(handle, entry, crlKey);
if (rv != SECSuccess) goto done;
done:
if (entry) {
DestroyDBEntry((certDBEntry *)entry);
}
return rv;
}
SECStatus
nsslowcert_AddCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl,
SECItem *crlKey, char *url, PRBool isKRL)
{
SECStatus rv;
rv = nsslowcert_UpdateCrl(handle, derCrl, crlKey, url, isKRL);
return rv;
}
SECStatus
nsslowcert_DeletePermCRL(NSSLOWCERTCertDBHandle *handle, const SECItem *derName,
PRBool isKRL)
{
SECStatus rv;
certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation
: certDBEntryTypeRevocation;
rv = DeleteDBCrlEntry(handle, derName, crlType);
if (rv != SECSuccess) goto done;
done:
return rv;
}
PRBool
nsslowcert_hasTrust(NSSLOWCERTCertTrust *trust)
{
if (trust == NULL) {
return PR_FALSE;
}
return !((trust->sslFlags & CERTDB_TRUSTED_UNKNOWN) &&
(trust->emailFlags & CERTDB_TRUSTED_UNKNOWN) &&
(trust->objectSigningFlags & CERTDB_TRUSTED_UNKNOWN));
}
/*
* This function has the logic that decides if another person's cert and
* email profile from an S/MIME message should be saved. It can deal with
* the case when there is no profile.
*/
static SECStatus
nsslowcert_UpdateSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle,
char *emailAddr, SECItem *derSubject, SECItem *emailProfile,
SECItem *profileTime)
{
certDBEntrySMime *entry = NULL;
SECStatus rv = SECFailure;;
/* find our existing entry */
entry = nsslowcert_ReadDBSMimeEntry(dbhandle, emailAddr);
if ( entry ) {
/* keep our old db entry consistant for old applications. */
if (!SECITEM_ItemsAreEqual(derSubject, &entry->subjectName)) {
nsslowcert_UpdateSubjectEmailAddr(dbhandle, &entry->subjectName,
emailAddr, nsslowcert_remove);
}
DestroyDBEntry((certDBEntry *)entry);
entry = NULL;
}
/* now save the entry */
entry = NewDBSMimeEntry(emailAddr, derSubject, emailProfile,
profileTime, 0);
if ( entry == NULL ) {
rv = SECFailure;
goto loser;
}
nsslowcert_LockDB(dbhandle);
rv = DeleteDBSMimeEntry(dbhandle, emailAddr);
/* if delete fails, try to write new entry anyway... */
/* link subject entry back here */
rv = nsslowcert_UpdateSubjectEmailAddr(dbhandle, derSubject, emailAddr,
nsslowcert_add);
if ( rv != SECSuccess ) {
nsslowcert_UnlockDB(dbhandle);
goto loser;
}
rv = WriteDBSMimeEntry(dbhandle, entry);
if ( rv != SECSuccess ) {
nsslowcert_UnlockDB(dbhandle);
goto loser;
}
nsslowcert_UnlockDB(dbhandle);
rv = SECSuccess;
loser:
if ( entry ) {
DestroyDBEntry((certDBEntry *)entry);
}
return(rv);
}
SECStatus
nsslowcert_SaveSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle, char *emailAddr,
SECItem *derSubject, SECItem *emailProfile, SECItem *profileTime)
{
SECStatus rv = SECFailure;;
rv = nsslowcert_UpdateSMimeProfile(dbhandle, emailAddr,
derSubject, emailProfile, profileTime);
return(rv);
}
void
nsslowcert_DestroyFreeLists(void)
{
if (freeListLock == NULL) {
return;
}
DestroyCertEntryFreeList();
DestroyTrustFreeList();
DestroyCertFreeList();
SKIP_AFTER_FORK(PZ_DestroyLock(freeListLock));
freeListLock = NULL;
}
void
nsslowcert_DestroyGlobalLocks(void)
{
if (dbLock) {
SKIP_AFTER_FORK(PZ_DestroyLock(dbLock));
dbLock = NULL;
}
if (certRefCountLock) {
SKIP_AFTER_FORK(PZ_DestroyLock(certRefCountLock));
certRefCountLock = NULL;
}
if (certTrustLock) {
SKIP_AFTER_FORK(PZ_DestroyLock(certTrustLock));
certTrustLock = NULL;
}
}
certDBEntry *
nsslowcert_DecodeAnyDBEntry(SECItem *dbData, const SECItem *dbKey,
certDBEntryType entryType, void *pdata)
{
PLArenaPool *arena = NULL;
certDBEntry *entry;
SECStatus rv;
SECItem dbEntry;
if ((dbData->len < SEC_DB_ENTRY_HEADER_LEN) || (dbKey->len == 0)) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
goto loser;
}
dbEntry.data = &dbData->data[SEC_DB_ENTRY_HEADER_LEN];
dbEntry.len = dbData->len - SEC_DB_ENTRY_HEADER_LEN;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) {
goto loser;
}
entry = PORT_ArenaZNew(arena, certDBEntry);
if (!entry)
goto loser;
entry->common.version = (unsigned int)dbData->data[0];
entry->common.flags = (unsigned int)dbData->data[2];
entry->common.type = entryType;
entry->common.arena = arena;
switch (entryType) {
case certDBEntryTypeContentVersion: /* This type appears to be unused */
case certDBEntryTypeVersion: /* This type has only the common hdr */
rv = SECSuccess;
break;
case certDBEntryTypeSubject:
rv = DecodeDBSubjectEntry(&entry->subject, &dbEntry, dbKey);
break;
case certDBEntryTypeNickname:
rv = DecodeDBNicknameEntry(&entry->nickname, &dbEntry,
(char *)dbKey->data);
break;
/* smime profiles need entries created after the certs have
* been imported, loop over them in a second run */
case certDBEntryTypeSMimeProfile:
rv = DecodeDBSMimeEntry(&entry->smime, &dbEntry, (char *)dbKey->data);
break;
case certDBEntryTypeCert:
rv = DecodeDBCertEntry(&entry->cert, &dbEntry);
break;
case certDBEntryTypeKeyRevocation:
case certDBEntryTypeRevocation:
rv = DecodeDBCrlEntry(&entry->revocation, &dbEntry);
break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
}
if (rv == SECSuccess)
return entry;
loser:
if (arena)
PORT_FreeArena(arena, PR_FALSE);
return NULL;
}