mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-11-13 11:10:13 +01:00
98d377b37b
bug920719, bug1026148, bug1028647, bug963150, bug1030486, bug1025729, bug836658, bug1028582, bug1038728, bug1038526, bug1042634, bug1047210, bug1043891, bug1043108, bug1046735, bug1043082, bug1036735, bug1046718, bug1050107, bug1054625, bug1057465, bug1057476, bug1041326, bug1058933, bug1064636, bug1057161, bug1078669, bug1049435, bug1070493, bug1083360, bug1028764, bug1065990, bug1073330, bug1064670, bug1094650
5356 lines
128 KiB
C
5356 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)
|
|
{
|
|
PRStatus prstat;
|
|
|
|
prstat = PZ_ExitMonitor(handle->dbMon);
|
|
|
|
PORT_Assert(prstat == PR_SUCCESS);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
PRStatus prstat;
|
|
|
|
PORT_Assert(certRefCountLock != NULL);
|
|
|
|
prstat = PZ_Unlock(certRefCountLock);
|
|
|
|
PORT_Assert(prstat == PR_SUCCESS);
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
PRStatus prstat;
|
|
|
|
PORT_Assert(certTrustLock != NULL);
|
|
|
|
prstat = PZ_Unlock(certTrustLock);
|
|
|
|
PORT_Assert(prstat == PR_SUCCESS);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
PRStatus prstat = PR_SUCCESS;
|
|
|
|
PORT_Assert(freeListLock != NULL);
|
|
|
|
SKIP_AFTER_FORK(prstat = PZ_Unlock(freeListLock));
|
|
|
|
PORT_Assert(prstat == PR_SUCCESS);
|
|
|
|
return;
|
|
}
|
|
|
|
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)
|
|
{
|
|
PRStatus prstat;
|
|
int ret;
|
|
|
|
PORT_Assert(dbLock != NULL);
|
|
PZ_Lock(dbLock);
|
|
|
|
ret = (* db->get)(db, key, data, flags);
|
|
|
|
prstat = PZ_Unlock(dbLock);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
certdb_Put(DB *db, DBT *key, DBT *data, unsigned int flags)
|
|
{
|
|
PRStatus prstat;
|
|
int ret = 0;
|
|
|
|
PORT_Assert(dbLock != NULL);
|
|
PZ_Lock(dbLock);
|
|
|
|
ret = (* db->put)(db, key, data, flags);
|
|
|
|
prstat = PZ_Unlock(dbLock);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
static int
|
|
certdb_Sync(DB *db, unsigned int flags)
|
|
{
|
|
PRStatus prstat;
|
|
int ret;
|
|
|
|
PORT_Assert(dbLock != NULL);
|
|
PZ_Lock(dbLock);
|
|
|
|
ret = (* db->sync)(db, flags);
|
|
|
|
prstat = 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)
|
|
{
|
|
PRStatus prstat;
|
|
int ret;
|
|
|
|
PORT_Assert(dbLock != NULL);
|
|
PZ_Lock(dbLock);
|
|
|
|
ret = (* db->del)(db, key, flags);
|
|
|
|
prstat = 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)
|
|
{
|
|
PRStatus prstat;
|
|
int ret;
|
|
|
|
PORT_Assert(dbLock != NULL);
|
|
PZ_Lock(dbLock);
|
|
|
|
ret = (* db->seq)(db, key, data, flags);
|
|
|
|
prstat = PZ_Unlock(dbLock);
|
|
|
|
return(ret);
|
|
}
|
|
|
|
static void
|
|
certdb_Close(DB *db)
|
|
{
|
|
PRStatus prstat = PR_SUCCESS;
|
|
|
|
PORT_Assert(dbLock != NULL);
|
|
SKIP_AFTER_FORK(PZ_Lock(dbLock));
|
|
|
|
(* db->close)(db);
|
|
|
|
SKIP_AFTER_FORK(prstat = 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;
|
|
unsigned int eaddrlen;
|
|
|
|
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;
|
|
}
|
|
|
|
eaddrlen = PORT_Strlen(emailAddr) + 1;
|
|
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 ) {
|
|
rv = DeleteDBCertEntry(handle, &cert->certKey);
|
|
}
|
|
if ( ( state > 1 ) && donnentry ) {
|
|
rv = DeleteDBNicknameEntry(handle, nickname);
|
|
}
|
|
if ( state > 2 ) {
|
|
rv = DeleteDBSubjectEntry(handle, &cert->derSubject);
|
|
}
|
|
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;
|
|
SECStatus rv;
|
|
|
|
/*
|
|
* 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);
|
|
rv = 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;
|
|
rv = 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;
|
|
SECStatus rv;
|
|
|
|
updatehandle.permCertDB = updatedb;
|
|
updatehandle.dbMon = PZ_NewMonitor(nssILockCertDB);
|
|
updatehandle.dbVerify = 0;
|
|
updatehandle.ref = 1; /* prevent premature close */
|
|
|
|
rv = 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;
|
|
DBT key;
|
|
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;
|
|
}
|
|
|
|
key.data = keyitem.data;
|
|
key.size = keyitem.len;
|
|
|
|
/* 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;
|
|
}
|
|
|