/* 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 *derC
ert);
static SECStatus
nsslowcert_UpdateSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle,
char *emailAddr, SECItem *derSubject, SECItem *emailProfile,
SECItem *profileTime);
static SECStatus
nsslowcert_UpdatePermCert(NSSLOWCERTCertDBHandle *dbhandle,
NSSLOWCERTCertificate *cert, char *nickname, NSSLOWCERTCertTrust *trust);
static SECStatus
nsslowcert_UpdateCrl(NSSLOWCERTCertDBHandle *handle, SECItem *derCrl,
SECItem *crlKey, char *url, PRBool isKRL);
static NSSLOWCERTCertificate *certListHead = NULL;
static NSSLOWCERTTrust *trustListHead = NULL;
static certDBEntryCert *entryListHead = NULL;
static int certListCount = 0;
static int trustListCount = 0;
static int entryListCount = 0;
#define MAX_CERT_LIST_COUNT 10
#define MAX_TRUST_LIST_COUNT 10
#define MAX_ENTRY_LIST_COUNT 10
/*
* the following functions are wrappers for the db library that implement
* a global lock to make the database thread safe.
*/
static PZLock *dbLock = NULL;
static PZLock *certRefCountLock = NULL;
static PZLock *certTrustLock = NULL;
static PZLock *freeListLock = NULL;
void
certdb_InitDBLock(NSSLOWCERTCertDBHandle *handle)
{
if (dbLock == NULL) {
dbLock = PZ_NewLock(nssILockCertDB);
PORT_Assert(dbLock != NULL);
}
}
SECStatus
nsslowcert_InitLocks(void )
{
if (freeListLock == NULL) {
freeListLock = PZ_NewLock(nssILockRefLock);
if (freeListLock == NULL) {
return SECFailure;
}
}
if (certRefCountLock == NULL) {
certRefCountLock = PZ_NewLock(nssILockRefLock);
if (certRefCountLock == NULL) {
return SECFailure;
}
}
if (certTrustLock == NULL) {
certTrustLock = PZ_NewLock(nssILockCertDB);
if (certTrustLock == NULL) {
return SECFailure;
}
}
return SECSuccess;
}
/*
* Acquire the global lock on the cert database.
* This lock is currently used for the following operations:
* adding or deleting a cert to either the temp or perm databases
* converting a temp to perm or perm to temp
* changing (maybe just adding!?) the trust of a cert
* chaning the DB status checking Configuration
*/
static void
nsslowcert_LockDB(NSSLOWCERTCertDBHandle *handle)
{
PZ_EnterMonitor(handle->dbMon);
return ;
}
/*
* Free the global cert database lock.
*/
static void
nsslowcert_UnlockDB(NSSLOWCERTCertDBHandle *handle)
{
#ifdef DEBUG
PRStatus prstat = PZ_ExitMonitor(handle->dbMon);
PORT_Assert(prstat == PR_SUCCESS);
#else
PZ_ExitMonitor(handle->dbMon);
#endif
}
/*
* Acquire the cert reference count lock
* There is currently one global lock for all certs, but I'm putting a cert
* arg here so that it will be easy to make it per-cert in the future if
* that turns out to be necessary.
*/
static void
nsslowcert_LockCertRefCount(NSSLOWCERTCertificate *cert)
{
PORT_Assert(certRefCountLock != NULL);
PZ_Lock(certRefCountLock);
return ;
}
/*
* Free the cert reference count lock
*/
static void
nsslowcert_UnlockCertRefCount(NSSLOWCERTCertificate *cert)
{
PORT_Assert(certRefCountLock != NULL);
#ifdef DEBUG
{
PRStatus prstat = PZ_Unlock(certRefCountLock);
PORT_Assert(prstat == PR_SUCCESS);
}
#else
PZ_Unlock(certRefCountLock);
#endif
}
/*
* Acquire the cert trust lock
* There is currently one global lock for all certs, but I'm putting a cert
* arg here so that it will be easy to make it per-cert in the future if
* that turns out to be necessary.
*/
static void
nsslowcert_LockCertTrust(NSSLOWCERTCertificate *cert)
{
PORT_Assert(certTrustLock != NULL);
PZ_Lock(certTrustLock);
return ;
}
/*
* Free the cert trust lock
*/
static void
nsslowcert_UnlockCertTrust(NSSLOWCERTCertificate *cert)
{
PORT_Assert(certTrustLock != NULL);
#ifdef DEBUG
{
PRStatus prstat = PZ_Unlock(certTrustLock);
PORT_Assert(prstat == PR_SUCCESS);
}
#else
PZ_Unlock(certTrustLock);
#endif
}
/*
* Acquire the cert reference count lock
* There is currently one global lock for all certs, but I'm putting a cert
* arg here so that it will be easy to make it per-cert in the future if
* that turns out to be necessary.
*/
static void
nsslowcert_LockFreeList(void )
{
PORT_Assert(freeListLock != NULL);
SKIP_AFTER_FORK(PZ_Lock(freeListLock));
return ;
}
/*
* Free the cert reference count lock
*/
static void
nsslowcert_UnlockFreeList(void )
{
PORT_Assert(freeListLock != NULL);
#ifdef DEBUG
{
PRStatus prstat = PR_SUCCESS;
SKIP_AFTER_FORK(prstat = PZ_Unlock(freeListLock));
PORT_Assert(prstat == PR_SUCCESS);
}
#else
SKIP_AFTER_FORK(PZ_Unlock(freeListLock));
#endif
}
NSSLOWCERTCertificate *
nsslowcert_DupCertificate(NSSLOWCERTCertificate *c)
{
if (c) {
nsslowcert_LockCertRefCount(c);
++c->referenceCount;
nsslowcert_UnlockCertRefCount(c);
}
return c;
}
static int
certdb_Get(DB *db, DBT *key, DBT *data, unsigned int flags)
{
int ret;
PORT_Assert(dbLock != NULL);
PZ_Lock(dbLock);
ret = (*db->get)(db, key, data, flags);
(void )PZ_Unlock(dbLock);
return (ret);
}
static int
certdb_Put(DB *db, DBT *key, DBT *data, unsigned int flags)
{
int ret = 0;
PORT_Assert(dbLock != NULL);
PZ_Lock(dbLock);
ret = (*db->put)(db, key, data, flags);
(void )PZ_Unlock(dbLock);
return (ret);
}
static int
certdb_Sync(DB *db, unsigned int flags)
{
int ret;
PORT_Assert(dbLock != NULL);
PZ_Lock(dbLock);
ret = (*db->sync)(db, flags);
(void )PZ_Unlock(dbLock);
return (ret);
}
#define DB_NOT_FOUND -30991 /* from DBM 3.2 */
static int
certdb_Del(DB *db, DBT *key, unsigned int flags)
{
int ret;
PORT_Assert(dbLock != NULL);
PZ_Lock(dbLock);
ret = (*db->del)(db, key, flags);
(void )PZ_Unlock(dbLock);
/* don't fail if the record is already deleted */
if (ret == DB_NOT_FOUND) {
ret = 0;
}
return (ret);
}
static int
certdb_Seq(DB *db, DBT *key, DBT *data, unsigned int flags)
{
int ret;
PORT_Assert(dbLock != NULL);
PZ_Lock(dbLock);
ret = (*db->seq)(db, key, data, flags);
(void )PZ_Unlock(dbLock);
return (ret);
}
static void
certdb_Close(DB *db)
{
PORT_Assert(dbLock != NULL);
SKIP_AFTER_FORK(PZ_Lock(dbLock));
(*db->close)(db);
SKIP_AFTER_FORK(PZ_Unlock(dbLock));
return ;
}
void
pkcs11_freeNickname(char *nickname, char *space)
{
if (nickname && nickname != space) {
PORT_Free(nickname);
}
}
char *
pkcs11_copyNickname(char *nickname, char *space, int spaceLen)
{
int len;
char *copy = NULL;
len = PORT_Strlen(nickname) + 1;
if (len <= spaceLen) {
copy = space;
PORT_Memcpy(copy, nickname, len);
} else {
copy = PORT_Strdup(nickname);
}
return copy;
}
void
pkcs11_freeStaticData(unsigned char *data, unsigned char *space)
{
if (data && data != space) {
PORT_Free(data);
}
}
unsigned char *
pkcs11_allocStaticData(int len, unsigned char *space, int spaceLen)
{
unsigned char *data = NULL;
if (len <= spaceLen) {
data = space;
} else {
data = (unsigned char *)PORT_Alloc(len);
}
return data;
}
unsigned char *
pkcs11_copyStaticData(unsigned char *data, int len,
unsigned char *space, int spaceLen)
{
unsigned char *copy = pkcs11_allocStaticData(len, space, spaceLen);
if (copy) {
PORT_Memcpy(copy, data, len);
}
return copy;
}
/*
* destroy a database entry
*/
static void
DestroyDBEntry(certDBEntry *entry)
{
PLArenaPool *arena = entry->common.arena;
/* must be one of our certDBEntry from the free list */
if (arena == NULL) {
certDBEntryCert *certEntry;
if (entry->common.type != certDBEntryTypeCert) {
return ;
}
certEntry = (certDBEntryCert *)entry;
pkcs11_freeStaticData(certEntry->derCert.data, certEntry->derCertSpace);
pkcs11_freeNickname(certEntry->nickname, certEntry->nicknameSpace);
nsslowcert_LockFreeList();
if (entryListCount > MAX_ENTRY_LIST_COUNT) {
PORT_Free(certEntry);
} else {
entryListCount++;
PORT_Memset(certEntry, 0, sizeof (*certEntry));
certEntry->next = entryListHead;
entryListHead = certEntry;
}
nsslowcert_UnlockFreeList();
return ;
}
/* Zero out the entry struct, so that any further attempts to use it
* will cause an exception (e.g. null pointer reference). */
PORT_Memset(&entry->common, 0, sizeof entry->common);
PORT_FreeArena(arena, PR_FALSE);
return ;
}
/* forward references */
static void nsslowcert_DestroyCertificateNoLocking(NSSLOWCERTCertificate *cert);
static SECStatus
DeleteDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryType type, SECItem *dbkey)
{
DBT key;
int ret;
/* init the database key */
key.data = dbkey->data;
key.size = dbkey->len;
dbkey->data[0] = (unsigned char )type;
/* delete entry from database */
ret = certdb_Del(handle->permCertDB, &key, 0);
if (ret != 0) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
ret = certdb_Sync(handle->permCertDB, 0);
if (ret) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
return (SECSuccess);
loser:
return (SECFailure);
}
static SECStatus
ReadDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCommon *entry,
SECItem *dbkey, SECItem *dbentry, PLArenaPool *arena)
{
DBT data, key;
int ret;
unsigned char *buf;
/* init the database key */
key.data = dbkey->data;
key.size = dbkey->len;
dbkey->data[0] = (unsigned char )entry->type;
/* read entry from database */
ret = certdb_Get(handle->permCertDB, &key, &data, 0);
if (ret != 0) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* validate the entry */
if (data.size < SEC_DB_ENTRY_HEADER_LEN) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
buf = (unsigned char *)data.data;
/* version 7 has the same schema, we may be using a v7 db if we openned
* the databases readonly. */
if (!((buf[0] == (unsigned char )CERT_DB_FILE_VERSION) ||
(buf[0] == (unsigned char )CERT_DB_V7_FILE_VERSION))) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
if (buf[1] != (unsigned char )entry->type) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* copy out header information */
entry->version = (unsigned int )buf[0];
entry->type = (certDBEntryType)buf[1];
entry->flags = (unsigned int )buf[2];
/* format body of entry for return to caller */
dbentry->len = data.size - SEC_DB_ENTRY_HEADER_LEN;
if (dbentry->len) {
if (arena) {
dbentry->data = (unsigned char *)
PORT_ArenaAlloc(arena, dbentry->len);
if (dbentry->data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(dbentry->data, &buf[SEC_DB_ENTRY_HEADER_LEN],
dbentry->len);
} else {
dbentry->data = &buf[SEC_DB_ENTRY_HEADER_LEN];
}
} else {
dbentry->data = NULL;
}
return (SECSuccess);
loser:
return (SECFailure);
}
/**
** Implement low level database access
**/
static SECStatus
WriteDBEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCommon *entry,
SECItem *dbkey, SECItem *dbentry)
{
int ret;
DBT data, key;
unsigned char *buf;
data.data = dbentry->data;
data.size = dbentry->len;
buf = (unsigned char *)data.data;
buf[0] = (unsigned char )entry->version;
buf[1] = (unsigned char )entry->type;
buf[2] = (unsigned char )entry->flags;
key.data = dbkey->data;
key.size = dbkey->len;
dbkey->data[0] = (unsigned char )entry->type;
/* put the record into the database now */
ret = certdb_Put(handle->permCertDB, &key, &data, 0);
if (ret != 0) {
goto loser;
}
ret = certdb_Sync(handle->permCertDB, 0);
if (ret) {
goto loser;
}
return (SECSuccess);
loser:
return (SECFailure);
}
/*
* encode a database cert record
*/
static SECStatus
EncodeDBCertEntry(certDBEntryCert *entry, PLArenaPool *arena, SECItem *dbitem)
{
unsigned int nnlen;
unsigned char *buf;
char *nn;
char zbuf = 0;
if (entry->nickname) {
nn = entry->nickname;
} else {
nn = &zbuf;
}
nnlen = PORT_Strlen(nn) + 1;
/* allocate space for encoded database record, including space
* for low level header
*/
dbitem->len = entry->derCert.len + nnlen + DB_CERT_ENTRY_HEADER_LEN +
SEC_DB_ENTRY_HEADER_LEN;
dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
if (dbitem->data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/* fill in database record */
buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
buf[0] = (PRUint8)(entry->trust.sslFlags >> 8);
buf[1] = (PRUint8)(entry->trust.sslFlags);
buf[2] = (PRUint8)(entry->trust.emailFlags >> 8);
buf[3] = (PRUint8)(entry->trust.emailFlags);
buf[4] = (PRUint8)(entry->trust.objectSigningFlags >> 8);
buf[5] = (PRUint8)(entry->trust.objectSigningFlags);
buf[6] = (PRUint8)(entry->derCert.len >> 8);
buf[7] = (PRUint8)(entry->derCert.len);
buf[8] = (PRUint8)(nnlen >> 8);
buf[9] = (PRUint8)(nnlen);
PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN], entry->derCert.data,
entry->derCert.len);
PORT_Memcpy(&buf[DB_CERT_ENTRY_HEADER_LEN + entry->derCert.len],
nn, nnlen);
return (SECSuccess);
loser:
return (SECFailure);
}
/*
* encode a database key for a cert record
*/
static SECStatus
EncodeDBCertKey(const SECItem *certKey, PLArenaPool *arena, SECItem *dbkey)
{
unsigned int len = certKey->len + SEC_DB_KEY_HEADER_LEN;
if (len > NSS_MAX_LEGACY_DB_KEY_SIZE)
goto loser;
if (arena) {
dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, len);
} else {
if (dbkey->len < len) {
dbkey->data = (unsigned char *)PORT_Alloc(len);
}
}
dbkey->len = len;
if (dbkey->data == NULL) {
goto loser;
}
PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],
certKey->data, certKey->len);
dbkey->data[0] = certDBEntryTypeCert;
return (SECSuccess);
loser:
return (SECFailure);
}
static SECStatus
EncodeDBGenericKey(const SECItem *certKey, PLArenaPool *arena, SECItem *dbkey,
certDBEntryType entryType)
{
/*
* we only allow _one_ KRL key!
*/
if (entryType == certDBEntryTypeKeyRevocation) {
dbkey->len = SEC_DB_KEY_HEADER_LEN;
dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
if (dbkey->data == NULL) {
goto loser;
}
dbkey->data[0] = (unsigned char )entryType;
return (SECSuccess);
}
dbkey->len = certKey->len + SEC_DB_KEY_HEADER_LEN;
if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
goto loser;
dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
if (dbkey->data == NULL) {
goto loser;
}
PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN],
certKey->data, certKey->len);
dbkey->data[0] = (unsigned char )entryType;
return (SECSuccess);
loser:
return (SECFailure);
}
static SECStatus
DecodeDBCertEntry(certDBEntryCert *entry, SECItem *dbentry)
{
unsigned int nnlen;
unsigned int headerlen;
int lenoff;
/* allow updates of old versions of the database */
switch (entry->common.version) {
case 5:
headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
lenoff = 3;
break ;
case 6:
/* should not get here */
PORT_Assert(0);
headerlen = DB_CERT_V6_ENTRY_HEADER_LEN;
lenoff = 3;
break ;
case 7:
case 8:
headerlen = DB_CERT_ENTRY_HEADER_LEN;
lenoff = 6;
break ;
default :
/* better not get here */
PORT_Assert(0);
headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
lenoff = 3;
break ;
}
/* is record long enough for header? */
if (dbentry->len < headerlen) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* is database entry correct length? */
entry->derCert.len = ((dbentry->data[lenoff] << 8) |
dbentry->data[lenoff + 1]);
nnlen = ((dbentry->data[lenoff + 2] << 8) | dbentry->data[lenoff + 3]);
lenoff = dbentry->len - (entry->derCert.len + nnlen + headerlen);
if (lenoff) {
if (lenoff < 0 || (lenoff & 0xffff) != 0) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* The cert size exceeded 64KB. Reconstruct the correct length. */
entry->derCert.len += lenoff;
}
/* Is data long enough? */
if (dbentry->len < headerlen + entry->derCert.len) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* copy the dercert */
entry->derCert.data = pkcs11_copyStaticData(&dbentry->data[headerlen],
entry->derCert.len, entry->derCertSpace, sizeof (entry->derCertSpace));
if (entry->derCert.data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/* copy the nickname */
if (nnlen > 1) {
/* Is data long enough? */
if (dbentry->len < headerlen + entry->derCert.len + nnlen) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
entry->nickname = (char *)pkcs11_copyStaticData(
&dbentry->data[headerlen + entry->derCert.len], nnlen,
(unsigned char *)entry->nicknameSpace,
sizeof (entry->nicknameSpace));
if (entry->nickname == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
} else {
entry->nickname = NULL;
}
if (entry->common.version < 7) {
/* allow updates of v5 db */
entry->trust.sslFlags = dbentry->data[0];
entry->trust.emailFlags = dbentry->data[1];
entry->trust.objectSigningFlags = dbentry->data[2];
} else {
entry->trust.sslFlags = (dbentry->data[0] << 8) | dbentry->data[1];
entry->trust.emailFlags = (dbentry->data[2] << 8) | dbentry->data[3];
entry->trust.objectSigningFlags =
(dbentry->data[4] << 8) | dbentry->data[5];
}
return (SECSuccess);
loser:
return (SECFailure);
}
/*
* Create a new certDBEntryCert from existing data
*/
static certDBEntryCert *
NewDBCertEntry(SECItem *derCert, char *nickname,
NSSLOWCERTCertTrust *trust, int flags)
{
certDBEntryCert *entry;
PLArenaPool *arena = NULL;
int nnlen;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena) {
goto loser;
}
entry = PORT_ArenaZNew(arena, certDBEntryCert);
if (entry == NULL) {
goto loser;
}
/* fill in the dbCert */
entry->common.arena = arena;
entry->common.type = certDBEntryTypeCert;
entry->common.version = CERT_DB_FILE_VERSION;
entry->common.flags = flags;
if (trust) {
entry->trust = *trust;
}
entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, derCert->len);
if (!entry->derCert.data) {
goto loser;
}
entry->derCert.len = derCert->len;
PORT_Memcpy(entry->derCert.data, derCert->data, derCert->len);
nnlen = (nickname ? strlen(nickname) + 1 : 0);
if (nnlen) {
entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
if (!entry->nickname) {
goto loser;
}
PORT_Memcpy(entry->nickname, nickname, nnlen);
} else {
entry->nickname = 0;
}
return (entry);
loser:
/* allocation error, free arena and return */
if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
PORT_SetError(SEC_ERROR_NO_MEMORY);
return (0);
}
/*
* Decode a version 4 DBCert from the byte stream database format
* and construct a current database entry struct
*/
static certDBEntryCert *
DecodeV4DBCertEntry(unsigned char *buf, int len)
{
certDBEntryCert *entry;
int certlen;
int nnlen;
PLArenaPool *arena;
/* make sure length is at least long enough for the header */
if (len < DBCERT_V4_HEADER_LEN) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
return (0);
}
/* get other lengths */
certlen = buf[3] << 8 | buf[4];
nnlen = buf[5] << 8 | buf[6];
/* make sure DB entry is the right size */
if ((certlen + nnlen + DBCERT_V4_HEADER_LEN) != len) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
return (0);
}
/* allocate arena */
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
return (0);
}
/* allocate structure and members */
entry = (certDBEntryCert *)PORT_ArenaAlloc(arena, sizeof (certDBEntryCert));
if (!entry) {
goto loser;
}
entry->common.arena = arena;
entry->common.version = CERT_DB_FILE_VERSION;
entry->common.type = certDBEntryTypeCert;
entry->common.flags = 0;
entry->trust.sslFlags = buf[0];
entry->trust.emailFlags = buf[1];
entry->trust.objectSigningFlags = buf[2];
entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, certlen);
if (!entry->derCert.data) {
goto loser;
}
entry->derCert.len = certlen;
PORT_Memcpy(entry->derCert.data, &buf[DBCERT_V4_HEADER_LEN], certlen);
if (nnlen) {
entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
if (!entry->nickname) {
goto loser;
}
PORT_Memcpy(entry->nickname, &buf[DBCERT_V4_HEADER_LEN + certlen], nnlen);
if (PORT_Strcmp(entry->nickname, "Server-Cert" ) == 0) {
entry->trust.sslFlags |= CERTDB_USER;
}
} else {
entry->nickname = 0;
}
return (entry);
loser:
PORT_FreeArena(arena, PR_FALSE);
PORT_SetError(SEC_ERROR_NO_MEMORY);
return (0);
}
/*
* Encode a Certificate database entry into byte stream suitable for
* the database
*/
static SECStatus
WriteDBCertEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry)
{
SECItem dbitem, dbkey;
PLArenaPool *tmparena = NULL;
SECItem tmpitem;
SECStatus rv;
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (tmparena == NULL) {
goto loser;
}
rv = EncodeDBCertEntry(entry, tmparena, &dbitem);
if (rv != SECSuccess) {
goto loser;
}
/* get the database key and format it */
rv = nsslowcert_KeyFromDERCert(tmparena, &entry->derCert, &tmpitem);
if (rv == SECFailure) {
goto loser;
}
rv = EncodeDBCertKey(&tmpitem, tmparena, &dbkey);
if (rv == SECFailure) {
goto loser;
}
/* now write it to the database */
rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
if (rv != SECSuccess) {
goto loser;
}
PORT_FreeArena(tmparena, PR_FALSE);
return (SECSuccess);
loser:
if (tmparena) {
PORT_FreeArena(tmparena, PR_FALSE);
}
return (SECFailure);
}
/*
* delete a certificate entry
*/
static SECStatus
DeleteDBCertEntry(NSSLOWCERTCertDBHandle *handle, SECItem *certKey)
{
SECItem dbkey;
SECStatus rv;
dbkey.data = NULL;
dbkey.len = 0;
rv = EncodeDBCertKey(certKey, NULL, &dbkey);
if (rv != SECSuccess) {
goto loser;
}
rv = DeleteDBEntry(handle, certDBEntryTypeCert, &dbkey);
if (rv == SECFailure) {
goto loser;
}
PORT_Free(dbkey.data);
return (SECSuccess);
loser:
if (dbkey.data) {
PORT_Free(dbkey.data);
}
return (SECFailure);
}
static certDBEntryCert *
CreateCertEntry(void )
{
certDBEntryCert *entry;
nsslowcert_LockFreeList();
entry = entryListHead;
if (entry) {
entryListCount--;
entryListHead = entry->next;
}
PORT_Assert(entryListCount >= 0);
nsslowcert_UnlockFreeList();
if (entry) {
return entry;
}
return PORT_ZNew(certDBEntryCert);
}
static void
DestroyCertEntryFreeList(void )
{
certDBEntryCert *entry;
nsslowcert_LockFreeList();
while (NULL != (entry = entryListHead)) {
entryListCount--;
entryListHead = entry->next;
PORT_Free(entry);
}
PORT_Assert(!entryListCount);
entryListCount = 0;
nsslowcert_UnlockFreeList();
}
/*
* Read a certificate entry
*/
static certDBEntryCert *
ReadDBCertEntry(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
{
certDBEntryCert *entry;
SECItem dbkey;
SECItem dbentry;
SECStatus rv;
unsigned char buf[512];
dbkey.data = buf;
dbkey.len = sizeof (buf);
entry = CreateCertEntry();
if (entry == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry->common.arena = NULL;
entry->common.type = certDBEntryTypeCert;
rv = EncodeDBCertKey(certKey, NULL, &dbkey);
if (rv != SECSuccess) {
goto loser;
}
rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, NULL);
if (rv == SECFailure) {
goto loser;
}
rv = DecodeDBCertEntry(entry, &dbentry);
if (rv != SECSuccess) {
goto loser;
}
pkcs11_freeStaticData(dbkey.data, buf);
dbkey.data = NULL;
return (entry);
loser:
pkcs11_freeStaticData(dbkey.data, buf);
dbkey.data = NULL;
if (entry) {
DestroyDBEntry((certDBEntry *)entry);
}
return (NULL);
}
/*
* encode a database cert record
*/
static SECStatus
EncodeDBCrlEntry(certDBEntryRevocation *entry, PLArenaPool *arena, SECItem *dbitem)
{
unsigned int nnlen = 0;
unsigned char *buf;
if (entry->url) {
nnlen = PORT_Strlen(entry->url) + 1;
}
/* allocate space for encoded database record, including space
* for low level header
*/
dbitem->len = entry->derCrl.len + nnlen + SEC_DB_ENTRY_HEADER_LEN + DB_CRL_ENTRY_HEADER_LEN;
dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
if (dbitem->data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/* fill in database record */
buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
buf[0] = (PRUint8)(entry->derCrl.len >> 8);
buf[1] = (PRUint8)(entry->derCrl.len);
buf[2] = (PRUint8)(nnlen >> 8);
buf[3] = (PRUint8)(nnlen);
PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN], entry->derCrl.data,
entry->derCrl.len);
if (nnlen != 0) {
PORT_Memcpy(&buf[DB_CRL_ENTRY_HEADER_LEN + entry->derCrl.len],
entry->url, nnlen);
}
return (SECSuccess);
loser:
return (SECFailure);
}
static SECStatus
DecodeDBCrlEntry(certDBEntryRevocation *entry, SECItem *dbentry)
{
unsigned int urlLen;
int lenDiff;
/* is record long enough for header? */
if (dbentry->len < DB_CRL_ENTRY_HEADER_LEN) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* is database entry correct length? */
entry->derCrl.len = ((dbentry->data[0] << 8) | dbentry->data[1]);
urlLen = ((dbentry->data[2] << 8) | dbentry->data[3]);
lenDiff = dbentry->len -
(entry->derCrl.len + urlLen + DB_CRL_ENTRY_HEADER_LEN);
if (lenDiff) {
if (lenDiff < 0 || (lenDiff & 0xffff) != 0) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* CRL entry is greater than 64 K. Hack to make this continue to work */
entry->derCrl.len += lenDiff;
}
/* copy the der CRL */
entry->derCrl.data = (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
entry->derCrl.len);
if (entry->derCrl.data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->derCrl.data, &dbentry->data[DB_CRL_ENTRY_HEADER_LEN],
entry->derCrl.len);
/* copy the url */
entry->url = NULL;
if (urlLen != 0) {
entry->url = (char *)PORT_ArenaAlloc(entry->common.arena, urlLen);
if (entry->url == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->url,
&dbentry->data[DB_CRL_ENTRY_HEADER_LEN + entry->derCrl.len],
urlLen);
}
return (SECSuccess);
loser:
return (SECFailure);
}
/*
* Create a new certDBEntryRevocation from existing data
*/
static certDBEntryRevocation *
NewDBCrlEntry(SECItem *derCrl, char *url, certDBEntryType crlType, int flags)
{
certDBEntryRevocation *entry;
PLArenaPool *arena = NULL;
int nnlen;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (!arena) {
goto loser;
}
entry = PORT_ArenaZNew(arena, certDBEntryRevocation);
if (entry == NULL) {
goto loser;
}
/* fill in the dbRevolcation */
entry->common.arena = arena;
entry->common.type = crlType;
entry->common.version = CERT_DB_FILE_VERSION;
entry->common.flags = flags;
entry->derCrl.data = (unsigned char *)PORT_ArenaAlloc(arena, derCrl->len);
if (!entry->derCrl.data) {
goto loser;
}
if (url) {
nnlen = PORT_Strlen(url) + 1;
entry->url = (char *)PORT_ArenaAlloc(arena, nnlen);
if (!entry->url) {
goto loser;
}
PORT_Memcpy(entry->url, url, nnlen);
} else {
entry->url = NULL;
}
entry->derCrl.len = derCrl->len;
PORT_Memcpy(entry->derCrl.data, derCrl->data, derCrl->len);
return (entry);
loser:
/* allocation error, free arena and return */
if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
PORT_SetError(SEC_ERROR_NO_MEMORY);
return (0);
}
static SECStatus
WriteDBCrlEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryRevocation *entry,
SECItem *crlKey)
{
SECItem dbkey;
PLArenaPool *tmparena = NULL;
SECItem encodedEntry;
SECStatus rv;
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (tmparena == NULL) {
goto loser;
}
rv = EncodeDBCrlEntry(entry, tmparena, &encodedEntry);
if (rv == SECFailure) {
goto loser;
}
rv = EncodeDBGenericKey(crlKey, tmparena, &dbkey, entry->common.type);
if (rv == SECFailure) {
goto loser;
}
/* now write it to the database */
rv = WriteDBEntry(handle, &entry->common, &dbkey, &encodedEntry);
if (rv != SECSuccess) {
goto loser;
}
PORT_FreeArena(tmparena, PR_FALSE);
return (SECSuccess);
loser:
if (tmparena) {
PORT_FreeArena(tmparena, PR_FALSE);
}
return (SECFailure);
}
/*
* delete a crl entry
*/
static SECStatus
DeleteDBCrlEntry(NSSLOWCERTCertDBHandle *handle, const SECItem *crlKey,
certDBEntryType crlType)
{
SECItem dbkey;
PLArenaPool *arena = NULL;
SECStatus rv;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) {
goto loser;
}
rv = EncodeDBGenericKey(crlKey, arena, &dbkey, crlType);
if (rv != SECSuccess) {
goto loser;
}
rv = DeleteDBEntry(handle, crlType, &dbkey);
if (rv == SECFailure) {
goto loser;
}
PORT_FreeArena(arena, PR_FALSE);
return (SECSuccess);
loser:
if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
return (SECFailure);
}
/*
* Read a certificate entry
*/
static certDBEntryRevocation *
ReadDBCrlEntry(NSSLOWCERTCertDBHandle *handle, SECItem *certKey,
certDBEntryType crlType)
{
PLArenaPool *arena = NULL;
PLArenaPool *tmparena = NULL;
certDBEntryRevocation *entry;
SECItem dbkey;
SECItem dbentry;
SECStatus rv;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (tmparena == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry = (certDBEntryRevocation *)
PORT_ArenaAlloc(arena, sizeof (certDBEntryRevocation));
if (entry == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry->common.arena = arena;
entry->common.type = crlType;
rv = EncodeDBGenericKey(certKey, tmparena, &dbkey, crlType);
if (rv != SECSuccess) {
goto loser;
}
rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, NULL);
if (rv == SECFailure) {
goto loser;
}
rv = DecodeDBCrlEntry(entry, &dbentry);
if (rv != SECSuccess) {
goto loser;
}
PORT_FreeArena(tmparena, PR_FALSE);
return (entry);
loser:
if (tmparena) {
PORT_FreeArena(tmparena, PR_FALSE);
}
if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
return (NULL);
}
void
nsslowcert_DestroyDBEntry(certDBEntry *entry)
{
DestroyDBEntry(entry);
return ;
}
/*
* Encode a database nickname record
*/
static SECStatus
EncodeDBNicknameEntry(certDBEntryNickname *entry, PLArenaPool *arena,
SECItem *dbitem)
{
unsigned char *buf;
/* allocate space for encoded database record, including space
* for low level header
*/
dbitem->len = entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN +
SEC_DB_ENTRY_HEADER_LEN;
dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
if (dbitem->data == NULL) {
goto loser;
}
/* fill in database record */
buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
buf[0] = (PRUint8)(entry->subjectName.len >> 8);
buf[1] = (PRUint8)(entry->subjectName.len);
PORT_Memcpy(&buf[DB_NICKNAME_ENTRY_HEADER_LEN], entry->subjectName.data,
entry->subjectName.len);
return (SECSuccess);
loser:
return (SECFailure);
}
/*
* Encode a database key for a nickname record
*/
static SECStatus
EncodeDBNicknameKey(char *nickname, PLArenaPool *arena,
SECItem *dbkey)
{
unsigned int nnlen;
nnlen = PORT_Strlen(nickname) + 1; /* includes null */
/* now get the database key and format it */
dbkey->len = nnlen + SEC_DB_KEY_HEADER_LEN;
if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
goto loser;
dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
if (dbkey->data == NULL) {
goto loser;
}
PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], nickname, nnlen);
dbkey->data[0] = certDBEntryTypeNickname;
return (SECSuccess);
loser:
return (SECFailure);
}
static SECStatus
DecodeDBNicknameEntry(certDBEntryNickname *entry, SECItem *dbentry,
char *nickname)
{
int lenDiff;
/* is record long enough for header? */
if (dbentry->len < DB_NICKNAME_ENTRY_HEADER_LEN) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* is database entry correct length? */
entry->subjectName.len = ((dbentry->data[0] << 8) | dbentry->data[1]);
lenDiff = dbentry->len -
(entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN);
if (lenDiff) {
if (lenDiff < 0 || (lenDiff & 0xffff) != 0) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* The entry size exceeded 64KB. Reconstruct the correct length. */
entry->subjectName.len += lenDiff;
}
/* copy the certkey */
entry->subjectName.data =
(unsigned char *)PORT_ArenaAlloc(entry->common.arena,
entry->subjectName.len);
if (entry->subjectName.data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->subjectName.data,
&dbentry->data[DB_NICKNAME_ENTRY_HEADER_LEN],
entry->subjectName.len);
entry->subjectName.type = siBuffer;
entry->nickname = (char *)PORT_ArenaAlloc(entry->common.arena,
PORT_Strlen(nickname) + 1);
if (entry->nickname) {
PORT_Strcpy(entry->nickname, nickname);
}
return (SECSuccess);
loser:
return (SECFailure);
}
/*
* create a new nickname entry
*/
static certDBEntryNickname *
NewDBNicknameEntry(char *nickname, SECItem *subjectName, unsigned int flags)
{
PLArenaPool *arena = NULL;
certDBEntryNickname *entry;
int nnlen;
SECStatus rv;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry = (certDBEntryNickname *)PORT_ArenaAlloc(arena,
sizeof (certDBEntryNickname));
if (entry == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/* init common fields */
entry->common.arena = arena;
entry->common.type = certDBEntryTypeNickname;
entry->common.version = CERT_DB_FILE_VERSION;
entry->common.flags = flags;
/* copy the nickname */
nnlen = PORT_Strlen(nickname) + 1;
entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
if (entry->nickname == NULL) {
goto loser;
}
PORT_Memcpy(entry->nickname, nickname, nnlen);
rv = SECITEM_CopyItem(arena, &entry->subjectName, subjectName);
if (rv != SECSuccess) {
goto loser;
}
return (entry);
loser:
if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
return (NULL);
}
/*
* delete a nickname entry
*/
static SECStatus
DeleteDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, char *nickname)
{
PLArenaPool *arena = NULL;
SECStatus rv;
SECItem dbkey;
if (nickname == NULL) {
return (SECSuccess);
}
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) {
goto loser;
}
rv = EncodeDBNicknameKey(nickname, arena, &dbkey);
if (rv != SECSuccess) {
goto loser;
}
rv = DeleteDBEntry(handle, certDBEntryTypeNickname, &dbkey);
if (rv == SECFailure) {
goto loser;
}
PORT_FreeArena(arena, PR_FALSE);
return (SECSuccess);
loser:
if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
return (SECFailure);
}
/*
* Read a nickname entry
*/
static certDBEntryNickname *
ReadDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, char *nickname)
{
PLArenaPool *arena = NULL;
PLArenaPool *tmparena = NULL;
certDBEntryNickname *entry;
SECItem dbkey;
SECItem dbentry;
SECStatus rv;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (tmparena == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry = (certDBEntryNickname *)PORT_ArenaAlloc(arena,
sizeof (certDBEntryNickname));
if (entry == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry->common.arena = arena;
entry->common.type = certDBEntryTypeNickname;
rv = EncodeDBNicknameKey(nickname, tmparena, &dbkey);
if (rv != SECSuccess) {
goto loser;
}
rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
if (rv == SECFailure) {
goto loser;
}
/* is record long enough for header? */
if (dbentry.len < DB_NICKNAME_ENTRY_HEADER_LEN) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
rv = DecodeDBNicknameEntry(entry, &dbentry, nickname);
if (rv != SECSuccess) {
goto loser;
}
PORT_FreeArena(tmparena, PR_FALSE);
return (entry);
loser:
if (tmparena) {
PORT_FreeArena(tmparena, PR_FALSE);
}
if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
return (NULL);
}
/*
* Encode a nickname entry into byte stream suitable for
* the database
*/
static SECStatus
WriteDBNicknameEntry(NSSLOWCERTCertDBHandle *handle, certDBEntryNickname *entry)
{
SECItem dbitem, dbkey;
PLArenaPool *tmparena = NULL;
SECStatus rv;
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (tmparena == NULL) {
goto loser;
}
rv = EncodeDBNicknameEntry(entry, tmparena, &dbitem);
if (rv != SECSuccess) {
goto loser;
}
rv = EncodeDBNicknameKey(entry->nickname, tmparena, &dbkey);
if (rv != SECSuccess) {
goto loser;
}
/* now write it to the database */
rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
if (rv != SECSuccess) {
goto loser;
}
PORT_FreeArena(tmparena, PR_FALSE);
return (SECSuccess);
loser:
if (tmparena) {
PORT_FreeArena(tmparena, PR_FALSE);
}
return (SECFailure);
}
static SECStatus
EncodeDBSMimeEntry(certDBEntrySMime *entry, PLArenaPool *arena,
SECItem *dbitem)
{
unsigned char *buf;
/* allocate space for encoded database record, including space
* for low level header
*/
dbitem->len = entry->subjectName.len + entry->smimeOptions.len +
entry->optionsDate.len +
DB_SMIME_ENTRY_HEADER_LEN + SEC_DB_ENTRY_HEADER_LEN;
dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
if (dbitem->data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/* fill in database record */
buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
buf[0] = (PRUint8)(entry->subjectName.len >> 8);
buf[1] = (PRUint8)(entry->subjectName.len);
buf[2] = (PRUint8)(entry->smimeOptions.len >> 8);
buf[3] = (PRUint8)(entry->smimeOptions.len);
buf[4] = (PRUint8)(entry->optionsDate.len >> 8);
buf[5] = (PRUint8)(entry->optionsDate.len);
/* if no smime options, then there should not be an options date either */
PORT_Assert(!((entry->smimeOptions.len == 0) &&
(entry->optionsDate.len != 0)));
PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN], entry->subjectName.data,
entry->subjectName.len);
if (entry->smimeOptions.len) {
PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN + entry->subjectName.len],
entry->smimeOptions.data,
entry->smimeOptions.len);
PORT_Memcpy(&buf[DB_SMIME_ENTRY_HEADER_LEN + entry->subjectName.len +
entry->smimeOptions.len],
entry->optionsDate.data,
entry->optionsDate.len);
}
return (SECSuccess);
loser:
return (SECFailure);
}
/*
* Encode a database key for a SMIME record
*/
static SECStatus
EncodeDBSMimeKey(char *emailAddr, PLArenaPool *arena,
SECItem *dbkey)
{
unsigned int addrlen;
addrlen = PORT_Strlen(emailAddr) + 1; /* includes null */
/* now get the database key and format it */
dbkey->len = addrlen + SEC_DB_KEY_HEADER_LEN;
if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
goto loser;
dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
if (dbkey->data == NULL) {
goto loser;
}
PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], emailAddr, addrlen);
dbkey->data[0] = certDBEntryTypeSMimeProfile;
return (SECSuccess);
loser:
return (SECFailure);
}
/*
* Decode a database SMIME record
*/
static SECStatus
DecodeDBSMimeEntry(certDBEntrySMime *entry, SECItem *dbentry, char *emailAddr)
{
int lenDiff;
/* is record long enough for header? */
if (dbentry->len < DB_SMIME_ENTRY_HEADER_LEN) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* is database entry correct length? */
entry->subjectName.len = ((dbentry->data[0] << 8) | dbentry->data[1]);
entry->smimeOptions.len = ((dbentry->data[2] << 8) | dbentry->data[3]);
entry->optionsDate.len = ((dbentry->data[4] << 8) | dbentry->data[5]);
lenDiff = dbentry->len - (entry->subjectName.len +
entry->smimeOptions.len +
entry->optionsDate.len +
DB_SMIME_ENTRY_HEADER_LEN);
if (lenDiff) {
if (lenDiff < 0 || (lenDiff & 0xffff) != 0) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
/* The entry size exceeded 64KB. Reconstruct the correct length. */
entry->subjectName.len += lenDiff;
}
/* copy the subject name */
entry->subjectName.data =
(unsigned char *)PORT_ArenaAlloc(entry->common.arena,
entry->subjectName.len);
if (entry->subjectName.data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->subjectName.data,
&dbentry->data[DB_SMIME_ENTRY_HEADER_LEN],
entry->subjectName.len);
/* copy the smime options */
if (entry->smimeOptions.len) {
entry->smimeOptions.data =
(unsigned char *)PORT_ArenaAlloc(entry->common.arena,
entry->smimeOptions.len);
if (entry->smimeOptions.data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->smimeOptions.data,
&dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
entry->subjectName.len],
entry->smimeOptions.len);
} else {
entry->smimeOptions.data = NULL;
}
if (entry->optionsDate.len) {
entry->optionsDate.data =
(unsigned char *)PORT_ArenaAlloc(entry->common.arena,
entry->optionsDate.len);
if (entry->optionsDate.data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->optionsDate.data,
&dbentry->data[DB_SMIME_ENTRY_HEADER_LEN +
entry->subjectName.len +
entry->smimeOptions.len],
entry->optionsDate.len);
} else {
entry->optionsDate.data = NULL;
}
/* both options and options date must either exist or not exist */
if (((entry->optionsDate.len == 0) ||
(entry->smimeOptions.len == 0)) &&
entry->smimeOptions.len != entry->optionsDate.len) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
entry->emailAddr = (char *)PORT_ArenaAlloc(entry->common.arena,
PORT_Strlen(emailAddr) + 1);
if (entry->emailAddr) {
PORT_Strcpy(entry->emailAddr, emailAddr);
}
return (SECSuccess);
loser:
return (SECFailure);
}
/*
* create a new SMIME entry
*/
static certDBEntrySMime *
NewDBSMimeEntry(char *emailAddr, SECItem *subjectName, SECItem *smimeOptions,
SECItem *optionsDate, unsigned int flags)
{
PLArenaPool *arena = NULL;
certDBEntrySMime *entry;
int addrlen;
SECStatus rv;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry = (certDBEntrySMime *)PORT_ArenaAlloc(arena,
sizeof (certDBEntrySMime));
if (entry == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/* init common fields */
entry->common.arena = arena;
entry->common.type = certDBEntryTypeSMimeProfile;
entry->common.version = CERT_DB_FILE_VERSION;
entry->common.flags = flags;
/* copy the email addr */
addrlen = PORT_Strlen(emailAddr) + 1;
entry->emailAddr = (char *)PORT_ArenaAlloc(arena, addrlen);
if (entry->emailAddr == NULL) {
goto loser;
}
PORT_Memcpy(entry->emailAddr, emailAddr, addrlen);
/* copy the subject name */
rv = SECITEM_CopyItem(arena, &entry->subjectName, subjectName);
if (rv != SECSuccess) {
goto loser;
}
/* copy the smime options */
if (smimeOptions) {
rv = SECITEM_CopyItem(arena, &entry->smimeOptions, smimeOptions);
if (rv != SECSuccess) {
goto loser;
}
} else {
PORT_Assert(optionsDate == NULL);
entry->smimeOptions.data = NULL;
entry->smimeOptions.len = 0;
}
/* copy the options date */
if (optionsDate) {
rv = SECITEM_CopyItem(arena, &entry->optionsDate, optionsDate);
if (rv != SECSuccess) {
goto loser;
}
} else {
PORT_Assert(smimeOptions == NULL);
entry->optionsDate.data = NULL;
entry->optionsDate.len = 0;
}
return (entry);
loser:
if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
return (NULL);
}
/*
* delete a SMIME entry
*/
static SECStatus
DeleteDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, char *emailAddr)
{
PLArenaPool *arena = NULL;
SECStatus rv;
SECItem dbkey;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) {
goto loser;
}
rv = EncodeDBSMimeKey(emailAddr, arena, &dbkey);
if (rv != SECSuccess) {
goto loser;
}
rv = DeleteDBEntry(handle, certDBEntryTypeSMimeProfile, &dbkey);
if (rv == SECFailure) {
goto loser;
}
PORT_FreeArena(arena, PR_FALSE);
return (SECSuccess);
loser:
if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
return (SECFailure);
}
/*
* Read a SMIME entry
*/
certDBEntrySMime *
nsslowcert_ReadDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, char *emailAddr)
{
PLArenaPool *arena = NULL;
PLArenaPool *tmparena = NULL;
certDBEntrySMime *entry = NULL;
SECItem dbkey;
SECItem dbentry;
SECStatus rv;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (tmparena == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry = (certDBEntrySMime *)PORT_ArenaZAlloc(arena,
sizeof (certDBEntrySMime));
if (entry == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry->common.arena = arena;
entry->common.type = certDBEntryTypeSMimeProfile;
rv = EncodeDBSMimeKey(emailAddr, tmparena, &dbkey);
if (rv != SECSuccess) {
goto loser;
}
rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
if (rv == SECFailure) {
goto loser;
}
/* is record long enough for header? */
if (dbentry.len < DB_SMIME_ENTRY_HEADER_LEN) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
rv = DecodeDBSMimeEntry(entry, &dbentry, emailAddr);
if (rv != SECSuccess) {
goto loser;
}
PORT_FreeArena(tmparena, PR_FALSE);
return (entry);
loser:
if (tmparena) {
PORT_FreeArena(tmparena, PR_FALSE);
}
if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
return (NULL);
}
/*
* Encode a SMIME entry into byte stream suitable for
* the database
*/
static SECStatus
WriteDBSMimeEntry(NSSLOWCERTCertDBHandle *handle, certDBEntrySMime *entry)
{
SECItem dbitem, dbkey;
PLArenaPool *tmparena = NULL;
SECStatus rv;
tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (tmparena == NULL) {
goto loser;
}
rv = EncodeDBSMimeEntry(entry, tmparena, &dbitem);
if (rv != SECSuccess) {
goto loser;
}
rv = EncodeDBSMimeKey(entry->emailAddr, tmparena, &dbkey);
if (rv != SECSuccess) {
goto loser;
}
/* now write it to the database */
rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem);
if (rv != SECSuccess) {
goto loser;
}
PORT_FreeArena(tmparena, PR_FALSE);
return (SECSuccess);
loser:
if (tmparena) {
PORT_FreeArena(tmparena, PR_FALSE);
}
return (SECFailure);
}
/*
* Encode a database subject record
*/
static SECStatus
EncodeDBSubjectEntry(certDBEntrySubject *entry, PLArenaPool *arena,
SECItem *dbitem)
{
unsigned char *buf;
int len;
unsigned int ncerts;
unsigned int i;
unsigned char *tmpbuf;
unsigned int nnlen = 0;
unsigned int eaddrslen = 0;
int keyidoff;
SECItem *certKeys = entry->certKeys;
SECItem *keyIDs = entry->keyIDs;
;
if (entry->nickname) {
nnlen = PORT_Strlen(entry->nickname) + 1;
}
if (entry->emailAddrs) {
eaddrslen = 2;
for (i = 0; i < entry->nemailAddrs; i++) {
eaddrslen += PORT_Strlen(entry->emailAddrs[i]) + 1 + 2;
}
}
ncerts = entry->ncerts;
/* compute the length of the entry */
keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen;
len = keyidoff + (4 * ncerts) + eaddrslen;
for (i = 0; i < ncerts; i++) {
if (keyIDs[i].len > 0xffff ||
(certKeys[i].len > 0xffff)) {
PORT_SetError(SEC_ERROR_INPUT_LEN);
goto loser;
}
len += certKeys[i].len;
len += keyIDs[i].len;
}
/* allocate space for encoded database record, including space
* for low level header
*/
dbitem->len = len + SEC_DB_ENTRY_HEADER_LEN;
dbitem->data = (unsigned char *)PORT_ArenaAlloc(arena, dbitem->len);
if (dbitem->data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/* fill in database record */
buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
buf[0] = (PRUint8)(ncerts >> 8);
buf[1] = (PRUint8)(ncerts);
buf[2] = (PRUint8)(nnlen >> 8);
buf[3] = (PRUint8)(nnlen);
/* v7 email field is NULL in v8 */
buf[4] = 0;
buf[5] = 0;
PORT_Assert(DB_SUBJECT_ENTRY_HEADER_LEN == 6);
if (entry->nickname) {
PORT_Memcpy(&buf[DB_SUBJECT_ENTRY_HEADER_LEN], entry->nickname, nnlen);
}
tmpbuf = &buf[keyidoff];
for (i = 0; i < ncerts; i++) {
tmpbuf[0] = (PRUint8)(certKeys[i].len >> 8);
tmpbuf[1] = (PRUint8)(certKeys[i].len);
tmpbuf += 2;
}
for (i = 0; i < ncerts; i++) {
tmpbuf[0] = (PRUint8)(keyIDs[i].len >> 8);
tmpbuf[1] = (PRUint8)(keyIDs[i].len);
tmpbuf += 2;
}
for (i = 0; i < ncerts; i++) {
PORT_Memcpy(tmpbuf, certKeys[i].data, certKeys[i].len);
tmpbuf += certKeys[i].len;
}
for (i = 0; i < ncerts; i++) {
if (keyIDs[i].len) {
PORT_Memcpy(tmpbuf, keyIDs[i].data, keyIDs[i].len);
tmpbuf += keyIDs[i].len;
}
}
if (entry->emailAddrs) {
tmpbuf[0] = (PRUint8)(entry->nemailAddrs >> 8);
tmpbuf[1] = (PRUint8)(entry->nemailAddrs);
tmpbuf += 2;
for (i = 0; i < entry->nemailAddrs; i++) {
int nameLen = PORT_Strlen(entry->emailAddrs[i]) + 1;
tmpbuf[0] = (PRUint8)(nameLen >> 8);
tmpbuf[1] = (PRUint8)(nameLen);
tmpbuf += 2;
PORT_Memcpy(tmpbuf, entry->emailAddrs[i], nameLen);
tmpbuf += nameLen;
}
}
PORT_Assert(tmpbuf == &buf[len]);
return (SECSuccess);
loser:
return (SECFailure);
}
/*
* Encode a database key for a subject record
*/
static SECStatus
EncodeDBSubjectKey(SECItem *derSubject, PLArenaPool *arena,
SECItem *dbkey)
{
dbkey->len = derSubject->len + SEC_DB_KEY_HEADER_LEN;
if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE)
goto loser;
dbkey->data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey->len);
if (dbkey->data == NULL) {
goto loser;
}
PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], derSubject->data,
derSubject->len);
dbkey->data[0] = certDBEntryTypeSubject;
return (SECSuccess);
loser:
return (SECFailure);
}
static SECStatus
DecodeDBSubjectEntry(certDBEntrySubject *entry, SECItem *dbentry,
const SECItem *derSubject)
{
PLArenaPool *arena = entry->common.arena;
unsigned char *tmpbuf;
unsigned char *end;
void *mark = PORT_ArenaMark(arena);
unsigned int eaddrlen;
unsigned int i;
unsigned int keyidoff;
unsigned int len;
unsigned int ncerts = 0;
unsigned int nnlen;
SECStatus rv;
rv = SECITEM_CopyItem(arena, &entry->derSubject, derSubject);
if (rv != SECSuccess) {
goto loser;
}
/* is record long enough for header? */
if (dbentry->len < DB_SUBJECT_ENTRY_HEADER_LEN) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
entry->ncerts = ncerts = ((dbentry->data[0] << 8) | dbentry->data[1]);
nnlen = ((dbentry->data[2] << 8) | dbentry->data[3]);
eaddrlen = ((dbentry->data[4] << 8) | dbentry->data[5]);
keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen + eaddrlen;
len = keyidoff + (4 * ncerts);
if (dbentry->len < len) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
entry->certKeys = PORT_ArenaNewArray(arena, SECItem, ncerts);
entry->keyIDs = PORT_ArenaNewArray(arena, SECItem, ncerts);
if ((entry->certKeys == NULL) || (entry->keyIDs == NULL)) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
if (nnlen > 1) { /* null terminator is stored */
entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
if (entry->nickname == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->nickname,
&dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN],
nnlen);
} else {
entry->nickname = NULL;
}
/* if we have an old style email entry, there is only one */
entry->nemailAddrs = 0;
if (eaddrlen > 1) { /* null terminator is stored */
entry->emailAddrs = PORT_ArenaNewArray(arena, char *, 2);
if (entry->emailAddrs == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry->emailAddrs[0] = (char *)PORT_ArenaAlloc(arena, eaddrlen);
if (entry->emailAddrs[0] == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->emailAddrs[0],
&dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN + nnlen],
eaddrlen);
entry->nemailAddrs = 1;
} else {
entry->emailAddrs = NULL;
}
/* collect the lengths of the certKeys and keyIDs, and total the
* overall length.
*/
tmpbuf = &dbentry->data[keyidoff];
for (i = 0; i < ncerts; i++) {
unsigned int itemlen = (tmpbuf[0] << 8) | tmpbuf[1];
entry->certKeys[i].len = itemlen;
len += itemlen;
tmpbuf += 2;
}
for (i = 0; i < ncerts; i++) {
unsigned int itemlen = (tmpbuf[0] << 8) | tmpbuf[1];
entry->keyIDs[i].len = itemlen;
len += itemlen;
tmpbuf += 2;
}
/* is encoded entry large enough ? */
if (len > dbentry->len) {
PORT_SetError(SEC_ERROR_BAD_DATABASE);
goto loser;
}
for (i = 0; i < ncerts; i++) {
unsigned int kLen = entry->certKeys[i].len;
entry->certKeys[i].data = (unsigned char *)PORT_ArenaAlloc(arena, kLen);
if (entry->certKeys[i].data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->certKeys[i].data, tmpbuf, kLen);
tmpbuf += kLen;
}
for (i = 0; i < ncerts; i++) {
unsigned int iLen = entry->keyIDs[i].len;
entry->keyIDs[i].data = (unsigned char *)PORT_ArenaAlloc(arena, iLen);
if (entry->keyIDs[i].data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->keyIDs[i].data, tmpbuf, iLen);
tmpbuf += iLen;
}
end = dbentry->data + dbentry->len;
if ((eaddrlen == 0) && (end - tmpbuf > 1)) {
/* read in the additional email addresses */
entry->nemailAddrs = (((unsigned int )tmpbuf[0]) << 8) | tmpbuf[1];
tmpbuf += 2;
if (end - tmpbuf < 2 * (int )entry->nemailAddrs)
goto loser;
entry->emailAddrs = PORT_ArenaNewArray(arena, char *, entry->nemailAddrs);
if (entry->emailAddrs == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
for (i = 0; i < entry->nemailAddrs; i++) {
int nameLen;
if (end - tmpbuf < 2) {
goto loser;
}
nameLen = (((int )tmpbuf[0]) << 8) | tmpbuf[1];
tmpbuf += 2;
if (end - tmpbuf < nameLen) {
goto loser;
}
entry->emailAddrs[i] = PORT_ArenaAlloc(arena, nameLen);
if (entry->emailAddrs == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
PORT_Memcpy(entry->emailAddrs[i], tmpbuf, nameLen);
tmpbuf += nameLen;
}
if (tmpbuf != end)
goto loser;
}
PORT_ArenaUnmark(arena, mark);
return (SECSuccess);
loser:
PORT_ArenaRelease(arena, mark); /* discard above allocations */
return (SECFailure);
}
/*
* create a new subject entry with a single cert
*/
static certDBEntrySubject *
NewDBSubjectEntry(SECItem *derSubject, SECItem *certKey,
SECItem *keyID, char *nickname, char *emailAddr,
unsigned int flags)
{
PLArenaPool *arena = NULL;
certDBEntrySubject *entry;
SECStatus rv;
unsigned int nnlen;
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (arena == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
entry = (certDBEntrySubject *)PORT_ArenaAlloc(arena,
sizeof (certDBEntrySubject));
if (entry == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY);
goto loser;
}
/* init common fields */
entry->common.arena = arena;
entry->common.type = certDBEntryTypeSubject;
entry->common.version = CERT_DB_FILE_VERSION;
entry->common.flags = flags;
/* copy the subject */
rv = SECITEM_CopyItem(arena, &entry->derSubject, derSubject);
if (rv != SECSuccess) {
goto loser;
}
entry->ncerts = 1;
entry->nemailAddrs = 0;
/* copy nickname */
if (nickname && (*nickname != '\0' )) {
nnlen = PORT_Strlen(nickname) + 1;
entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen);
if (entry->nickname == NULL) {
goto loser;
}
PORT_Memcpy(entry->nickname, nickname, nnlen);
} else {
entry->nickname = NULL;
}
/* copy email addr */
if (emailAddr && (*emailAddr != '\0' )) {
emailAddr = nsslowcert_FixupEmailAddr(emailAddr);
if (emailAddr == NULL) {
entry->emailAddrs = NULL;
goto loser;
}
entry->emailAddrs = (char **)PORT_ArenaAlloc(arena, sizeof (char *));
if (entry->emailAddrs == NULL) {
PORT_Free(emailAddr);
goto loser;
}
entry->emailAddrs[0] = PORT_ArenaStrdup(arena, emailAddr);
if (entry->emailAddrs[0]) {
entry->nemailAddrs = 1;
}
PORT_Free(emailAddr);
} else {
entry->emailAddrs = NULL;
}
/* allocate space for certKeys and keyIDs */
entry->certKeys = (SECItem *)PORT_ArenaAlloc(arena, sizeof (SECItem));
entry->keyIDs = (SECItem *)PORT_ArenaAlloc(arena, sizeof (SECItem));
if ((entry->certKeys == NULL) || (entry->keyIDs == NULL)) {
goto loser;
}
/* copy the certKey and keyID */
rv = SECITEM_CopyItem(arena, &entry->certKeys[0], certKey);
if (rv != SECSuccess) {
goto loser;
}
rv = SECITEM_CopyItem(arena, &entry->keyIDs[0], keyID);
if (rv != SECSuccess) {
goto loser;
}
return (entry);
loser:
if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
return (NULL);
}
/*
* delete a subject entry
*/
static SECStatus
DeleteDBSubjectEntry(NSSLOWCERTCertDBHandle *handle, SECItem *derSubject)
{
SECItem dbkey;
PLArenaPool *arena = NULL;
SECStatus rv;
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=96 H=85 G=90
¤ Dauer der Verarbeitung: 0.22 Sekunden
¤
*© Formatika GbR, Deutschland