summaryrefslogtreecommitdiff
path: root/security/nss/lib/certdb/pcertdb.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/certdb/pcertdb.c')
-rw-r--r--security/nss/lib/certdb/pcertdb.c7292
1 files changed, 7292 insertions, 0 deletions
diff --git a/security/nss/lib/certdb/pcertdb.c b/security/nss/lib/certdb/pcertdb.c
new file mode 100644
index 000000000..aef986c01
--- /dev/null
+++ b/security/nss/lib/certdb/pcertdb.c
@@ -0,0 +1,7292 @@
+/*
+ * The contents of this file are subject to the Mozilla Public
+ * License Version 1.1 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS
+ * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
+ * implied. See the License for the specific language governing
+ * rights and limitations under the License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is Netscape
+ * Communications Corporation. Portions created by Netscape are
+ * Copyright (C) 1994-2000 Netscape Communications Corporation. All
+ * Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the
+ * terms of the GNU General Public License Version 2 or later (the
+ * "GPL"), in which case the provisions of the GPL are applicable
+ * instead of those above. If you wish to allow use of your
+ * version of this file only under the terms of the GPL and not to
+ * allow others to use your version of this file under the MPL,
+ * indicate your decision by deleting the provisions above and
+ * replace them with the notice and other provisions required by
+ * the GPL. If you do not delete the provisions above, a recipient
+ * may use your version of this file under either the MPL or the
+ * GPL.
+ */
+
+/*
+ * Permanent Certificate database handling code
+ *
+ * $Id$
+ */
+#include "prtime.h"
+
+#include "cert.h"
+#include "mcom_db.h"
+#include "certdb.h"
+#include "secitem.h"
+#include "secder.h"
+
+/* Call to PK11_FreeSlot below */
+
+#include "secasn1.h"
+#include "secerr.h"
+#include "prlock.h"
+#include "prmon.h"
+#include "nsslocks.h"
+#include "base64.h"
+#include "sechash.h"
+#include "plhash.h"
+
+#include "cdbhdl.h"
+
+/*
+ * the following functions are wrappers for the db library that implement
+ * a global lock to make the database thread safe.
+ */
+static PRLock *dbLock = NULL;
+
+void
+certdb_InitDBLock(void)
+{
+ if (dbLock == NULL) {
+ nss_InitLock(&dbLock);
+ PORT_Assert(dbLock != NULL);
+ }
+
+ return;
+}
+
+static int
+certdb_Get(DB *db, DBT *key, DBT *data, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+
+ PORT_Assert(dbLock != NULL);
+ PR_Lock(dbLock);
+
+ ret = (* db->get)(db, key, data, flags);
+
+ prstat = PR_Unlock(dbLock);
+
+ return(ret);
+}
+
+static int
+certdb_Put(DB *db, DBT *key, DBT *data, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+
+ PORT_Assert(dbLock != NULL);
+ PR_Lock(dbLock);
+
+ ret = (* db->put)(db, key, data, flags);
+
+ prstat = PR_Unlock(dbLock);
+
+ return(ret);
+}
+
+static int
+certdb_Sync(DB *db, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+
+ PORT_Assert(dbLock != NULL);
+ PR_Lock(dbLock);
+
+ ret = (* db->sync)(db, flags);
+
+ prstat = PR_Unlock(dbLock);
+
+ return(ret);
+}
+
+static int
+certdb_Del(DB *db, DBT *key, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+
+ PORT_Assert(dbLock != NULL);
+ PR_Lock(dbLock);
+
+ ret = (* db->del)(db, key, flags);
+
+ prstat = PR_Unlock(dbLock);
+
+ return(ret);
+}
+
+static int
+certdb_Seq(DB *db, DBT *key, DBT *data, unsigned int flags)
+{
+ PRStatus prstat;
+ int ret;
+
+ PORT_Assert(dbLock != NULL);
+ PR_Lock(dbLock);
+
+ ret = (* db->seq)(db, key, data, flags);
+
+ prstat = PR_Unlock(dbLock);
+
+ return(ret);
+}
+
+static void
+certdb_Close(DB *db)
+{
+ PRStatus prstat;
+
+ PORT_Assert(dbLock != NULL);
+ PR_Lock(dbLock);
+
+ (* db->close)(db);
+
+ prstat = PR_Unlock(dbLock);
+
+ return;
+}
+
+/* forward references */
+static void CERT_DestroyCertificateNoLocking(CERTCertificate *cert);
+static SECStatus AddCertToSPKDigestTable(CERTCertDBHandle *handle,
+ CERTCertificate *cert);
+static SECStatus RemoveCertFromSPKDigestTable(CERTCertDBHandle *handle,
+ CERTCertificate *cert);
+
+static SECStatus
+DeleteDBEntry(CERTCertDBHandle *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(CERTCertDBHandle *handle, certDBEntryCommon *entry,
+ SECItem *dbkey, SECItem *dbentry, PRArenaPool *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;
+ if ( buf[0] != (unsigned char)CERT_DB_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 ) {
+ 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 = NULL;
+ }
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/**
+ ** Implement low level database access
+ **/
+static SECStatus
+WriteDBEntry(CERTCertDBHandle *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, PRArenaPool *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] = ( entry->trust.sslFlags >> 8 ) & 0xff;
+ buf[1] = entry->trust.sslFlags & 0xff;
+ buf[2] = ( entry->trust.emailFlags >> 8 ) & 0xff;
+ buf[3] = entry->trust.emailFlags & 0xff;
+ buf[4] = ( entry->trust.objectSigningFlags >> 8 ) & 0xff;
+ buf[5] = entry->trust.objectSigningFlags & 0xff;
+ buf[6] = ( entry->derCert.len >> 8 ) & 0xff;
+ buf[7] = entry->derCert.len & 0xff;
+ buf[8] = ( nnlen >> 8 ) & 0xff;
+ buf[9] = nnlen & 0xff;
+
+ 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(SECItem *certKey, PRArenaPool *arena, SECItem *dbkey)
+{
+ dbkey->len = certKey->len + SEC_DB_KEY_HEADER_LEN;
+ 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] = certDBEntryTypeCert;
+
+ return(SECSuccess);
+loser:
+ return(SECFailure);
+}
+
+static SECStatus
+EncodeDBGenericKey(SECItem *certKey, PRArenaPool *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;
+ 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;
+ 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:
+ 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] );
+ if ( ( entry->derCert.len + nnlen + headerlen )
+ != dbentry->len) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* copy the dercert */
+ entry->derCert.data = (unsigned char *)PORT_ArenaAlloc(entry->common.arena,
+ entry->derCert.len);
+ if ( entry->derCert.data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->derCert.data, &dbentry->data[headerlen],
+ entry->derCert.len);
+
+ /* copy the nickname */
+ if ( nnlen > 1 ) {
+ entry->nickname = (char *)PORT_ArenaAlloc(entry->common.arena, nnlen);
+ if ( entry->nickname == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->nickname,
+ &dbentry->data[headerlen +
+ entry->derCert.len],
+ nnlen);
+ } 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,
+ CERTCertTrust *trust, int flags)
+{
+ certDBEntryCert *entry;
+ PRArenaPool *arena = NULL;
+ int nnlen;
+
+ arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
+
+ if ( !arena ) {
+ goto loser;
+ }
+
+ entry = (certDBEntryCert *)PORT_ArenaZAlloc(arena, sizeof(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;
+ PRArenaPool *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->derCert.data = (unsigned char *)PORT_ArenaAlloc(arena, certlen);
+ if ( !entry->derCert.data ) {
+ goto loser;
+ }
+ entry->derCert.len = certlen;
+
+ if ( nnlen ) {
+ entry->nickname = (char *) PORT_ArenaAlloc(arena, nnlen);
+ if ( !entry->nickname ) {
+ goto loser;
+ }
+ } else {
+ entry->nickname = 0;
+ }
+
+ 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];
+
+ PORT_Memcpy(entry->derCert.data, &buf[DBCERT_V4_HEADER_LEN], certlen);
+ PORT_Memcpy(entry->nickname, &buf[DBCERT_V4_HEADER_LEN + certlen], nnlen);
+
+ 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(CERTCertDBHandle *handle, certDBEntryCert *entry)
+{
+ SECItem dbitem, dbkey;
+ PRArenaPool *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 = CERT_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(CERTCertDBHandle *handle, SECItem *certKey)
+{
+ SECItem dbkey;
+ PRArenaPool *arena = NULL;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBCertKey(certKey, arena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = DeleteDBEntry(handle, certDBEntryTypeCert, &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 certDBEntryCert *
+ReadDBCertEntry(CERTCertDBHandle *handle, SECItem *certKey)
+{
+ PRArenaPool *arena = NULL;
+ PRArenaPool *tmparena = NULL;
+ certDBEntryCert *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 = (certDBEntryCert *)PORT_ArenaAlloc(arena, sizeof(certDBEntryCert));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeCert;
+
+ rv = EncodeDBCertKey(certKey, tmparena, &dbkey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ rv = DecodeDBCertEntry(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);
+}
+
+/*
+ * encode a database cert record
+ */
+static SECStatus
+EncodeDBCrlEntry(certDBEntryRevocation *entry, PRArenaPool *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] = ( entry->derCrl.len >> 8 ) & 0xff;
+ buf[1] = entry->derCrl.len & 0xff;
+ buf[2] = ( nnlen >> 8 ) & 0xff;
+ buf[3] = nnlen & 0xff;
+
+ 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 nnlen;
+
+ /* 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] );
+ nnlen = ( ( dbentry->data[2] << 8 ) | dbentry->data[3] );
+ if ( ( entry->derCrl.len + nnlen + DB_CRL_ENTRY_HEADER_LEN )
+ != dbentry->len) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* copy the dercert */
+ 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 (nnlen != 0) {
+ entry->url = (char *)PORT_ArenaAlloc(entry->common.arena, nnlen);
+ 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],
+ nnlen);
+ }
+
+ 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;
+ PRArenaPool *arena = NULL;
+ int nnlen;
+
+ arena = PORT_NewArena( DER_DEFAULT_CHUNKSIZE );
+
+ if ( !arena ) {
+ goto loser;
+ }
+
+ entry = (certDBEntryRevocation*)
+ PORT_ArenaZAlloc(arena, sizeof(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(CERTCertDBHandle *handle, certDBEntryRevocation *entry )
+{
+ SECItem dbkey;
+ PRArenaPool *tmparena = NULL;
+ SECItem tmpitem,encodedEntry;
+ SECStatus rv;
+
+ tmparena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( tmparena == NULL ) {
+ goto loser;
+ }
+
+ /* get the database key and format it */
+ rv = CERT_KeyFromDERCrl(tmparena, &entry->derCrl, &tmpitem);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ rv = EncodeDBCrlEntry(entry, tmparena, &encodedEntry);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ rv = EncodeDBGenericKey(&tmpitem, 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(CERTCertDBHandle *handle, SECItem *crlKey,
+ certDBEntryType crlType)
+{
+ SECItem dbkey;
+ PRArenaPool *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(CERTCertDBHandle *handle, SECItem *certKey,
+ certDBEntryType crlType)
+{
+ PRArenaPool *arena = NULL;
+ PRArenaPool *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, tmparena);
+ 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);
+}
+
+/*
+ * destroy a database entry
+ */
+static void
+DestroyDBEntry(certDBEntry *entry)
+{
+ PRArenaPool *arena = entry->common.arena;
+
+ /* 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;
+}
+
+/*
+ * Encode a database nickname record
+ */
+static SECStatus
+EncodeDBNicknameEntry(certDBEntryNickname *entry, PRArenaPool *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) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* fill in database record */
+ buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
+
+ buf[0] = ( entry->subjectName.len >> 8 ) & 0xff;
+ buf[1] = entry->subjectName.len & 0xff;
+
+ 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, PRArenaPool *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;
+ 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)
+{
+ /* 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] );
+ if (( entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN ) !=
+ dbentry->len ){
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* 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);
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * create a new nickname entry
+ */
+static certDBEntryNickname *
+NewDBNicknameEntry(char *nickname, SECItem *subjectName, unsigned int flags)
+{
+ PRArenaPool *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(CERTCertDBHandle *handle, char *nickname)
+{
+ PRArenaPool *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(CERTCertDBHandle *handle, char *nickname)
+{
+ PRArenaPool *arena = NULL;
+ PRArenaPool *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);
+ 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(CERTCertDBHandle *handle, certDBEntryNickname *entry)
+{
+ SECItem dbitem, dbkey;
+ PRArenaPool *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);
+
+}
+
+/*
+ * Encode a database smime record
+ */
+static SECStatus
+EncodeDBSMimeEntry(certDBEntrySMime *entry, PRArenaPool *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] = ( entry->subjectName.len >> 8 ) & 0xff;
+ buf[1] = entry->subjectName.len & 0xff;
+ buf[2] = ( entry->smimeOptions.len >> 8 ) & 0xff;
+ buf[3] = entry->smimeOptions.len & 0xff;
+ buf[4] = ( entry->optionsDate.len >> 8 ) & 0xff;
+ buf[5] = entry->optionsDate.len & 0xff;
+
+ /* 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, PRArenaPool *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;
+ 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)
+{
+ /* 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] );
+ if (( entry->subjectName.len + entry->smimeOptions.len +
+ entry->optionsDate.len + DB_SMIME_ENTRY_HEADER_LEN ) != dbentry->len){
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* 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_Alloc(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)
+{
+ PRArenaPool *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(CERTCertDBHandle *handle, char *emailAddr)
+{
+ PRArenaPool *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
+ */
+static certDBEntrySMime *
+ReadDBSMimeEntry(CERTCertDBHandle *handle, char *emailAddr)
+{
+ PRArenaPool *arena = NULL;
+ PRArenaPool *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(CERTCertDBHandle *handle, certDBEntrySMime *entry)
+{
+ SECItem dbitem, dbkey;
+ PRArenaPool *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, PRArenaPool *arena,
+ SECItem *dbitem)
+{
+ unsigned char *buf;
+ int len;
+ unsigned int ncerts;
+ unsigned int i;
+ unsigned char *tmpbuf;
+ unsigned int nnlen = 0;
+ unsigned int eaddrlen = 0;
+ int keyidoff;
+ SECItem *certKeys;
+ SECItem *keyIDs;
+
+ if ( entry->nickname ) {
+ nnlen = PORT_Strlen(entry->nickname) + 1;
+ }
+ if ( entry->emailAddr ) {
+ eaddrlen = PORT_Strlen(entry->emailAddr) + 1;
+ }
+
+ ncerts = entry->ncerts;
+
+ /* compute the length of the entry */
+ keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen + eaddrlen;
+ len = keyidoff + 4 * ncerts;
+ for ( i = 0; i < ncerts; i++ ) {
+ len += entry->certKeys[i].len;
+ len += entry->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] = ( ncerts >> 8 ) & 0xff;
+ buf[1] = ncerts & 0xff;
+ buf[2] = ( nnlen >> 8 ) & 0xff;
+ buf[3] = nnlen & 0xff;
+ buf[4] = ( eaddrlen >> 8 ) & 0xff;
+ buf[5] = eaddrlen & 0xff;
+
+ PORT_Memcpy(&buf[DB_SUBJECT_ENTRY_HEADER_LEN], entry->nickname, nnlen);
+ PORT_Memcpy(&buf[DB_SUBJECT_ENTRY_HEADER_LEN+nnlen], entry->emailAddr,
+ eaddrlen);
+
+ for ( i = 0; i < ncerts; i++ ) {
+
+ certKeys = entry->certKeys;
+ keyIDs = entry->keyIDs;
+
+ buf[keyidoff+i*2] = ( certKeys[i].len >> 8 ) & 0xff;
+ buf[keyidoff+1+i*2] = certKeys[i].len & 0xff;
+ buf[keyidoff+ncerts*2+i*2] = ( keyIDs[i].len >> 8 ) & 0xff;
+ buf[keyidoff+1+ncerts*2+i*2] = keyIDs[i].len & 0xff;
+ }
+
+ /* temp pointer used to stuff certkeys and keyids into the buffer */
+ tmpbuf = &buf[keyidoff+ncerts*4];
+
+ for ( i = 0; i < ncerts; i++ ) {
+ certKeys = entry->certKeys;
+ PORT_Memcpy(tmpbuf, certKeys[i].data, certKeys[i].len);
+ tmpbuf = tmpbuf + certKeys[i].len;
+ }
+
+ for ( i = 0; i < ncerts; i++ ) {
+ keyIDs = entry->keyIDs;
+ PORT_Memcpy(tmpbuf, keyIDs[i].data, keyIDs[i].len);
+ tmpbuf = tmpbuf + keyIDs[i].len;
+ }
+
+ PORT_Assert(tmpbuf == &buf[len]);
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * Encode a database key for a subject record
+ */
+static SECStatus
+EncodeDBSubjectKey(SECItem *derSubject, PRArenaPool *arena,
+ SECItem *dbkey)
+{
+ dbkey->len = derSubject->len + SEC_DB_KEY_HEADER_LEN;
+ 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,
+ SECItem *derSubject)
+{
+ unsigned int ncerts;
+ PRArenaPool *arena;
+ unsigned int len, itemlen;
+ unsigned char *tmpbuf;
+ unsigned int i;
+ SECStatus rv;
+ unsigned int keyidoff;
+ unsigned int nnlen, eaddrlen;
+
+ arena = entry->common.arena;
+
+ 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] );
+ if ( dbentry->len < ( ncerts * 4 + DB_SUBJECT_ENTRY_HEADER_LEN +
+ nnlen + eaddrlen) ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ entry->certKeys = (SECItem *)PORT_ArenaAlloc(arena,
+ sizeof(SECItem) * ncerts);
+ entry->keyIDs = (SECItem *)PORT_ArenaAlloc(arena,
+ sizeof(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 ( eaddrlen > 1 ) { /* null terminator is stored */
+ entry->emailAddr = (char *)PORT_ArenaAlloc(arena, eaddrlen);
+ if ( entry->emailAddr == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->emailAddr,
+ &dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN+nnlen],
+ eaddrlen);
+ } else {
+ entry->emailAddr = NULL;
+ }
+
+ /* collect the lengths of the certKeys and keyIDs, and total the
+ * overall length.
+ */
+ keyidoff = DB_SUBJECT_ENTRY_HEADER_LEN + nnlen + eaddrlen;
+ len = keyidoff + 4 * ncerts;
+
+ tmpbuf = &dbentry->data[0];
+
+ for ( i = 0; i < ncerts; i++ ) {
+
+ itemlen = ( tmpbuf[keyidoff + 2*i] << 8 ) | tmpbuf[keyidoff + 1 + 2*i] ;
+ len += itemlen;
+ entry->certKeys[i].len = itemlen;
+
+ itemlen = ( tmpbuf[keyidoff + 2*ncerts + 2*i] << 8 ) |
+ tmpbuf[keyidoff + 1 + 2*ncerts + 2*i] ;
+ len += itemlen;
+ entry->keyIDs[i].len = itemlen;
+ }
+
+ /* is database entry correct length? */
+ if ( len != dbentry->len ){
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ tmpbuf = &tmpbuf[keyidoff + 4*ncerts];
+ for ( i = 0; i < ncerts; i++ ) {
+ entry->certKeys[i].data =
+ (unsigned char *)PORT_ArenaAlloc(arena, entry->certKeys[i].len);
+ if ( entry->certKeys[i].data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->certKeys[i].data, tmpbuf, entry->certKeys[i].len);
+ tmpbuf = &tmpbuf[entry->certKeys[i].len];
+ }
+
+ for ( i = 0; i < ncerts; i++ ) {
+ entry->keyIDs[i].data =
+ (unsigned char *)PORT_ArenaAlloc(arena, entry->keyIDs[i].len);
+ if ( entry->keyIDs[i].data == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ PORT_Memcpy(entry->keyIDs[i].data, tmpbuf, entry->keyIDs[i].len);
+ tmpbuf = &tmpbuf[entry->keyIDs[i].len];
+ }
+
+ PORT_Assert(tmpbuf == &dbentry->data[dbentry->len]);
+
+ return(SECSuccess);
+
+loser:
+ 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)
+{
+ PRArenaPool *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;
+ /* 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 = CERT_FixupEmailAddr(emailAddr);
+ if ( emailAddr == NULL ) {
+ entry->emailAddr = NULL;
+ goto loser;
+ }
+
+ eaddrlen = PORT_Strlen(emailAddr) + 1;
+ entry->emailAddr = (char *)PORT_ArenaAlloc(arena, eaddrlen);
+ if ( entry->emailAddr == NULL ) {
+ PORT_Free(emailAddr);
+ goto loser;
+ }
+
+ PORT_Memcpy(entry->emailAddr, emailAddr, eaddrlen);
+ PORT_Free(emailAddr);
+ } else {
+ entry->emailAddr = 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(CERTCertDBHandle *handle, SECItem *derSubject)
+{
+ SECItem dbkey;
+ PRArenaPool *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(CERTCertDBHandle *handle, SECItem *derSubject)
+{
+ PRArenaPool *arena = NULL;
+ PRArenaPool *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(CERTCertDBHandle *handle, certDBEntrySubject *entry)
+{
+ SECItem dbitem, dbkey;
+ PRArenaPool *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);
+
+}
+
+static SECStatus
+UpdateSubjectWithEmailAddr(CERTCertificate *cert, char *emailAddr)
+{
+ CERTSubjectList *subjectList;
+ PRBool save = PR_FALSE, delold = PR_FALSE;
+ certDBEntrySubject *entry;
+ SECStatus rv;
+
+ emailAddr = CERT_FixupEmailAddr(emailAddr);
+ if ( emailAddr == NULL ) {
+ return(SECFailure);
+ }
+
+ subjectList = cert->subjectList;
+ PORT_Assert(subjectList != NULL);
+
+ if ( subjectList->emailAddr ) {
+ if ( PORT_Strcmp(subjectList->emailAddr, emailAddr) != 0 ) {
+ save = PR_TRUE;
+ delold = PR_TRUE;
+ }
+ } else {
+ save = PR_TRUE;
+ }
+
+ if ( delold ) {
+ /* delete the old smime entry, because this cert now has a new
+ * smime entry pointing to it
+ */
+ PORT_Assert(save);
+ PORT_Assert(subjectList->emailAddr != NULL);
+ DeleteDBSMimeEntry(cert->dbhandle, subjectList->emailAddr);
+ }
+
+ if ( save ) {
+ unsigned int len;
+
+ entry = subjectList->entry;
+
+ PORT_Assert(entry != NULL);
+ len = PORT_Strlen(emailAddr) + 1;
+ entry->emailAddr = (char *)PORT_ArenaAlloc(entry->common.arena, len);
+ if ( entry->emailAddr == NULL ) {
+ goto loser;
+ }
+ PORT_Memcpy(entry->emailAddr, emailAddr, len);
+
+ /* delete the subject entry */
+ DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
+
+ /* write the new one */
+ rv = WriteDBSubjectEntry(cert->dbhandle, entry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ }
+
+ PORT_Free(emailAddr);
+ return(SECSuccess);
+
+loser:
+ PORT_Free(emailAddr);
+ return(SECFailure);
+}
+
+/*
+ * writes a nickname to an existing subject entry that does not currently
+ * have one
+ */
+static SECStatus
+AddNicknameToSubject(CERTCertificate *cert, char *nickname)
+{
+ CERTSubjectList *subjectList;
+ certDBEntrySubject *entry;
+ SECStatus rv;
+
+ if ( nickname == NULL ) {
+ return(SECFailure);
+ }
+
+ subjectList = cert->subjectList;
+ PORT_Assert(subjectList != NULL);
+ if ( subjectList == NULL ) {
+ goto loser;
+ }
+
+ entry = subjectList->entry;
+ PORT_Assert(entry != NULL);
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+ PORT_Assert(entry->nickname == NULL);
+ if ( entry->nickname != NULL ) {
+ goto loser;
+ }
+
+ entry->nickname = (nickname) ? PORT_ArenaStrdup(entry->common.arena, nickname) : NULL;
+
+ if ( entry->nickname == NULL ) {
+ goto loser;
+ }
+
+ /* delete the subject entry */
+ DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
+
+ /* write the new one */
+ rv = WriteDBSubjectEntry(cert->dbhandle, entry);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * create a new version entry
+ */
+static certDBEntryVersion *
+NewDBVersionEntry(unsigned int flags)
+{
+ PRArenaPool *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(CERTCertDBHandle *handle)
+{
+ PRArenaPool *arena = NULL;
+ PRArenaPool *tmparena = NULL;
+ certDBEntryVersion *entry;
+ SECItem dbkey;
+ SECItem dbentry;
+
+ 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 = (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;
+
+ /* 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);
+
+ ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
+
+ 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(CERTCertDBHandle *handle, certDBEntryVersion *entry)
+{
+ SECItem dbitem, dbkey;
+ PRArenaPool *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);
+}
+
+/*
+ * create a new version entry
+ */
+static certDBEntryContentVersion *
+NewDBContentVersionEntry(unsigned int flags)
+{
+ PRArenaPool *arena = NULL;
+ certDBEntryContentVersion *entry;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ entry = (certDBEntryContentVersion *)
+ PORT_ArenaAlloc(arena, sizeof(certDBEntryContentVersion));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeContentVersion;
+ entry->common.version = CERT_DB_FILE_VERSION;
+ entry->common.flags = flags;
+
+ entry->contentVersion = CERT_DB_CONTENT_VERSION;
+
+ return(entry);
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(NULL);
+}
+
+/*
+ * Read the version entry
+ */
+static certDBEntryContentVersion *
+ReadDBContentVersionEntry(CERTCertDBHandle *handle)
+{
+ PRArenaPool *arena = NULL;
+ PRArenaPool *tmparena = NULL;
+ certDBEntryContentVersion *entry;
+ SECItem dbkey;
+ SECItem dbentry;
+
+ 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 = (certDBEntryContentVersion *)
+ PORT_ArenaAlloc(arena, sizeof(certDBEntryContentVersion));
+ if ( entry == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ entry->common.arena = arena;
+ entry->common.type = certDBEntryTypeContentVersion;
+
+ /* now get the database key and format it */
+ dbkey.len = SEC_DB_CONTENT_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_CONTENT_VERSION_KEY,
+ SEC_DB_CONTENT_VERSION_KEY_LEN);
+
+ dbentry.len = 0;
+ dbentry.data = NULL;
+
+ ReadDBEntry(handle, &entry->common, &dbkey, &dbentry, tmparena);
+
+ if ( dbentry.len != 1 ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ entry->contentVersion = dbentry.data[0];
+
+ 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
+WriteDBContentVersionEntry(CERTCertDBHandle *handle,
+ certDBEntryContentVersion *entry)
+{
+ SECItem dbitem, dbkey;
+ PRArenaPool *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 + 1;
+
+ dbitem.data = (unsigned char *)PORT_ArenaAlloc(tmparena, dbitem.len);
+ if ( dbitem.data == NULL) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ dbitem.data[SEC_DB_ENTRY_HEADER_LEN] = entry->contentVersion;
+
+ /* now get the database key and format it */
+ dbkey.len = SEC_DB_CONTENT_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_CONTENT_VERSION_KEY,
+ SEC_DB_CONTENT_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);
+}
+
+/*
+ * delete a content version entry
+ */
+static SECStatus
+DeleteDBContentVersionEntry(CERTCertDBHandle *handle)
+{
+ SECItem dbkey;
+ PRArenaPool *arena = NULL;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ /* now get the database key and format it */
+ dbkey.len = SEC_DB_CONTENT_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
+ dbkey.data = (unsigned char *)PORT_ArenaAlloc(arena, dbkey.len);
+ if ( dbkey.data == NULL ) {
+ goto loser;
+ }
+ PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_CONTENT_VERSION_KEY,
+ SEC_DB_CONTENT_VERSION_KEY_LEN);
+
+ rv = DeleteDBEntry(handle, certDBEntryTypeContentVersion, &dbkey);
+ if ( rv == SECFailure ) {
+ goto loser;
+ }
+
+ PORT_FreeArena(arena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(SECFailure);
+}
+
+/*
+ * Routines and datastructures to manage the list of certificates for a
+ * particular subject name.
+ */
+
+/*
+ * Create a new certificate subject list. If entry exists, then populate
+ * the list with the entries from the permanent database.
+ */
+static CERTSubjectList *
+NewSubjectList(certDBEntrySubject *entry)
+{
+ PRArenaPool *permarena;
+ unsigned int i;
+ CERTSubjectList *subjectList;
+ CERTSubjectNode *node;
+ SECStatus rv;
+
+ permarena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( permarena == NULL ) {
+ goto loser;
+ }
+ subjectList = (CERTSubjectList *)PORT_ArenaAlloc(permarena,
+ sizeof(CERTSubjectList));
+ if ( subjectList == NULL ) {
+ goto loser;
+ }
+
+ subjectList->arena = permarena;
+ subjectList->ncerts = 0;
+ subjectList->head = NULL;
+ subjectList->tail = NULL;
+ subjectList->entry = entry;
+ subjectList->emailAddr = NULL;
+ if ( entry ) {
+
+ /* initialize the list with certs from database entry */
+ for ( i = 0; i < entry->ncerts; i++ ) {
+ /* Init the node */
+ node = (CERTSubjectNode *)PORT_ArenaAlloc(permarena,
+ sizeof(CERTSubjectNode));
+ if ( node == NULL ) {
+ goto loser;
+ }
+
+ /* copy certKey and keyID to node */
+ rv = SECITEM_CopyItem(permarena, &node->certKey,
+ &entry->certKeys[i]);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ rv = SECITEM_CopyItem(permarena, &node->keyID,
+ &entry->keyIDs[i]);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* the certs are already in order, so just add them
+ * to the tail.
+ */
+ node->next = NULL;
+ if ( subjectList->tail == NULL ) {
+ /* first in list */
+ subjectList->head = node;
+ subjectList->tail = node;
+ node->prev = NULL;
+ } else {
+ /* add to end of list */
+ node->prev = subjectList->tail;
+ subjectList->tail = node;
+ node->prev->next = node;
+ }
+ subjectList->ncerts++;
+ }
+ }
+
+ return(subjectList);
+
+loser:
+ PORT_FreeArena(permarena, PR_FALSE);
+ return(NULL);
+}
+
+/*
+ * Find the Subject entry in the temp database. It it is not in the
+ * temp database, then get it from the perm DB. It its not there either,
+ * then create a new one.
+ */
+static CERTSubjectList *
+FindSubjectList(CERTCertDBHandle *handle, SECItem *subject, PRBool create)
+{
+ PRArenaPool *arena = NULL;
+ SECItem keyitem;
+ SECStatus rv;
+ DBT namekey;
+ DBT tmpdata;
+ int ret;
+ CERTSubjectList *subjectList = NULL;
+ certDBEntrySubject *entry;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBSubjectKey(subject, arena, &keyitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ namekey.data = keyitem.data;
+ namekey.size = keyitem.len;
+
+ /* lookup in the temporary database */
+ ret = certdb_Get(handle->tempCertDB, &namekey, &tmpdata, 0);
+
+ /* error accessing the database */
+ if ( ret < 0 ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ if ( ret == 0 ) { /* found in temp database */
+ if ( tmpdata.size != sizeof(CERTCertificate *) ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ /* copy pointer out of database */
+ PORT_Memcpy(&subjectList, tmpdata.data, tmpdata.size);
+ } else { /* not found in temporary database */
+ entry = ReadDBSubjectEntry(handle, subject);
+ if ( entry || create ) {
+ /* decode or create new subject list */
+ subjectList = NewSubjectList(entry);
+
+ /* put it in the temp database */
+ if ( subjectList ) {
+ tmpdata.data = (unsigned char *)(&subjectList);
+ tmpdata.size = sizeof(subjectList);
+ ret = certdb_Put(handle->tempCertDB, &namekey,
+ &tmpdata, R_NOOVERWRITE);
+ if ( ret ) {
+ goto loser;
+ }
+ }
+ } else {
+ PORT_SetError(SEC_ERROR_UNKNOWN_CERT);
+ goto loser;
+ }
+ }
+
+ goto done;
+
+loser:
+ subjectList = NULL;
+
+done:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(subjectList);
+}
+
+/*
+ * Add a temp cert to the temp subject list
+ */
+static SECStatus
+AddTempCertToSubjectList(CERTCertificate *cert)
+{
+ CERTSubjectList *subjectList;
+ CERTSubjectNode *node, *newnode;
+ CERTCertificate *cmpcert;
+ PRBool newer;
+ SECStatus rv;
+
+ PORT_Assert(cert->isperm == PR_FALSE);
+ PORT_Assert(cert->subjectList == NULL);
+
+ subjectList = FindSubjectList(cert->dbhandle, &cert->derSubject, PR_TRUE);
+
+ if ( subjectList == NULL ) {
+ goto loser;
+ }
+
+ newnode = (CERTSubjectNode*)PORT_ArenaAlloc(subjectList->arena,
+ sizeof(CERTSubjectNode));
+ /* copy certKey and keyID to node */
+ rv = SECITEM_CopyItem(subjectList->arena, &newnode->certKey,
+ &cert->certKey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ rv = SECITEM_CopyItem(subjectList->arena, &newnode->keyID,
+ &cert->subjectKeyID);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ node = subjectList->head;
+
+ if ( node ) {
+ /* list is not empty */
+ while ( node ) {
+ cmpcert = CERT_FindCertByKeyNoLocking(cert->dbhandle,
+ &node->certKey);
+ if ( cmpcert ) {
+
+ newer = CERT_IsNewer(cert, cmpcert);
+ CERT_DestroyCertificateNoLocking(cmpcert);
+ if ( newer ) {
+ /* insert before this cert */
+ newnode->next = node;
+ newnode->prev = node->prev;
+ if ( newnode->prev ) {
+ newnode->prev->next = newnode;
+ } else {
+ /* at the head of the list */
+ subjectList->head = newnode;
+ }
+ node->prev = newnode;
+ goto done;
+ }
+ }
+ node = node->next;
+ }
+ /* if we get here, we add the node to the end of the list */
+ newnode->prev = subjectList->tail;
+ newnode->next = NULL;
+ subjectList->tail->next = newnode;
+ subjectList->tail = newnode;
+ } else {
+ /* this is a new/empty list */
+ newnode->next = NULL;
+ newnode->prev = NULL;
+ subjectList->head = newnode;
+ subjectList->tail = newnode;
+ }
+
+done:
+ subjectList->ncerts++;
+ cert->subjectList = subjectList;
+ return(SECSuccess);
+
+loser:
+ return(SECFailure);
+}
+
+/*
+ * Find the node in a subjectList that belongs a cert
+ */
+static CERTSubjectNode *
+FindCertSubjectNode(CERTCertificate *cert)
+{
+ CERTSubjectList *subjectList;
+ CERTSubjectNode *node = NULL;
+
+ PORT_Assert(cert->subjectList);
+
+ subjectList = cert->subjectList;
+
+ if ( subjectList ) {
+ node = subjectList->head;
+ }
+
+ while ( node ) {
+ if ( SECITEM_CompareItem(&node->certKey, &cert->certKey) == SECEqual ){
+ return(node);
+ break;
+ }
+
+ node = node->next;
+ }
+
+ return(NULL);
+}
+
+/*
+ * Remove a temp cert from the temp subject list
+ */
+static SECStatus
+RemoveTempCertFromSubjectList(CERTCertificate *cert)
+{
+ CERTSubjectList *subjectList;
+ CERTSubjectNode *node;
+ SECItem keyitem;
+ DBT namekey;
+ SECStatus rv;
+ int ret;
+ CERTCertDBHandle *handle;
+
+ PORT_Assert(cert->subjectList);
+
+ /* don't remove perm certs */
+ if ( cert->isperm ) {
+ return(SECSuccess);
+ }
+
+ subjectList = cert->subjectList;
+
+ node = FindCertSubjectNode(cert);
+
+ if ( node ) {
+ /* found it, unlink it */
+ if ( node->next ) {
+ node->next->prev = node->prev;
+ } else {
+ /* removing from tail of list */
+ subjectList->tail = node->prev;
+ }
+ if ( node->prev ) {
+ node->prev->next = node->next;
+ } else {
+ /* removing from head of list */
+ subjectList->head = node->next;
+ }
+
+ subjectList->ncerts--;
+
+ /* dont need to free the node, because it is from subjectList
+ * arena.
+ */
+
+ /* remove reference from cert */
+ cert->subjectList = NULL;
+
+ /* if the list is now empty, remove the list from the db and free it */
+ if ( subjectList->head == NULL ) {
+ PORT_Assert(subjectList->ncerts == 0);
+ rv = EncodeDBSubjectKey(&cert->derSubject, subjectList->arena,
+ &keyitem);
+ if ( rv == SECSuccess ) {
+ namekey.data = keyitem.data;
+ namekey.size = keyitem.len;
+
+ handle = cert->dbhandle;
+
+ ret = certdb_Del(handle->tempCertDB, &namekey, 0);
+ /* keep going if it fails */
+
+ if ( cert->dbnickname ) {
+ rv = SEC_DeleteTempNickname(handle, cert->dbnickname);
+ } else if ( cert->nickname ) {
+ rv = SEC_DeleteTempNickname(handle, cert->nickname);
+ }
+
+ /* keep going if it fails */
+ }
+
+ PORT_FreeArena(subjectList->arena, PR_FALSE);
+ }
+ }
+
+ PORT_Assert(cert->subjectList == NULL);
+
+ if ( cert->subjectList != NULL ) {
+ return(SECFailure);
+ }
+
+ return(SECSuccess);
+}
+
+/*
+ * cert is no longer a perm cert, but will remain a temp cert
+ */
+static SECStatus
+RemovePermSubjectNode(CERTCertificate *cert)
+{
+ CERTSubjectList *subjectList;
+ certDBEntrySubject *entry;
+ unsigned int i;
+ SECStatus rv;
+
+ PORT_Assert(cert->isperm);
+ if ( !cert->isperm ) {
+ return(SECFailure);
+ }
+
+ subjectList = cert->subjectList;
+ PORT_Assert(subjectList);
+ if ( subjectList == NULL ) {
+ return(SECFailure);
+ }
+ entry = subjectList->entry;
+ PORT_Assert(entry);
+ 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 ( subjectList->entry->emailAddr ) {
+ /* if the subject had an email record, then delete it too */
+ DeleteDBSMimeEntry(cert->dbhandle, subjectList->entry->emailAddr);
+ }
+
+ DestroyDBEntry((certDBEntry *)subjectList->entry);
+ subjectList->entry = NULL;
+ DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
+ }
+
+ return(rv);
+}
+
+/*
+ * add a cert to the perm subject list
+ */
+static SECStatus
+AddPermSubjectNode(CERTCertificate *cert, char *nickname)
+{
+ CERTSubjectList *subjectList;
+ certDBEntrySubject *entry;
+ SECItem *newCertKeys, *newKeyIDs;
+ int i;
+ SECStatus rv;
+ CERTCertificate *cmpcert;
+ unsigned int nnlen;
+ int ncerts;
+
+ subjectList = cert->subjectList;
+
+ PORT_Assert(subjectList);
+ if ( subjectList == NULL ) {
+ return(SECFailure);
+ }
+
+ entry = subjectList->entry;
+
+ if ( 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 */
+ nnlen = PORT_Strlen(nickname) + 1;
+ entry->nickname = (char *)PORT_ArenaAlloc(entry->common.arena,
+ nnlen);
+ if ( entry->nickname == NULL ) {
+ return(SECFailure);
+ }
+ PORT_Memcpy(entry->nickname, nickname, nnlen);
+ }
+
+ /* a DB entry already exists, so add this cert */
+ newCertKeys = (SECItem *)PORT_ArenaAlloc(entry->common.arena,
+ sizeof(SECItem) *
+ ( ncerts + 1 ) );
+ newKeyIDs = (SECItem *)PORT_ArenaAlloc(entry->common.arena,
+ sizeof(SECItem) *
+ ( ncerts + 1 ) );
+
+ if ( ( newCertKeys == NULL ) || ( newKeyIDs == NULL ) ) {
+ return(SECFailure);
+ }
+
+ for ( i = 0; i < ncerts; i++ ) {
+ cmpcert = CERT_FindCertByKeyNoLocking(cert->dbhandle,
+ &entry->certKeys[i]);
+ PORT_Assert(cmpcert);
+
+ if ( CERT_IsNewer(cert, cmpcert) ) {
+ /* insert before cmpcert */
+ rv = SECITEM_CopyItem(entry->common.arena, &newCertKeys[i],
+ &cert->certKey);
+ if ( rv != SECSuccess ) {
+ return(SECFailure);
+ }
+ rv = SECITEM_CopyItem(entry->common.arena, &newKeyIDs[i],
+ &cert->subjectKeyID);
+ if ( rv != SECSuccess ) {
+ return(SECFailure);
+ }
+ /* copy the rest of the entry */
+ for ( ; i < ncerts; i++ ) {
+ newCertKeys[i+1] = entry->certKeys[i];
+ newKeyIDs[i+1] = entry->keyIDs[i];
+ }
+
+ /* update certKeys and keyIDs */
+ entry->certKeys = newCertKeys;
+ entry->keyIDs = newKeyIDs;
+
+ /* increment count */
+ entry->ncerts++;
+ break;
+ }
+ /* copy this cert entry */
+ newCertKeys[i] = entry->certKeys[i];
+ newKeyIDs[i] = entry->keyIDs[i];
+ }
+
+ if ( entry->ncerts == ncerts ) {
+ /* insert new one at end */
+ rv = SECITEM_CopyItem(entry->common.arena, &newCertKeys[ncerts],
+ &cert->certKey);
+ if ( rv != SECSuccess ) {
+ return(SECFailure);
+ }
+ rv = SECITEM_CopyItem(entry->common.arena, &newKeyIDs[ncerts],
+ &cert->subjectKeyID);
+ if ( rv != SECSuccess ) {
+ return(SECFailure);
+ }
+
+ /* update certKeys and keyIDs */
+ entry->certKeys = newCertKeys;
+ entry->keyIDs = newKeyIDs;
+
+ /* increment count */
+ entry->ncerts++;
+ }
+ } else {
+ /* need to make a new DB entry */
+ entry = NewDBSubjectEntry(&cert->derSubject, &cert->certKey,
+ &cert->subjectKeyID, nickname,
+ NULL, 0);
+ cert->subjectList->entry = entry;
+ }
+ if ( entry ) {
+ DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
+ rv = WriteDBSubjectEntry(cert->dbhandle, entry);
+ } else {
+ rv = SECFailure;
+ }
+
+ return(rv);
+}
+
+
+
+SECStatus
+CERT_TraversePermCertsForSubject(CERTCertDBHandle *handle, SECItem *derSubject,
+ CERTCertCallback cb, void *cbarg)
+{
+ certDBEntrySubject *entry;
+ int i;
+ CERTCertificate *cert;
+ SECStatus rv = SECSuccess;
+
+ entry = ReadDBSubjectEntry(handle, derSubject);
+
+ if ( entry == NULL ) {
+ return(SECFailure);
+ }
+
+ for( i = 0; i < entry->ncerts; i++ ) {
+ cert = CERT_FindCertByKey(handle, &entry->certKeys[i]);
+ rv = (* cb)(cert, cbarg);
+ CERT_DestroyCertificate(cert);
+ if ( rv == SECFailure ) {
+ break;
+ }
+ }
+
+ DestroyDBEntry((certDBEntry *)entry);
+
+ return(rv);
+}
+
+int
+CERT_NumPermCertsForSubject(CERTCertDBHandle *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
+CERT_TraversePermCertsForNickname(CERTCertDBHandle *handle, char *nickname,
+ CERTCertCallback 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 = ReadDBSMimeEntry(handle, nickname);
+ if ( smentry ) {
+ derSubject = &smentry->subjectName;
+ }
+ }
+
+ if ( derSubject ) {
+ rv = CERT_TraversePermCertsForSubject(handle, derSubject,
+ cb, cbarg);
+ } else {
+ rv = SECFailure;
+ }
+
+ if ( nnentry ) {
+ DestroyDBEntry((certDBEntry *)nnentry);
+ }
+ if ( smentry ) {
+ DestroyDBEntry((certDBEntry *)smentry);
+ }
+
+ return(rv);
+}
+
+int
+CERT_NumPermCertsForNickname(CERTCertDBHandle *handle, char *nickname)
+{
+ certDBEntryNickname *entry;
+ int ret;
+
+ entry = ReadDBNicknameEntry(handle, nickname);
+
+ if ( entry ) {
+ ret = CERT_NumPermCertsForSubject(handle, &entry->subjectName);
+ DestroyDBEntry((certDBEntry *)entry);
+ } else {
+ ret = 0;
+ }
+ return(ret);
+}
+
+int
+CERT_NumCertsForCertSubject(CERTCertificate *cert)
+{
+ int ret = 0;
+
+ if ( cert->subjectList ) {
+ ret = cert->subjectList->ncerts;
+ }
+ return(ret);
+}
+
+int
+CERT_NumPermCertsForCertSubject(CERTCertificate *cert)
+{
+ int ret = 0;
+
+ if ( cert->subjectList ) {
+ if ( cert->subjectList->entry ) {
+ ret = cert->subjectList->entry->ncerts;
+ }
+ }
+ return(ret);
+}
+
+SECStatus
+CERT_TraverseCertsForSubject(CERTCertDBHandle *handle,
+ CERTSubjectList *subjectList,
+ CERTCertCallback cb, void *cbarg)
+{
+ CERTSubjectNode *node;
+ CERTCertificate *cert;
+ SECStatus rv = SECSuccess;
+
+ CERT_LockDB(handle);
+
+ node = subjectList->head;
+ while ( node ) {
+
+ cert = CERT_FindCertByKeyNoLocking(handle, &node->certKey);
+
+ PORT_Assert(cert != NULL);
+
+ if ( cert != NULL ) {
+ rv = (* cb)(cert, cbarg);
+ CERT_DestroyCertificateNoLocking(cert);
+ if ( rv == SECFailure ) {
+ break;
+ }
+ }
+
+ node = node->next;
+ }
+
+ CERT_UnlockDB(handle);
+
+ return(rv);
+}
+
+
+/*
+ * Given a cert, find the cert with the same subject name that
+ * has the given key usage. If the given cert has the correct keyUsage, then
+ * return it, otherwise search the list in order.
+ */
+CERTCertificate *
+CERT_FindCertByUsage(CERTCertificate *basecert, unsigned int requiredKeyUsage)
+{
+ CERTSubjectNode *node;
+ CERTCertificate *cert;
+ CERTSubjectList *subjectList;
+
+ if ( ( basecert->keyUsage & requiredKeyUsage ) == requiredKeyUsage ) {
+ return(CERT_DupCertificate(basecert));
+ }
+
+ CERT_LockDB(basecert->dbhandle);
+
+ subjectList = basecert->subjectList;
+
+ node = subjectList->head;
+ while ( node ) {
+
+ cert = CERT_FindCertByKeyNoLocking(basecert->dbhandle, &node->certKey);
+
+ PORT_Assert(cert != NULL);
+
+ if ( cert != NULL ) {
+ if ( ( cert->keyUsage & requiredKeyUsage ) ==
+ requiredKeyUsage ) {
+ CERT_UnlockDB(basecert->dbhandle);
+ return(cert);
+ }
+
+ CERT_DestroyCertificateNoLocking(cert);
+ }
+
+ node = node->next;
+ }
+
+ CERT_UnlockDB(basecert->dbhandle);
+
+ return(NULL);
+}
+
+
+/*
+ * add a nickname to a cert that doesn't have one
+ */
+static SECStatus
+AddNicknameToPermCert(CERTCertificate *cert, char *nickname)
+{
+ certDBEntryCert *entry;
+ int rv;
+
+ PORT_Assert(cert->isperm);
+ if ( !cert->isperm ) {
+ goto loser;
+ }
+
+ entry = cert->dbEntry;
+ PORT_Assert(entry != NULL);
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+ entry->nickname = PORT_ArenaStrdup(entry->common.arena, nickname);
+
+ rv = WriteDBCertEntry(cert->dbhandle, entry);
+ if ( rv ) {
+ goto loser;
+ }
+
+ cert->nickname = PORT_ArenaStrdup(cert->arena, nickname);
+ 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
+CERT_AddPermNickname(CERTCertificate *cert, char *nickname)
+{
+ SECStatus rv;
+
+ CERT_LockDB(cert->dbhandle);
+
+ PORT_Assert(cert->nickname == NULL);
+ PORT_Assert(cert->isperm);
+ PORT_Assert(cert->subjectList != NULL);
+ PORT_Assert(cert->subjectList->entry != NULL);
+
+ if ( cert->nickname != NULL ) {
+ goto done;
+ }
+
+ if ( cert->subjectList == NULL ) {
+ goto loser;
+ }
+
+ if ( cert->subjectList->entry == NULL ) {
+ goto loser;
+ }
+
+ if ( cert->subjectList->entry->nickname == NULL ) {
+ /* no nickname for subject */
+ rv = AddNicknameToSubject(cert, nickname);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ rv = AddNicknameToPermCert(cert, nickname);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ rv = SEC_AddTempNickname(cert->dbhandle, nickname,
+ &cert->derSubject);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ } else {
+ /* subject already has a nickname */
+ rv = AddNicknameToPermCert(cert, cert->subjectList->entry->nickname);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ }
+
+done:
+ CERT_UnlockDB(cert->dbhandle);
+ return(SECSuccess);
+loser:
+ CERT_UnlockDB(cert->dbhandle);
+ return(SECFailure);
+}
+
+static certDBEntryCert *
+AddCertToPermDB(CERTCertDBHandle *handle, CERTCertificate *cert,
+ char *nickname, CERTCertTrust *trust)
+{
+ certDBEntryCert *certEntry = NULL;
+ certDBEntryNickname *nicknameEntry = NULL;
+ certDBEntrySubject *subjectEntry = NULL;
+ int state = 0;
+ SECStatus rv;
+ PRBool donnentry = PR_FALSE;
+
+ if ( nickname ) {
+ donnentry = PR_TRUE;
+ }
+
+ if ( cert->subjectList != NULL ) {
+ if ( cert->subjectList->entry != NULL ) {
+ if ( cert->subjectList->entry->ncerts > 0 ) {
+ /* of other certs with same subject exist, then they already
+ * have a nickname, so don't add a new one.
+ */
+ donnentry = PR_FALSE;
+ nickname = cert->subjectList->entry->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;
+
+ /* add to or create new subject entry */
+ if ( cert->subjectList ) {
+ rv = AddPermSubjectNode(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.
+ */
+ 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);
+}
+
+/*
+ * 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(CERTCertDBHandle *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 = 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->nickname =
+ (char *)PORT_ArenaAlloc(subjectEntry->common.arena,
+ key.size - 1);
+ if ( subjectEntry->emailAddr ) {
+ PORT_Memcpy(subjectEntry->emailAddr, emailAddr,
+ key.size - 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(CERTCertificate *cert, SECItem *k, void *pdata)
+{
+ CERTCertDBHandle *handle;
+ certDBEntryCert *entry;
+ CERTCertTrust *trust;
+
+ handle = (CERTCertDBHandle *)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;
+ }
+
+ entry = AddCertToPermDB(handle, cert, cert->dbEntry->nickname,
+ &cert->dbEntry->trust);
+ if ( entry ) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+
+ return(SECSuccess);
+}
+
+static SECStatus
+UpdateV5DB(CERTCertDBHandle *handle, DB *updatedb)
+{
+ CERTCertDBHandle updatehandle;
+ SECStatus rv;
+
+ updatehandle.permCertDB = updatedb;
+ updatehandle.dbMon = PR_NewMonitor();
+
+ rv = SEC_TraversePermCerts(&updatehandle, updateV5Callback,
+ (void *)handle);
+
+ PR_DestroyMonitor(updatehandle.dbMon);
+
+ return(rv);
+}
+
+static SECStatus
+UpdateV4DB(CERTCertDBHandle *handle, DB *updatedb)
+{
+ DBT key, data;
+ certDBEntryCert *entry, *entry2;
+ SECItem derSubject;
+ int ret;
+ PRArenaPool *arena = NULL;
+ CERTCertificate *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);
+ derSubject.data = NULL;
+
+ if ( entry ) {
+ cert = CERT_DecodeDERCertificate(&entry->derCert, PR_TRUE,
+ entry->nickname);
+
+ if ( cert != NULL ) {
+ /* add to new database */
+ entry2 = AddCertToPermDB(handle, cert, entry->nickname,
+ &entry->trust);
+
+ CERT_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
+SEC_CertDBKeyConflict(SECItem *derCert, CERTCertDBHandle *handle)
+{
+ SECStatus rv;
+ DBT tmpdata;
+ DBT namekey;
+ int ret;
+ SECItem keyitem;
+ PRArenaPool *arena = NULL;
+ SECItem derKey;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ /* get the db key of the cert */
+ rv = CERT_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;
+
+ /* lookup in the temporary database */
+ ret = certdb_Get(handle->tempCertDB, &namekey, &tmpdata, 0);
+
+ if ( ret == 0 ) { /* found in temp database */
+ goto loser;
+ } else { /* not found in temporary database */
+ 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);
+}
+
+#ifdef NOTDEF
+/*
+ * return true if a subject name conflict exists
+ * NOTE: caller must have already made sure that this exact cert
+ * doesn't exist in the DB
+ */
+PRBool
+SEC_CertSubjectConflict(SECItem *derCert, CERTCertDBHandle *handle)
+{
+ SECStatus rv;
+ DBT tmpdata;
+ DBT namekey;
+ int ret;
+ SECItem keyitem;
+ PRArenaPool *arena = NULL;
+ SECItem derName;
+
+ derName.data = NULL;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ /* get the subject name of the cert */
+ rv = CERT_NameFromDERCert(derCert, &derName);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ rv = EncodeDBSubjectKey(&derName, arena, &keyitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ namekey.data = keyitem.data;
+ namekey.size = keyitem.len;
+
+ /* lookup in the temporary database */
+ ret = certdb_Get(handle->tempCertDB, &namekey, &tmpdata, 0);
+
+ if ( ret == 0 ) { /* found in temp database */
+ return(PR_TRUE);
+ } else { /* not found in temporary database */
+ ret = certdb_Get(handle->permCertDB, &namekey, &tmpdata, 0);
+ if ( ret == 0 ) {
+ return(PR_TRUE);
+ }
+ }
+
+ PORT_FreeArena(arena, PR_FALSE);
+ PORT_Free(derName.data);
+
+ return(PR_FALSE);
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ if ( derName.data ) {
+ PORT_Free(derName.data);
+ }
+
+ return(PR_TRUE);
+}
+#endif
+
+/*
+ * return true if a nickname conflict exists
+ * NOTE: caller must have already made sure that this exact cert
+ * doesn't exist in the DB
+ */
+PRBool
+SEC_CertNicknameConflict(char *nickname, SECItem *derSubject,
+ CERTCertDBHandle *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);
+}
+
+/*
+ * Open the certificate database and index databases. Create them if
+ * they are not there or bad.
+ */
+SECStatus
+SEC_OpenPermCertDB(CERTCertDBHandle *handle, PRBool readOnly,
+ CERTDBNameFunc namecb, void *cbarg)
+{
+ SECStatus rv;
+ int openflags;
+ certDBEntryVersion *versionEntry = NULL;
+ DB *updatedb = NULL;
+ char *tmpname;
+ char *certdbname;
+ PRBool updated = PR_FALSE;
+ PRBool forceUpdate = PR_FALSE;
+
+ certdbname = (* namecb)(cbarg, CERT_DB_FILE_VERSION);
+ if ( certdbname == NULL ) {
+ return(SECFailure);
+ }
+
+ if ( readOnly ) {
+ openflags = O_RDONLY;
+ } else {
+ openflags = O_RDWR;
+ }
+
+ /*
+ * first open the permanent file based database.
+ */
+ handle->permCertDB = dbopen( certdbname, openflags, 0600, DB_HASH, 0 );
+
+ /* check for correct version number */
+ if ( handle->permCertDB ) {
+ versionEntry = ReadDBVersionEntry(handle);
+
+ if ( versionEntry == NULL ) {
+ /* no version number */
+ certdb_Close(handle->permCertDB);
+ handle->permCertDB = 0;
+ } else if ( versionEntry->common.version != CERT_DB_FILE_VERSION ) {
+ /* wrong version number, can't update in place */
+ DestroyDBEntry((certDBEntry *)versionEntry);
+ return(SECFailure);
+ }
+
+ }
+
+
+ /* if first open fails, try to create a new DB */
+ if ( handle->permCertDB == NULL ) {
+
+ /* don't create if readonly */
+ if ( readOnly ) {
+ goto loser;
+ }
+
+ handle->permCertDB = dbopen(certdbname,
+ O_RDWR | O_CREAT | O_TRUNC,
+ 0600, DB_HASH, 0);
+
+ /* if create fails then we lose */
+ if ( handle->permCertDB == 0 ) {
+ goto loser;
+ }
+
+ versionEntry = NewDBVersionEntry(0);
+ if ( versionEntry == NULL ) {
+ goto loser;
+ }
+
+ rv = WriteDBVersionEntry(handle, versionEntry);
+
+ DestroyDBEntry((certDBEntry *)versionEntry);
+
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* try to upgrade old db here */
+ tmpname = (* namecb)(cbarg, 6); /* get v6 db name */
+ if ( tmpname ) {
+ updatedb = dbopen( tmpname, O_RDONLY, 0600, DB_HASH, 0 );
+ PORT_Free(tmpname);
+ if ( updatedb ) {
+ rv = UpdateV6DB(handle, updatedb);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ updated = PR_TRUE;
+ } else { /* no v6 db, so try v5 db */
+ tmpname = (* namecb)(cbarg, 5); /* get v5 db name */
+ if ( tmpname ) {
+ updatedb = dbopen( tmpname, O_RDONLY, 0600, DB_HASH, 0 );
+ PORT_Free(tmpname);
+ if ( updatedb ) {
+ rv = UpdateV5DB(handle, updatedb);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ updated = PR_TRUE;
+ } else { /* no v5 db, so try v4 db */
+ /* try to upgrade v4 db */
+ tmpname = (* namecb)(cbarg, 4); /* get v4 db name */
+ if ( tmpname ) {
+ updatedb = dbopen( tmpname, O_RDONLY, 0600,
+ DB_HASH, 0 );
+ PORT_Free(tmpname);
+ if ( updatedb ) {
+ rv = UpdateV4DB(handle, updatedb);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ forceUpdate = PR_TRUE;
+ updated = PR_TRUE;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* initialize the database with our well known certificates
+ * or in the case of update, just fall down to CERT_AddNewCerts()
+ * below.
+ * Note - if we are updating a really old database, then we try
+ * to push all of the certs into it.
+ */
+ if ( ( !updated ) || forceUpdate ) {
+ rv = CERT_InitCertDB(handle);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ }
+ }
+
+ rv = CERT_AddNewCerts(handle);
+
+ return (SECSuccess);
+
+loser:
+
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+
+ if ( handle->permCertDB ) {
+ certdb_Close(handle->permCertDB);
+ handle->permCertDB = 0;
+ }
+
+ return(SECFailure);
+}
+
+/*
+ * delete all DB records associated with a particular certificate
+ */
+static SECStatus
+DeletePermCert(CERTCertificate *cert)
+{
+ SECStatus rv;
+ SECStatus ret;
+
+ ret = SECSuccess;
+
+ rv = DeleteDBCertEntry(cert->dbhandle, &cert->certKey);
+ if ( rv != SECSuccess ) {
+ ret = SECFailure;
+ }
+
+ if ( cert->nickname ) {
+ rv = DeleteDBNicknameEntry(cert->dbhandle, cert->nickname);
+ if ( rv != SECSuccess ) {
+ ret = SECFailure;
+ }
+ }
+
+ rv = RemovePermSubjectNode(cert);
+
+ return(ret);
+}
+
+/*
+ * Delete a certificate from the permanent database.
+ */
+SECStatus
+SEC_DeletePermCertificate(CERTCertificate *cert)
+{
+ SECStatus rv;
+
+ if ( !cert->isperm ) {
+ return(SECSuccess);
+ }
+ CERT_LockDB(cert->dbhandle);
+ /* delete the records from the permanent database */
+ rv = DeletePermCert(cert);
+
+ /* no longer permanent */
+ cert->isperm = PR_FALSE;
+
+ /* get rid of dbcert and stuff pointing to it */
+ DestroyDBEntry((certDBEntry *)cert->dbEntry);
+ cert->dbEntry = NULL;
+ cert->trust = NULL;
+
+ /* delete it from the temporary database too. It will remain in
+ * memory until all references go away.
+ */
+ if (cert->slot) {
+ /* If it's owned by a PKCS #11 slot, don't deleted if from the temp DB just
+ * yet... rv inherited from DeletePermCert (as if anyone checks the return
+ * code from this function anyway. */
+ CERT_DestroyCertificateNoLocking(cert);
+ rv = SECSuccess;
+ } else {
+ rv = CERT_DeleteTempCertificate(cert);
+ }
+
+ CERT_UnlockDB(cert->dbhandle);
+ return(rv);
+}
+
+/*
+ * Lookup a certificate in the databases.
+ */
+certDBEntryCert *
+SEC_FindPermCertByKey(CERTCertDBHandle *handle, SECItem *key)
+{
+ return(ReadDBCertEntry(handle, key));
+}
+
+/*
+ * Lookup a certificate in the database by name
+ */
+certDBEntryCert *
+SEC_FindPermCertByName(CERTCertDBHandle *handle, SECItem *derSubject)
+{
+ certDBEntrySubject *subjectEntry;
+ certDBEntryCert *certEntry;
+
+ subjectEntry = ReadDBSubjectEntry(handle, derSubject);
+
+ if ( subjectEntry == NULL ) {
+ goto loser;
+ }
+
+ certEntry = ReadDBCertEntry(handle, &subjectEntry->certKeys[0]);
+ DestroyDBEntry((certDBEntry *)subjectEntry);
+
+ return(certEntry);
+
+loser:
+ return(NULL);
+}
+
+/*
+ * Lookup a certificate in the database by nickname
+ */
+certDBEntryCert *
+SEC_FindPermCertByNickname(CERTCertDBHandle *handle, char *nickname)
+{
+ certDBEntryNickname *nicknameEntry;
+ certDBEntryCert *certEntry;
+
+ nicknameEntry = ReadDBNicknameEntry(handle, nickname);
+
+ if ( nicknameEntry == NULL ) {
+ goto loser;
+ }
+
+ certEntry = SEC_FindPermCertByName(handle, &nicknameEntry->subjectName);
+ DestroyDBEntry((certDBEntry *)nicknameEntry);
+
+ return(certEntry);
+
+loser:
+ return(NULL);
+}
+
+/*
+ * Traverse all of the entries in the database of a particular type
+ * call the given function for each one.
+ */
+SECStatus
+SEC_TraverseDBEntries(CERTCertDBHandle *handle,
+ certDBEntryType type,
+ SECStatus (* callback)(SECItem *data, SECItem *key,
+ certDBEntryType type, void *pdata),
+ void *udata )
+{
+ DBT data;
+ DBT key;
+ SECStatus rv;
+ 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);
+ }
+
+ 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;
+
+ rv = (* callback)(&dataitem, &keyitem, type, udata);
+ if ( rv != SECSuccess ) {
+ return(rv);
+ }
+ }
+ } while ( certdb_Seq(handle->permCertDB, &key, &data, R_NEXT) == 0 );
+
+ return(SECSuccess);
+}
+
+typedef struct {
+ PermCertCallback certfunc;
+ CERTCertDBHandle *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;
+ CERTCertificate *cert;
+ PRArenaPool *arena = NULL;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ 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;
+
+ cert = CERT_DecodeDERCertificate(&entry.derCert, PR_FALSE,
+ entry.nickname);
+ cert->dbEntry = &entry;
+ cert->trust = &entry.trust;
+ cert->dbhandle = mystate->handle;
+
+ if ( CERT_IsCACert(cert, NULL) ||
+ (( cert->trust->sslFlags & CERTDB_VALID_CA ) ||
+ ( cert->trust->emailFlags & CERTDB_VALID_CA ) ||
+ ( cert->trust->objectSigningFlags & CERTDB_VALID_CA)) ) {
+ cert->nsCertType |= EXT_KEY_USAGE_STATUS_RESPONDER;
+ }
+
+ rv = (* mystate->certfunc)(cert, dbkey, mystate->data);
+
+ /* arena destroyed by SEC_DestroyCert */
+ CERT_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(CERTCertDBHandle *handle,
+ SECStatus (* certfunc)(CERTCertificate *cert,
+ SECItem *k,
+ void *pdata),
+ void *udata )
+{
+ SECStatus rv;
+ PermCertCallbackState mystate;
+
+ mystate.certfunc = certfunc;
+ mystate.handle = handle;
+ mystate.data = udata;
+ rv = SEC_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
+SEC_TraversePermCerts(CERTCertDBHandle *handle,
+ SECStatus (* certfunc)(CERTCertificate *cert, SECItem *k,
+ void *pdata),
+ void *udata )
+{
+ SECStatus rv;
+
+ CERT_LockDB(handle);
+ rv = TraversePermCertsNoLocking(handle, certfunc, udata);
+ CERT_UnlockDB(handle);
+
+ return(rv);
+}
+
+
+
+/*
+ * Close the database
+ */
+void
+CERT_ClosePermCertDB(CERTCertDBHandle *handle)
+{
+ if ( handle ) {
+ if ( handle->permCertDB ) {
+ if ( handle->statusConfig ) {
+ PORT_Assert(handle->statusConfig->statusDestroy != NULL);
+ (void) (* handle->statusConfig->statusDestroy)(handle->statusConfig);
+ handle->statusConfig = NULL; /* Destroy frees the structure */
+ PORT_Assert(handle->statusConfig == NULL);
+ }
+ certdb_Close( handle->permCertDB );
+ handle->permCertDB = 0;
+ }
+ }
+ return;
+}
+
+/*
+ * Get the trust attributes from a certificate
+ */
+SECStatus
+CERT_GetCertTrust(CERTCertificate *cert, CERTCertTrust *trust)
+{
+ SECStatus rv;
+
+ CERT_LockCertTrust(cert);
+
+ if ( cert->trust == NULL ) {
+ rv = SECFailure;
+ } else {
+ *trust = *cert->trust;
+ rv = SECSuccess;
+ }
+
+ CERT_UnlockCertTrust(cert);
+ return(rv);
+}
+
+/*
+ * Change the trust attributes of a certificate and make them permanent
+ * in the database.
+ */
+SECStatus
+CERT_ChangeCertTrust(CERTCertDBHandle *handle, CERTCertificate *cert,
+ CERTCertTrust *trust)
+{
+ certDBEntryCert *entry;
+ int rv;
+ SECStatus ret;
+
+ CERT_LockDB(handle);
+ CERT_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:
+ CERT_UnlockCertTrust(cert);
+ CERT_UnlockDB(handle);
+ return(ret);
+}
+
+
+SECStatus
+CERT_AddTempCertToPerm(CERTCertificate *cert, char *nickname,
+ CERTCertTrust *trust)
+{
+ char *oldnn;
+ certDBEntryCert *entry;
+ SECStatus rv;
+ PRBool conflict;
+ SECStatus ret;
+
+ PORT_Assert(cert->dbhandle);
+
+ CERT_LockDB(cert->dbhandle);
+
+ PORT_Assert(cert->istemp);
+ PORT_Assert(!cert->isperm);
+ PORT_Assert(!cert->dbEntry);
+
+ /* don't add a conflicting nickname */
+ conflict = SEC_CertNicknameConflict(nickname, &cert->derSubject,
+ cert->dbhandle);
+ if ( conflict ) {
+ ret = SECFailure;
+ goto done;
+ }
+
+ /* save old nickname so that we can delete it */
+ oldnn = cert->nickname;
+
+ entry = AddCertToPermDB(cert->dbhandle, cert, nickname, trust);
+
+ if ( entry == NULL ) {
+ ret = SECFailure;
+ goto done;
+ }
+
+ cert->nickname = (entry->nickname) ? PORT_ArenaStrdup(cert->arena,entry->nickname) : NULL;
+ cert->trust = &entry->trust;
+ cert->isperm = PR_TRUE;
+ cert->dbEntry = entry;
+
+ if ( nickname && oldnn && ( PORT_Strcmp(nickname, oldnn) != 0 ) ) {
+ /* only delete the old one if they are not the same */
+ /* delete old nickname from temp database */
+ rv = SEC_DeleteTempNickname(cert->dbhandle, oldnn);
+ if ( rv != SECSuccess ) {
+ /* do we care?? */
+ }
+ }
+ /* add new nickname to temp database */
+ if ( cert->nickname ) {
+ rv = SEC_AddTempNickname(cert->dbhandle, cert->nickname,
+ &cert->derSubject);
+ if ( rv != SECSuccess ) {
+ ret = SECFailure;
+ goto done;
+ }
+ }
+
+ ret = SECSuccess;
+done:
+ CERT_UnlockDB(cert->dbhandle);
+ return(ret);
+}
+
+/*
+ * Open the certificate database and index databases. Create them if
+ * they are not there or bad.
+ */
+SECStatus
+CERT_OpenCertDB(CERTCertDBHandle *handle, PRBool readOnly,
+ CERTDBNameFunc namecb, void *cbarg)
+{
+ int rv;
+
+ certdb_InitDBLock();
+
+ handle->dbMon = PR_NewMonitor();
+ PORT_Assert(handle->dbMon != NULL);
+
+ handle->spkDigestInfo = NULL;
+ handle->statusConfig = NULL;
+
+ /*
+ * Open the memory resident decoded cert database.
+ */
+ handle->tempCertDB = dbopen( 0, O_RDWR | O_CREAT, 0600, DB_HASH, 0 );
+ if ( !handle->tempCertDB ) {
+ goto loser;
+ }
+
+ rv = SEC_OpenPermCertDB(handle, readOnly, namecb, cbarg);
+ if ( rv ) {
+ goto loser;
+ }
+
+ return (SECSuccess);
+
+loser:
+
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+
+ if ( handle->tempCertDB ) {
+ certdb_Close(handle->tempCertDB);
+ handle->tempCertDB = 0;
+ }
+
+ return(SECFailure);
+}
+
+static char *
+certDBFilenameCallback(void *arg, int dbVersion)
+{
+ return((char *)arg);
+}
+
+SECStatus
+CERT_OpenCertDBFilename(CERTCertDBHandle *handle, char *certdbname,
+ PRBool readOnly)
+{
+ return(CERT_OpenCertDB(handle, readOnly, certDBFilenameCallback,
+ (void *)certdbname));
+}
+
+/*
+ * Add a nickname to the temp database
+ */
+SECStatus
+SEC_AddTempNickname(CERTCertDBHandle *handle, char *nickname,
+ SECItem *subjectName)
+{
+ DBT namekey;
+ int ret;
+ SECItem nameitem;
+ SECStatus rv;
+ DBT keydata;
+ PRArenaPool *arena = NULL;
+ SECItem tmpitem;
+
+ PORT_Assert(nickname != NULL);
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ rv = EncodeDBNicknameKey(nickname, arena, &nameitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ namekey.data = nameitem.data;
+ namekey.size = nameitem.len;
+
+ /* see if an entry already exists */
+ ret = certdb_Get(handle->tempCertDB, &namekey, &keydata, 0);
+
+ if ( ret == 0 ) {
+ /* found in temp database */
+ tmpitem.data = (unsigned char*)keydata.data;
+ tmpitem.len = keydata.size;
+
+ if ( SECITEM_CompareItem(subjectName, &tmpitem) == SECEqual ) {
+ /* same subject name */
+ goto done;
+ } else {
+ /* different subject name is an error */
+ goto loser;
+ }
+ }
+
+ keydata.data = subjectName->data;
+ keydata.size = subjectName->len;
+
+ /* put into temp byname index */
+ ret = certdb_Put(handle->tempCertDB, &namekey, &keydata, R_NOOVERWRITE);
+
+ if ( ret ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+done:
+ PORT_FreeArena(arena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return(SECFailure);
+}
+
+SECStatus
+SEC_DeleteTempNickname(CERTCertDBHandle *handle, char *nickname)
+{
+ DBT namekey;
+ SECStatus rv;
+ PRArenaPool *arena = NULL;
+ SECItem nameitem;
+ int ret;
+
+ PORT_Assert(nickname != NULL);
+ if ( nickname == NULL ) {
+ return(SECSuccess);
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+
+ /* format a database key based on the nickname */
+ if ( nickname ) {
+ rv = EncodeDBNicknameKey(nickname, arena, &nameitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ namekey.data = nameitem.data;
+ namekey.size = nameitem.len;
+
+ ret = certdb_Del(handle->tempCertDB, &namekey, 0);
+ if ( ret ) {
+ goto loser;
+ }
+ }
+
+ PORT_FreeArena(arena, PR_FALSE);
+
+ return(SECSuccess);
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return(SECFailure);
+}
+
+/*
+ * Decode a certificate and enter it into the temporary certificate database.
+ * Deal with nicknames correctly
+ *
+ * nickname is only used if isperm == PR_TRUE
+ *
+ * This is the private entry point, and locking is optional
+ */
+static CERTCertificate *
+NewTempCertificate(CERTCertDBHandle *handle, SECItem *derCert, char *nickname,
+ PRBool isperm, PRBool copyDER, PRBool lockdb)
+
+{
+ DBT key;
+ DBT data;
+ int status;
+ CERTCertificate *cert = NULL;
+ PRBool promoteError = PR_TRUE;
+ PRArenaPool *arena = NULL;
+ SECItem keyitem;
+ SECStatus rv;
+
+ if ( isperm == PR_FALSE ) {
+ cert = CERT_FindCertByDERCert(handle, derCert);
+ if ( cert ) {
+ return(cert);
+ }
+
+ nickname = NULL;
+ }
+
+ if ( lockdb ) {
+ CERT_LockDB(handle);
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ cert = CERT_DecodeDERCertificate(derCert, copyDER, nickname );
+
+ if ( cert == NULL ) {
+ /* We want to save the decoding error here */
+ promoteError = PR_FALSE;
+ goto loser;
+ }
+
+ cert->dbhandle = handle;
+
+ /* only save pointer to cert in database */
+ data.data = &cert;
+ data.size = sizeof(cert);
+
+ /* if this is a perm cert, then it is already in the subject db */
+ if ( isperm == PR_FALSE ) {
+ /* enter into the subject index */
+ rv = AddTempCertToSubjectList(cert);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ /*
+ * Since it's not a perm cert, add it to the key hash lookup; if it
+ * is permanent it will either already be there or will get put there
+ * later along with the rest of the perm certs. A failure of the
+ * addition does not seem to warrant failing this whole function,
+ * so we intentionally ignore the returned status.
+ */
+ (void) AddCertToSPKDigestTable(handle, cert);
+ } else {
+ cert->subjectList = FindSubjectList(cert->dbhandle, &cert->derSubject,
+ PR_FALSE);
+ }
+
+ rv = EncodeDBCertKey(&cert->certKey, arena, &keyitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ key.data = keyitem.data;
+ key.size = keyitem.len;
+
+ /* enter into main db */
+ status = certdb_Put(handle->tempCertDB, &key, &data, R_NOOVERWRITE);
+ if ( status ) {
+ goto loser;
+ }
+
+ if ( cert->nickname ) {
+ status = SEC_AddTempNickname(handle, cert->nickname,
+ &cert->derSubject);
+ if ( status ) {
+ promoteError = PR_FALSE;
+ goto loser;
+ }
+ }
+
+ cert->isperm = isperm;
+ cert->istemp = PR_TRUE;
+
+ PORT_FreeArena(arena, PR_FALSE);
+
+ if ( lockdb ) {
+ CERT_UnlockDB(handle);
+ }
+
+ return(cert);
+
+loser:
+ if ( cert ) {
+ CERT_DestroyCertificateNoLocking(cert);
+ }
+
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ if ( promoteError ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ }
+
+ if ( lockdb ) {
+ CERT_UnlockDB(handle);
+ }
+
+ return(0);
+}
+
+/*
+ * Decode a certificate and enter it into the temporary certificate database.
+ * Deal with nicknames correctly
+ *
+ * nickname is only used if isperm == PR_TRUE
+ *
+ * This is the public entry point and does locking.
+ */
+CERTCertificate *
+CERT_NewTempCertificate(CERTCertDBHandle *handle, SECItem *derCert,
+ char *nickname, PRBool isperm, PRBool copyDER)
+{
+ return( NewTempCertificate(handle, derCert, nickname, isperm, copyDER,
+ PR_TRUE) );
+}
+
+/*
+ * Decode a permanent certificate and enter it into the temporary certificate
+ * database.
+ */
+static CERTCertificate *
+SEC_AddPermCertToTemp(CERTCertDBHandle *handle, certDBEntryCert *entry)
+{
+ CERTCertificate *cert;
+
+ /* we already hold the lock */
+ cert = NewTempCertificate(handle, &entry->derCert, entry->nickname,
+ PR_TRUE, PR_TRUE, PR_FALSE);
+ if ( !cert ) {
+ return(0);
+ }
+
+ cert->dbEntry = entry;
+
+ cert->trust = &entry->trust;
+
+ return(cert);
+}
+
+SECStatus
+CERT_DeleteTempCertificate(CERTCertificate *cert)
+{
+ SECStatus rv;
+ DBT nameKey;
+ CERTCertDBHandle *handle;
+ SECItem keyitem;
+ PRArenaPool *arena;
+ int ret;
+
+ handle = cert->dbhandle;
+
+ if ( !cert->istemp ) {
+ return(SECSuccess);
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ if (cert->slot) {
+ PK11_FreeSlot(cert->slot);
+ cert->slot = NULL;
+ cert->pkcs11ID = CK_INVALID_KEY;
+ }
+
+ /* delete from subject list (also takes care of nickname) */
+ rv = RemoveTempCertFromSubjectList(cert);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ if ( !cert->isperm ) {
+ /*
+ * Remove the cert from the subject public key digest table,
+ * though we do not care if the removal fails (perhaps meaning
+ * the cert wasn't even there).
+ */
+ (void) RemoveCertFromSPKDigestTable(handle, cert);
+ }
+
+ rv = EncodeDBCertKey(&cert->certKey, arena, &keyitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ nameKey.data = keyitem.data;
+ nameKey.size = keyitem.len;
+ /* delete the cert */
+ ret = certdb_Del(handle->tempCertDB, &nameKey, 0);
+ if ( ret ) {
+ goto loser;
+ }
+
+ cert->istemp = PR_FALSE;
+
+ PORT_FreeArena(arena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(SECFailure);
+}
+
+/*
+ * Lookup a certificate in the databases.
+ */
+static CERTCertificate *
+FindCertByKey(CERTCertDBHandle *handle, SECItem *certKey, PRBool lockdb)
+{
+ DBT tmpdata;
+ int ret;
+ SECItem keyitem;
+ DBT key;
+ SECStatus rv;
+ CERTCertificate *cert = NULL;
+ PRArenaPool *arena = NULL;
+ certDBEntryCert *entry;
+ PRBool locked = PR_FALSE;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBCertKey(certKey, arena, &keyitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ key.data = keyitem.data;
+ key.size = keyitem.len;
+
+ if ( lockdb ) {
+ locked = PR_TRUE;
+ CERT_LockDB(handle);
+ }
+
+ /* lookup in the temporary database */
+ ret = certdb_Get( handle->tempCertDB, &key, &tmpdata, 0 );
+
+ /* error accessing the database */
+ if ( ret < 0 ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ if ( ret == 0 ) { /* found in temp database */
+ if ( tmpdata.size != sizeof(CERTCertificate *) ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ PORT_Memcpy(&cert, tmpdata.data, tmpdata.size);
+ CERT_LockCertRefCount(cert);
+ cert->referenceCount++;
+ CERT_UnlockCertRefCount(cert);
+ }
+ if ( ret != 0 ) {
+ /* not found in temporary database */
+
+ /* find in perm database */
+ entry = SEC_FindPermCertByKey(handle, certKey);
+
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+ cert = SEC_AddPermCertToTemp(handle, entry);
+ }
+
+loser:
+ if ( locked ) {
+ CERT_UnlockDB(handle);
+ }
+
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(cert);
+}
+
+/*
+ * Lookup a certificate in the databases, with locking
+ */
+CERTCertificate *
+CERT_FindCertByKey(CERTCertDBHandle *handle, SECItem *certKey)
+{
+ return(FindCertByKey(handle, certKey, PR_TRUE));
+}
+
+/*
+ * Lookup a certificate in the databases without locking
+ */
+CERTCertificate *
+CERT_FindCertByKeyNoLocking(CERTCertDBHandle *handle, SECItem *certKey)
+{
+ return(FindCertByKey(handle, certKey, PR_FALSE));
+}
+
+/*
+ * Generate a key from an issuerAndSerialNumber, and find the
+ * associated cert in the database.
+ */
+CERTCertificate *
+CERT_FindCertByIssuerAndSN(CERTCertDBHandle *handle, CERTIssuerAndSN *issuerAndSN)
+{
+ SECItem certKey;
+ CERTCertificate *cert;
+
+ certKey.len = issuerAndSN->serialNumber.len + issuerAndSN->derIssuer.len;
+ certKey.data = (unsigned char*)PORT_Alloc(certKey.len);
+
+ if ( certKey.data == NULL ) {
+ return(0);
+ }
+
+ /* copy the serialNumber */
+ PORT_Memcpy(certKey.data, issuerAndSN->serialNumber.data,
+ issuerAndSN->serialNumber.len);
+
+ /* copy the issuer */
+ PORT_Memcpy( &certKey.data[issuerAndSN->serialNumber.len],
+ issuerAndSN->derIssuer.data, issuerAndSN->derIssuer.len);
+
+ cert = CERT_FindCertByKey(handle, &certKey);
+
+ PORT_Free(certKey.data);
+
+ return(cert);
+}
+
+/*
+ * Lookup a certificate in the database by name
+ */
+CERTCertificate *
+CERT_FindCertByName(CERTCertDBHandle *handle, SECItem *name)
+{
+ CERTCertificate *cert = NULL;
+ CERTSubjectList *subjectList;
+
+ CERT_LockDB(handle);
+
+ subjectList = FindSubjectList(handle, name, PR_FALSE);
+
+ if ( subjectList ) {
+ PORT_Assert(subjectList->head);
+ cert = CERT_FindCertByKeyNoLocking(handle,
+ &subjectList->head->certKey);
+ }
+
+ CERT_UnlockDB(handle);
+
+ return(cert);
+}
+
+/*
+ * Lookup a certificate in the database by name and key ID
+ */
+CERTCertificate *
+CERT_FindCertByKeyID(CERTCertDBHandle *handle, SECItem *name, SECItem *keyID)
+{
+ CERTCertificate *cert = NULL;
+ CERTSubjectList *subjectList;
+ CERTSubjectNode *node;
+
+ CERT_LockDB(handle);
+
+ /* find the list of certs for the given subject */
+ subjectList = FindSubjectList(handle, name, PR_FALSE);
+
+ if ( subjectList ) {
+ PORT_Assert(subjectList->head);
+ node = subjectList->head;
+
+ /* walk through the certs until we find one with a matching key ID */
+ while ( node ) {
+ if ( SECITEM_CompareItem(keyID, &node->keyID) == SECEqual ) {
+ cert = CERT_FindCertByKeyNoLocking(handle, &node->certKey);
+ break;
+ }
+ node = node->next;
+ }
+ }
+
+ CERT_UnlockDB(handle);
+
+ return(cert);
+}
+
+/*
+ * look up a cert by its nickname string
+ */
+CERTCertificate *
+CERT_FindCertByNickname(CERTCertDBHandle *handle, char *nickname)
+{
+ DBT tmpdata;
+ DBT namekey;
+ CERTCertificate *cert;
+ SECStatus rv;
+ int ret;
+ SECItem keyitem;
+ PRArenaPool *arena = NULL;
+ certDBEntryCert *entry;
+
+ PORT_Assert(nickname != NULL);
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBNicknameKey(nickname, arena, &keyitem);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ namekey.data = keyitem.data;
+ namekey.size = keyitem.len;
+
+ /* lookup in the temporary database */
+ ret = certdb_Get(handle->tempCertDB, &namekey, &tmpdata, 0);
+
+ /* error accessing the database */
+ if ( ret < 0 ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ if ( ret == 0 ) { /* found in temp database */
+ SECItem nameitem;
+
+ nameitem.len = tmpdata.size;
+ nameitem.data = (unsigned char *)PORT_Alloc(tmpdata.size);
+ if ( nameitem.data == NULL ) {
+ goto loser;
+ }
+ PORT_Memcpy(nameitem.data, tmpdata.data, nameitem.len);
+ cert = CERT_FindCertByName(handle, &nameitem);
+ PORT_Free(nameitem.data);
+ } else { /* not found in temporary database */
+
+ CERT_LockDB(handle);
+
+ entry = SEC_FindPermCertByNickname(handle, nickname);
+
+ if ( entry == NULL ) {
+ CERT_UnlockDB(handle);
+ goto loser;
+ }
+
+ cert = SEC_AddPermCertToTemp(handle, entry);
+ CERT_UnlockDB(handle);
+ }
+
+ PORT_FreeArena(arena, PR_FALSE);
+
+ return(cert);
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(0);
+}
+
+/*
+ * look for the given DER certificate in the database
+ */
+CERTCertificate *
+CERT_FindCertByDERCert(CERTCertDBHandle *handle, SECItem *derCert)
+{
+ PRArenaPool *arena;
+ SECItem certKey;
+ SECStatus rv;
+ CERTCertificate *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 = CERT_KeyFromDERCert(arena, derCert, &certKey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* find the certificate */
+ cert = CERT_FindCertByKey(handle, &certKey);
+
+loser:
+ PORT_FreeArena(arena, PR_FALSE);
+ return(cert);
+}
+
+/*
+ * The following is bunch of types and code to allow looking up a certificate
+ * by a hash of its subject public key. Because the words "hash" and "key"
+ * are overloaded and thus terribly confusing, I tried to disambiguate things.
+ * - Where I could, I used "digest" instead of "hash" when referring to
+ * hashing of the subject public key. The PLHashTable interfaces and
+ * our own HASH_Foo interfaces had to be left as is, obviously. The latter
+ * should be thought of as "digest" in this case.
+ * - There are three keys in use here -- the subject public key, the key
+ * used to do a lookup in the PLHashTable, and the key used to do a lookup
+ * in the cert database. As the latter is a fairly pervasive interface,
+ * I left it alone. The other two uses I changed to "spk" or "SPK" when
+ * referring to the subject public key, and "index" when referring to the
+ * key into the PLHashTable.
+ */
+
+typedef struct SPKDigestInfoStr {
+ PLHashTable *table;
+ PRBool permPopulated;
+} SPKDigestInfo;
+
+/*
+ * Since the key hash information is "hidden" (in a void pointer in the handle)
+ * these macros with the appropriate casts make it easy to get at the parts.
+ */
+#define SPK_DIGEST_TABLE(handle) \
+ (((SPKDigestInfo *)(handle->spkDigestInfo))->table)
+
+/*
+ * Create the key hash lookup table. Note that the table, and the
+ * structure which holds it and a little more information, is never freed.
+ * This is because the temporary database is never actually closed out,
+ * so there is no safe/obvious place to free the whole thing. Also,
+ * every time an entry is added to the table, the key under which it is
+ * added is leaked. I think the only way to fix that is to provide our
+ * own allocation functions to PL_NewHashTable. But I'm not in the mood
+ * to do that, given that all of this is about to change, and we should
+ * have the key hash lookup done inside the permanent database itself.
+ *
+ * The database must be locked already.
+ */
+static SECStatus
+InitDBspkDigestInfo(CERTCertDBHandle *handle)
+{
+ SPKDigestInfo *spkDigestInfo;
+ PLHashTable *table;
+
+ PORT_Assert(handle != NULL);
+ PORT_Assert(handle->spkDigestInfo == NULL);
+
+ spkDigestInfo = PORT_ZAlloc(sizeof(SPKDigestInfo));
+ if ( spkDigestInfo == NULL ) {
+ return(SECFailure);
+ }
+
+ table = PL_NewHashTable(128, PL_HashString, PL_CompareStrings,
+ (PLHashComparator) SECITEM_ItemsAreEqual,
+ NULL, NULL);
+ if ( table == NULL ) {
+ PORT_Free(spkDigestInfo);
+ return(SECFailure);
+ }
+
+ spkDigestInfo->table = table;
+ handle->spkDigestInfo = spkDigestInfo;
+ return(SECSuccess);
+}
+
+static SECHashObject *
+OidTagToRawDigestObject(SECOidTag digestAlg)
+{
+ SECHashObject *rawDigestObject;
+
+ switch (digestAlg) {
+ case SEC_OID_MD2:
+ rawDigestObject = &SECRawHashObjects[HASH_AlgMD2];
+ break;
+ case SEC_OID_MD5:
+ rawDigestObject = &SECRawHashObjects[HASH_AlgMD5];
+ break;
+ case SEC_OID_SHA1:
+ rawDigestObject = &SECRawHashObjects[HASH_AlgSHA1];
+ break;
+ default:
+ PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
+ rawDigestObject = NULL;
+ break;
+ }
+ return(rawDigestObject);
+}
+
+/*
+ * Digest the cert's subject public key using the specified algorithm.
+ * The necessary storage for the digest data is allocated. If "fill" is
+ * non-null, the data is put there, otherwise a SECItem is allocated.
+ * Allocation from "arena" if it is non-null, heap otherwise. Any problem
+ * results in a NULL being returned (and an appropriate error set).
+ */
+SECItem *
+CERT_SPKDigestValueForCert(PRArenaPool *arena, CERTCertificate *cert,
+ SECOidTag digestAlg, SECItem *fill)
+{
+ SECHashObject *digestObject;
+ void *digestContext;
+ SECItem *result = NULL;
+ void *mark = NULL;
+ SECItem spk;
+
+ if ( arena != NULL ) {
+ mark = PORT_ArenaMark(arena);
+ }
+
+ /*
+ * This can end up being called before PKCS #11 is initialized,
+ * so we have to use the raw digest functions.
+ */
+ digestObject = OidTagToRawDigestObject(digestAlg);
+ if ( digestObject == NULL ) {
+ goto loser;
+ }
+
+ result = SECITEM_AllocItem(arena, fill, digestObject->length);
+ if ( result == NULL ) {
+ goto loser;
+ }
+
+ /*
+ * Copy just the length and data pointer (nothing needs to be freed)
+ * of the subject public key so we can convert the length from bits
+ * to bytes, which is what the digest function expects.
+ */
+ spk = cert->subjectPublicKeyInfo.subjectPublicKey;
+ DER_ConvertBitString(&spk);
+
+ /*
+ * Now digest the value, using the specified algorithm.
+ */
+ digestContext = digestObject->create();
+ if ( digestContext == NULL ) {
+ goto loser;
+ }
+ digestObject->begin(digestContext);
+ digestObject->update(digestContext, spk.data, spk.len);
+ digestObject->end(digestContext, result->data, &(result->len), result->len);
+ digestObject->destroy(digestContext, PR_TRUE);
+
+ if ( arena != NULL ) {
+ PORT_ArenaUnmark(arena, mark);
+ }
+ return(result);
+
+loser:
+ if ( arena != NULL ) {
+ PORT_ArenaRelease(arena, mark);
+ } else {
+ if ( result != NULL ) {
+ SECITEM_FreeItem(result, (fill == NULL) ? PR_TRUE : PR_FALSE);
+ }
+ }
+ return(NULL);
+}
+
+/*
+ * Return the index for the spk digest lookup table for "spkDigest".
+ *
+ * Caller is responsible for freeing the returned string.
+ */
+static char *
+spkDigestIndexFromDigest(SECItem *spkDigest)
+{
+ return BTOA_ConvertItemToAscii(spkDigest);
+}
+
+/*
+ * Return the index for the spk digest lookup table for this certificate,
+ * based on the specified digest algorithm.
+ *
+ * Caller is responsible for freeing the returned string.
+ */
+static char *
+spkDigestIndexFromCert(CERTCertificate *cert, SECOidTag digestAlg)
+{
+ SECItem *spkDigest;
+ char *index;
+
+ spkDigest = CERT_SPKDigestValueForCert(NULL, cert, digestAlg, NULL);
+ if ( spkDigest == NULL )
+ return(NULL);
+
+ index = spkDigestIndexFromDigest(spkDigest);
+
+ SECITEM_FreeItem(spkDigest, PR_TRUE);
+
+ return(index);
+}
+
+/*
+ * Remove the spk digest for the given cert from the spk digest table,
+ * based on the given digest algorithm.
+ *
+ * The database must be locked already.
+ */
+static SECStatus
+RemoveCertFromSPKDigestTableForAlg(CERTCertDBHandle *handle,
+ CERTCertificate *cert, SECOidTag digestAlg)
+{
+ SECStatus rv = SECSuccess;
+ SECItem *certDBKey = NULL;
+ char *index = NULL;
+ PLHashTable *table;
+
+ /* Expect to only be called if there is a table to work with. */
+ PORT_Assert(handle->spkDigestInfo != NULL);
+
+ table = SPK_DIGEST_TABLE(handle);
+ PORT_Assert(table != NULL);
+
+ index = spkDigestIndexFromCert(cert, digestAlg);
+ if ( index == NULL ) {
+ rv = SECFailure;
+ goto done;
+ }
+
+ /*
+ * Get hold of this value so we can free it after doing the remove.
+ */
+ certDBKey = PL_HashTableLookup(table, index);
+ if ( certDBKey == NULL ) {
+ /* not found means nothing to remove, which is fine */
+ goto done;
+ }
+
+ if ( PL_HashTableRemove(table, index) != PR_TRUE ) {
+ /* This should not actually happen, since we already did a lookup. */
+ PORT_Assert(0);
+ rv = SECFailure;
+ }
+
+done:
+ if ( certDBKey != NULL ) {
+ SECITEM_FreeItem(certDBKey, PR_TRUE);
+ }
+ if ( index != NULL ) {
+ PORT_Free(index);
+ }
+ return(rv);
+}
+
+/*
+ * Remove the spk digests for the given cert from the spk digest table,
+ * for all known digest algorithms.
+ *
+ * The database must be locked already.
+ */
+static SECStatus
+RemoveCertFromSPKDigestTable(CERTCertDBHandle *handle, CERTCertificate *cert)
+{
+ /*
+ * If no certs have been added yet, then nothing to do.
+ */
+ if ( handle->spkDigestInfo == NULL ) {
+ return(SECSuccess);
+ }
+
+ (void) RemoveCertFromSPKDigestTableForAlg(handle, cert, SEC_OID_MD2);
+ (void) RemoveCertFromSPKDigestTableForAlg(handle, cert, SEC_OID_MD5);
+ return RemoveCertFromSPKDigestTableForAlg(handle, cert, SEC_OID_SHA1);
+}
+
+/*
+ * Add the spk digest for the given cert to the spk digest table,
+ * based on the given digest algorithm.
+ *
+ * If a cert for the same spk digest is already in the table, choose whichever
+ * cert is "newer". (The other cert cannot be found via spk digest.)
+ *
+ * The database must be locked already.
+ *
+ * XXX Note that this implementation results in leaking the index value.
+ * Fixing that did not seem worth the trouble, given we will only leak
+ * once per cert. This whole thing should be done differently in the
+ * new rewrite (Stan), and then the problem will go away.
+ */
+static SECStatus
+AddCertToSPKDigestTableForAlg(CERTCertDBHandle *handle, CERTCertificate *cert,
+ SECItem *certDBKey, SECOidTag digestAlg)
+{
+ SECStatus rv = SECFailure;
+ SECItem *oldCertDBKey = NULL;
+ PRBool addit = PR_TRUE;
+ CERTCertificate *oldCert = NULL;
+ char *index = NULL;
+ PLHashTable *table;
+
+ /*
+ * After running some testing doing key hash lookups (like using OCSP),
+ * if these are never hit, they can probably be removed.
+ */
+ PORT_Assert(handle != NULL);
+ PORT_Assert(handle == cert->dbhandle);
+ PORT_Assert(handle->spkDigestInfo != NULL);
+ PORT_Assert((certDBKey == &cert->certKey)
+ || (SECITEM_CompareItem(certDBKey,
+ &cert->certKey) == SECEqual));
+
+ table = SPK_DIGEST_TABLE(handle);
+ PORT_Assert(table != NULL);
+
+ index = spkDigestIndexFromCert(cert, digestAlg);
+ if ( index == NULL ) {
+ goto loser;
+ }
+
+ /*
+ * See if this cert's spk digest is already in the table.
+ */
+ oldCertDBKey = PL_HashTableLookup(table, index);
+ if ( oldCertDBKey != NULL ) {
+ /*
+ * The spk digest *is* already in the table. We need to find that
+ * cert and see -- if it is the same, then we can just leave as is.
+ * Otherwise we have to choose which cert we want represented;
+ * in that case the best plan I can think of is to hang onto the
+ * most recent one.
+ */
+ oldCert = CERT_FindCertByKey(handle, oldCertDBKey);
+ if ( oldCert != NULL ) {
+ if ( cert == oldCert ) {
+ /* They are the same cert, so we are done. */
+ addit = PR_FALSE;
+ oldCertDBKey = NULL; /* don't want to free it */
+ } else if ( CERT_IsNewer(cert, oldCert) ) {
+ if ( PL_HashTableRemove(table, index) != PR_TRUE ) {
+ goto loser;
+ }
+ } else {
+ /* oldCert is "newer", so we are done. */
+ addit = PR_FALSE;
+ oldCertDBKey = NULL; /* don't want to free it */
+ }
+ }
+ }
+
+ if ( addit ) {
+ certDBKey = SECITEM_DupItem(certDBKey);
+ if ( certDBKey == NULL ) {
+ goto loser;
+ }
+ if ( PL_HashTableAdd(table, index, certDBKey) == NULL ) {
+ SECITEM_FreeItem(certDBKey, PR_TRUE);
+ PORT_SetError(SEC_ERROR_NO_MEMORY);
+ goto loser;
+ }
+ index = NULL; /* don't want to free it */
+ }
+
+ rv = SECSuccess;
+
+loser:
+ if ( oldCertDBKey != NULL ) {
+ SECITEM_FreeItem(oldCertDBKey, PR_TRUE);
+ }
+ if ( index != NULL ) {
+ PORT_Free(index);
+ }
+ if ( oldCert != NULL ) {
+ CERT_DestroyCertificate(oldCert);
+ }
+ return(rv);
+}
+
+/*
+ * Add the spk digest for the given cert to the spk digest table,
+ * for all known digest algorithms.
+ *
+ * The database must be locked already, and the digest table already created.
+ */
+static SECStatus
+AddCertToSPKDigestTableForAllAlgs(CERTCertDBHandle *handle,
+ CERTCertificate *cert, SECItem *certDBKey)
+{
+ (void) AddCertToSPKDigestTableForAlg(handle, cert, certDBKey, SEC_OID_MD2);
+ (void) AddCertToSPKDigestTableForAlg(handle, cert, certDBKey, SEC_OID_MD5);
+ return AddCertToSPKDigestTableForAlg(handle, cert, certDBKey, SEC_OID_SHA1);
+}
+
+/*
+ * Add the spk digests for the given cert to the spk digest table,
+ * for all known digest algorithms. (This function is called when a
+ * new cert is added to the temporary database.)
+ *
+ * If the spk digest table does not yet exist, create it.
+ *
+ * In an ideal world, we would not hardwire the digest algorithms.
+ * But it is the case that we do not currently support any digest
+ * algorithms outside of these three. In the newer, cleaned-up world,
+ * this may be done differently.
+ *
+ * The database must be locked already.
+ */
+static SECStatus
+AddCertToSPKDigestTable(CERTCertDBHandle *handle, CERTCertificate *cert)
+{
+ SECStatus rv;
+
+ if ( handle->spkDigestInfo == NULL ) {
+ rv = InitDBspkDigestInfo(handle);
+ if ( rv != SECSuccess ) {
+ return(rv);
+ }
+ }
+
+ return AddCertToSPKDigestTableForAllAlgs(handle, cert, &cert->certKey);
+}
+
+/*
+ * Add the spk digest for the given cert to the spk digest table,
+ * for all known digest algorithms. This function is called while
+ * traversing all of the certs in the permanent database -- since
+ * that imposes some constraints on its arguments this routine is a
+ * simple cover for the "real" interface.
+ *
+ * The database must be locked already, and the digest table already created.
+ */
+static SECStatus
+AddCertToSPKDigestTableInTraversal(CERTCertificate *cert, SECItem *certDBKey,
+ void *data)
+{
+ CERTCertDBHandle *handle = data;
+
+ return AddCertToSPKDigestTableForAllAlgs(handle, cert, certDBKey);
+}
+
+/*
+ * Add the spk digests for the all permanent certs to the spk digest table,
+ * for all known digest algorithms.
+ *
+ * This locks the database, and then checks to make sure that the work
+ * actually needs to get done.
+ *
+ * If the spk digest table does not yet exist, it is created.
+ */
+static SECStatus
+PopulateSPKDigestTable(CERTCertDBHandle *handle)
+{
+ SPKDigestInfo *spkDigestInfo;
+ SECStatus rv = SECSuccess;
+
+ CERT_LockDB(handle);
+
+ spkDigestInfo = handle->spkDigestInfo;
+ if ( spkDigestInfo == NULL ) {
+ rv = InitDBspkDigestInfo(handle);
+ if ( rv != SECSuccess ) {
+ return(rv);
+ }
+ spkDigestInfo = handle->spkDigestInfo;
+ PORT_Assert(spkDigestInfo != NULL);
+ } else {
+ /*
+ * Check to see if someone already did it; it is important to do
+ * this after getting the lock.
+ */
+ if ( spkDigestInfo->permPopulated == PR_TRUE ) {
+ goto done;
+ }
+ }
+
+ rv = TraversePermCertsNoLocking(handle, AddCertToSPKDigestTableInTraversal,
+ handle);
+
+ if ( rv != SECSuccess ) {
+ goto done;
+ }
+
+ spkDigestInfo->permPopulated = PR_TRUE;
+
+done:
+ CERT_UnlockDB(handle);
+
+ return(rv);
+}
+
+/*
+ * Lookup a certificate by a digest of a subject public key. If it is
+ * found, it is returned (and must then be destroyed by the caller).
+ * NULL is returned otherwise -- if there was a problem performing the
+ * lookup, an appropriate error is set (e.g. SEC_ERROR_NO_MEMORY);
+ * if the cert simply was not found, the error is SEC_ERROR_UNKNOWN_CERT.
+ *
+ * If the lookup table has not yet been created or populated, do that first.
+ */
+CERTCertificate *
+CERT_FindCertBySPKDigest(CERTCertDBHandle *handle, SECItem *spkDigest)
+{
+ SPKDigestInfo *spkDigestInfo;
+ char *index = NULL;
+ SECItem *certDBKey;
+ CERTCertificate *cert = NULL;
+
+ PORT_Assert(handle != NULL);
+ spkDigestInfo = handle->spkDigestInfo;
+
+ if ( spkDigestInfo == NULL || spkDigestInfo->permPopulated != PR_TRUE ) {
+ if ( PopulateSPKDigestTable(handle) != SECSuccess ) {
+ goto loser;
+ }
+ }
+
+ index = spkDigestIndexFromDigest(spkDigest);
+ if ( index == NULL ) {
+ goto loser;
+ }
+
+ certDBKey = PL_HashTableLookup(SPK_DIGEST_TABLE(handle), index);
+ if ( certDBKey != NULL ) {
+ cert = CERT_FindCertByKey(handle, certDBKey);
+ }
+
+ if ( cert == NULL ) {
+ PORT_SetError(SEC_ERROR_UNKNOWN_CERT);
+ }
+
+loser:
+ if ( index != NULL ) {
+ PORT_Free(index);
+ }
+ return(cert);
+}
+
+static void
+DestroyCertificate(CERTCertificate *cert, PRBool lockdb)
+{
+ int refCount;
+ CERTCertDBHandle *handle;
+
+ if ( cert ) {
+
+ handle = cert->dbhandle;
+
+ /*
+ * handle may be NULL, for example if the cert was created with
+ * CERT_DecodeDERCertificate.
+ */
+ if ( lockdb && handle ) {
+ CERT_LockDB(handle);
+ }
+
+ CERT_LockCertRefCount(cert);
+ PORT_Assert(cert->referenceCount > 0);
+ refCount = --cert->referenceCount;
+ CERT_UnlockCertRefCount(cert);
+
+ if ( ( refCount == 0 ) && !cert->keepSession ) {
+ certDBEntryCert *entry = cert->dbEntry;
+ PRArenaPool * arena = cert->arena;
+
+ if ( cert->istemp ) {
+ CERT_DeleteTempCertificate(cert);
+ }
+
+ if ( entry ) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+
+ /* zero cert before freeing. Any stale references to this cert
+ * after this point will probably cause an exception. */
+ PORT_Memset(cert, 0, sizeof *cert);
+
+ cert = NULL;
+
+ /* free the arena that contains the cert. */
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ if ( lockdb && handle ) {
+ CERT_UnlockDB(handle);
+ }
+ }
+
+ return;
+}
+
+void
+CERT_DestroyCertificate(CERTCertificate *cert)
+{
+ DestroyCertificate(cert, PR_TRUE);
+ return;
+}
+
+void
+CERT_DestroyCertificateNoLocking(CERTCertificate *cert)
+{
+ DestroyCertificate(cert, PR_FALSE);
+ return;
+}
+
+/*
+ * cache a CRL from the permanent database into the temporary database
+ */
+CERTSignedCrl *
+SEC_AddPermCrlToTemp(CERTCertDBHandle *handle, certDBEntryRevocation *entry)
+{
+ CERTSignedCrl *crl = NULL;
+ DBT key;
+ DBT data;
+ int status;
+ PRArenaPool *arena = NULL;
+ SECItem keyitem;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ crl = CERT_DecodeDERCrl(NULL, &entry->derCrl,
+ entry->common.type == certDBEntryTypeRevocation ?
+ SEC_CRL_TYPE : SEC_KRL_TYPE);
+
+ if ( crl == NULL ) {
+ goto loser;
+ }
+
+ /* only save pointer to cert in database */
+ data.data = &crl;
+ data.size = sizeof(crl);
+
+
+ rv = EncodeDBGenericKey(&(crl->crl.derName), arena,
+ &keyitem, entry->common.type);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ if (entry->url) {
+ int nnlen = PORT_Strlen(entry->url) + 1;
+ crl->url = (char *)PORT_ArenaAlloc(crl->arena, nnlen);
+ if ( !crl->url ) {
+ goto loser;
+ }
+ PORT_Memcpy(crl->url, entry->url, nnlen);
+ } else {
+ crl->url = NULL;
+ }
+
+ key.data = keyitem.data;
+ key.size = keyitem.len;
+
+ /* enter into main db */
+ status = certdb_Put(handle->tempCertDB, &key, &data, R_NOOVERWRITE);
+ if ( status ) {
+ goto loser;
+ }
+
+ crl->istemp = PR_TRUE;
+ crl->isperm = PR_TRUE;
+ crl->dbhandle = handle;
+ crl->dbEntry = entry;
+
+
+ PORT_FreeArena(arena, PR_FALSE);
+
+ crl->keep = PR_TRUE;
+ return(crl);
+
+loser:
+ if ( crl ) {
+ SEC_DestroyCrl(crl);
+ }
+
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ return(0);
+}
+
+SECStatus
+SEC_DeleteTempCrl(CERTSignedCrl *crl)
+{
+ SECStatus rv;
+ DBT nameKey;
+ CERTCertDBHandle *handle;
+ SECItem keyitem;
+ PRArenaPool *arena;
+ int ret;
+
+ handle = crl->dbhandle;
+
+ if ( !crl->istemp ) {
+ return(SECSuccess);
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ rv = EncodeDBGenericKey
+ (&crl->crl.derName, arena, &keyitem, crl->dbEntry->common.type);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ nameKey.data = keyitem.data;
+ nameKey.size = keyitem.len;
+
+ /* delete the cert */
+ ret = certdb_Del(handle->tempCertDB, &nameKey, 0);
+ if ( ret ) {
+ goto loser;
+ }
+
+ crl->istemp = PR_FALSE;
+
+ PORT_FreeArena(arena, PR_FALSE);
+ return(SECSuccess);
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(SECFailure);
+}
+
+/*
+ * Lookup a CRL in the databases. We mirror the same fast caching data base
+ * caching stuff used by certificates....?
+ */
+CERTSignedCrl *
+SEC_FindCrlByKey(CERTCertDBHandle *handle, SECItem *crlKey, int type)
+{
+ DBT tmpdata;
+ int ret;
+ SECItem keyitem;
+ DBT key;
+ SECStatus rv;
+ CERTSignedCrl *crl = NULL;
+ PRArenaPool *arena = NULL;
+ certDBEntryRevocation *entry;
+ certDBEntryType crlType = (type == SEC_CRL_TYPE) ?
+ certDBEntryTypeRevocation : certDBEntryTypeKeyRevocation;
+
+ 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;
+
+ /* lookup in the temporary database */
+ ret = certdb_Get( handle->tempCertDB, &key, &tmpdata, 0 );
+
+ /* error accessing the database */
+ if ( ret < 0 ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ if ( ret == 0 ) { /* found in temp database */
+ if ( tmpdata.size != sizeof(CERTSignedCrl *) ) {
+ PORT_SetError(SEC_ERROR_BAD_DATABASE);
+ goto loser;
+ }
+
+ PORT_Memcpy(&crl, tmpdata.data, tmpdata.size);
+ crl->referenceCount++;
+ } else { /* not found in temporary database */
+
+ /* find in perm database */
+ entry = ReadDBCrlEntry(handle, crlKey, crlType);
+
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+ crl = SEC_AddPermCrlToTemp(handle, entry);
+ }
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+
+ return(crl);
+}
+
+
+CERTSignedCrl *
+SEC_FindCrlByName(CERTCertDBHandle *handle, SECItem *crlKey, int type)
+{
+ return SEC_FindCrlByKey(handle,crlKey,type);
+}
+
+CERTSignedCrl *
+SEC_FindCrlByDERCert(CERTCertDBHandle *handle, SECItem *derCrl, int type)
+{
+ PRArenaPool *arena;
+ SECItem crlKey;
+ SECStatus rv;
+ CERTSignedCrl *crl = NULL;
+
+ /* create a scratch arena */
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ return(NULL);
+ }
+
+ /* extract the database key from the cert */
+ rv = CERT_KeyFromDERCrl(arena, derCrl, &crlKey);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ /* find the crl */
+ crl = SEC_FindCrlByKey(handle, &crlKey, type);
+
+loser:
+ PORT_FreeArena(arena, PR_FALSE);
+ return(crl);
+}
+
+
+SECStatus
+SEC_DestroyCrl(CERTSignedCrl *crl)
+{
+ if (crl) {
+ if (crl->referenceCount-- <= 1) {
+ if (!crl->keep) {
+ SEC_DeleteTempCrl(crl);
+ if (crl->dbEntry) {
+ DestroyDBEntry((certDBEntry *)crl->dbEntry);
+ }
+ PORT_FreeArena(crl->arena, PR_FALSE);
+ }
+ }
+ }
+ return SECSuccess;
+}
+
+CERTSignedCrl *
+cert_DBInsertCRL (CERTCertDBHandle *handle, char *url,
+ CERTSignedCrl *newCrl, SECItem *derCrl, int type)
+{
+ CERTSignedCrl *oldCrl = NULL, *crl = NULL;
+ certDBEntryRevocation *entry = NULL;
+ PRArenaPool *arena = NULL;
+ SECCertTimeValidity validity;
+ certDBEntryType crlType = (type == SEC_CRL_TYPE) ?
+ certDBEntryTypeRevocation : certDBEntryTypeKeyRevocation;
+ SECStatus rv;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (arena == NULL) goto done;
+
+ validity = SEC_CheckCrlTimes(&newCrl->crl,PR_Now());
+ if ( validity == secCertTimeExpired) {
+ if (type == SEC_CRL_TYPE) {
+ PORT_SetError(SEC_ERROR_CRL_EXPIRED);
+ } else {
+ PORT_SetError(SEC_ERROR_KRL_EXPIRED);
+ }
+ goto done;
+ } else if (validity == secCertTimeNotValidYet) {
+ if (type == SEC_CRL_TYPE) {
+ PORT_SetError(SEC_ERROR_CRL_NOT_YET_VALID);
+ } else {
+ PORT_SetError(SEC_ERROR_KRL_NOT_YET_VALID);
+ }
+ goto done;
+ }
+
+ oldCrl = SEC_FindCrlByKey(handle, &newCrl->crl.derName, type);
+
+ /* if there is an old crl, make sure the one we are installing
+ * is newer. If not, exit out, otherwise delete the old crl.
+ */
+ if (oldCrl != NULL) {
+ if (!SEC_CrlIsNewer(&newCrl->crl,&oldCrl->crl)) {
+
+ if (type == SEC_CRL_TYPE) {
+ PORT_SetError(SEC_ERROR_OLD_CRL);
+ } else {
+ PORT_SetError(SEC_ERROR_OLD_KRL);
+ }
+
+ goto done;
+ }
+
+ if ((SECITEM_CompareItem(&newCrl->crl.derName,
+ &oldCrl->crl.derName) != SECEqual) &&
+ (type == SEC_KRL_TYPE) ) {
+
+ PORT_SetError(SEC_ERROR_CKL_CONFLICT);
+ goto done;
+ }
+
+ /* if we have a url in the database, use that one */
+ if (oldCrl->url) {
+ int nnlen = PORT_Strlen(oldCrl->url) + 1;
+ url = (char *)PORT_ArenaAlloc(arena, nnlen);
+ if ( url != NULL ) {
+ PORT_Memcpy(url, oldCrl->url, nnlen);
+ }
+ }
+
+
+ /* really destroy this crl */
+ /* first drum it out of the permanment Data base */
+ SEC_DeletePermCRL(oldCrl);
+ /* then get rid of our reference to it... */
+ SEC_DestroyCrl(oldCrl);
+ oldCrl = NULL;
+
+ }
+
+ /* Write the new entry into the data base */
+ entry = NewDBCrlEntry(derCrl, url, crlType, 0);
+ if (entry == NULL) goto done;
+
+ rv = WriteDBCrlEntry(handle, entry);
+ if (rv != SECSuccess) goto done;
+
+ crl = SEC_AddPermCrlToTemp(handle, entry);
+ if (crl) entry = NULL; /*crl->dbEntry now points to entry data */
+ crl->isperm = PR_TRUE;
+
+done:
+ if (entry) DestroyDBEntry((certDBEntry *)entry);
+ if (arena) PORT_FreeArena(arena, PR_FALSE);
+ if (oldCrl) SEC_DestroyCrl(oldCrl);
+
+ return crl;
+}
+
+
+/*
+ * create a new CRL from DER material.
+ *
+ * The signature on this CRL must be checked before you
+ * load it. ???
+ */
+CERTSignedCrl *
+SEC_NewCrl(CERTCertDBHandle *handle, char *url, SECItem *derCrl, int type)
+{
+ CERTSignedCrl *newCrl = NULL, *crl = NULL;
+
+ /* make this decode dates! */
+ newCrl = CERT_DecodeDERCrl(NULL, derCrl, type);
+ if (newCrl == NULL) {
+ if (type == SEC_CRL_TYPE) {
+ PORT_SetError(SEC_ERROR_CRL_INVALID);
+ } else {
+ PORT_SetError(SEC_ERROR_KRL_INVALID);
+ }
+ goto done;
+ }
+
+ crl = cert_DBInsertCRL (handle, url, newCrl, derCrl, type);
+
+
+done:
+ if (newCrl) PORT_FreeArena(newCrl->arena, PR_FALSE);
+
+ return crl;
+}
+
+
+/*
+ * replace the existing URL in the data base with a new one
+ */
+SECStatus
+SEC_CrlReplaceUrl(CERTSignedCrl *crl,char *url) {
+ SECStatus rv = SECFailure;
+ certDBEntryRevocation *entry = NULL;
+ int nnlen=0;
+
+ SEC_DeletePermCRL(crl);
+
+ /* Write the new entry into the data base */
+ entry = NewDBCrlEntry(&crl->dbEntry->derCrl, url, crl->dbEntry->common.type, 0);
+ if (entry == NULL) goto done;
+
+ rv = WriteDBCrlEntry(crl->dbhandle, entry);
+ if (rv != SECSuccess) goto done;
+
+ if (url) {
+ nnlen = PORT_Strlen(url) + 1;
+ crl->url = (char *)PORT_ArenaAlloc(crl->arena, nnlen);
+ if ( !crl->url ) {
+ goto done;
+ }
+ PORT_Memcpy(crl->url, url, nnlen);
+ } else {
+ crl->url = NULL;
+ }
+done:
+ return rv;
+}
+
+
+/*
+ * collect a linked list of CRL's
+ */
+static SECStatus CollectCrls(SECItem *dbdata, SECItem *dbkey,
+ certDBEntryType type, void *data) {
+ SECStatus rv;
+ certDBEntryRevocation entry;
+ SECItem entryitem;
+ PRArenaPool *arena = NULL;
+ CERTCrlHeadNode *head;
+ CERTCrlNode *new_node;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ goto loser;
+ }
+
+ head = (CERTCrlHeadNode *)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 = DecodeDBCrlEntry(&entry, &entryitem);
+ if (rv != SECSuccess ) {
+ goto loser;
+ }
+
+ new_node = (CERTCrlNode *)PORT_ArenaAlloc(head->arena, sizeof(CERTCrlNode));
+ if (new_node == NULL) {
+ goto loser;
+ }
+
+ new_node->type = (entry.common.type == certDBEntryTypeRevocation) ?
+ SEC_CRL_TYPE : SEC_KRL_TYPE;
+ new_node->crl=CERT_DecodeDERCrl(head->arena,&entry.derCrl,new_node->type);
+
+ if (entry.url) {
+ int nnlen = PORT_Strlen(entry.url) + 1;
+ new_node->crl->url = (char *)PORT_ArenaAlloc(head->arena, nnlen);
+ if ( !new_node->crl->url ) {
+ goto loser;
+ }
+ PORT_Memcpy(new_node->crl->url, entry.url, nnlen);
+ } else {
+ new_node->crl->url = NULL;
+ }
+
+
+ new_node->next = NULL;
+ if (head->last) {
+ head->last->next = new_node;
+ head->last = new_node;
+ } else {
+ head->first = head->last = new_node;
+ }
+ return (SECSuccess);
+
+loser:
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ }
+ return(SECFailure);
+}
+
+
+SECStatus
+SEC_LookupCrls(CERTCertDBHandle *handle, CERTCrlHeadNode **nodes, int type)
+{
+ CERTCrlHeadNode *head;
+ PRArenaPool *arena = NULL;
+ SECStatus rv;
+
+ *nodes = NULL;
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if ( arena == NULL ) {
+ return SECFailure;
+ }
+
+ /* build a head structure */
+ head = (CERTCrlHeadNode *)PORT_ArenaAlloc(arena, sizeof(CERTCrlHeadNode));
+ head->arena = arena;
+ head->first = NULL;
+ head->last = NULL;
+ head->dbhandle = handle;
+
+ /* Look up the proper crl types */
+ *nodes = head;
+
+ CERT_LockDB(handle);
+
+ switch (type) {
+ case SEC_CRL_TYPE:
+ rv = SEC_TraverseDBEntries(handle, certDBEntryTypeRevocation,
+ CollectCrls, (void *)head);
+ break;
+ case SEC_KRL_TYPE:
+ rv = SEC_TraverseDBEntries(handle, certDBEntryTypeKeyRevocation,
+ CollectCrls, (void *)head);
+ break;
+ case -1:
+ rv = SEC_TraverseDBEntries(handle, certDBEntryTypeKeyRevocation,
+ CollectCrls, (void *)head);
+ if (rv != SECSuccess) break;
+ rv = SEC_TraverseDBEntries(handle, certDBEntryTypeRevocation,
+ CollectCrls, (void *)head);
+ break;
+ default:
+ rv = SECFailure;
+ break;
+ }
+
+ CERT_UnlockDB(handle);
+
+ if (rv != SECSuccess) {
+ if ( arena ) {
+ PORT_FreeArena(arena, PR_FALSE);
+ *nodes = NULL;
+ }
+ }
+
+ return rv;
+}
+
+
+SECStatus
+SEC_DeletePermCRL(CERTSignedCrl *crl) {
+ SECStatus rv;
+
+ if (crl == NULL) {
+ return SECFailure;
+ }
+
+ rv = DeleteDBCrlEntry(crl->dbhandle, &crl->crl.derName,
+ crl->dbEntry->common.type);
+ if (rv != SECSuccess) goto done;
+
+ /* now force it to be freed when all the reference counts go */
+ crl->keep = PR_FALSE;
+ /* force it out of the temporary data base */
+ SEC_DeleteTempCrl(crl);
+
+done:
+ return rv;
+}
+
+/*
+ * find a cert by email address
+ *
+ * pick one that is a valid recipient, meaning that it is an encryption
+ * cert.
+ *
+ */
+static CERTCertificate*
+find_smime_recipient_cert(CERTCertDBHandle* handle, const char* email_addr)
+{
+ CERTCertificate* cert = NULL;
+ CERTCertList* certList = NULL;
+ SECStatus rv;
+
+ certList = CERT_CreateEmailAddrCertList(NULL, handle, (char*)email_addr,
+ PR_Now(), PR_TRUE);
+ if (certList == NULL) {
+ return NULL;
+ }
+
+ rv = CERT_FilterCertListByUsage(certList, certUsageEmailRecipient,
+ PR_FALSE);
+
+ if (!CERT_LIST_END(CERT_LIST_HEAD(certList), certList)) {
+ cert = CERT_DupCertificate(CERT_LIST_HEAD(certList)->cert);
+ }
+
+ CERT_DestroyCertList(certList);
+
+ return cert; /* cert may point to a cert or may be NULL */
+}
+
+/*
+ * 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.
+ */
+SECStatus
+CERT_SaveSMimeProfile(CERTCertificate *cert, SECItem *emailProfile,
+ SECItem *profileTime)
+{
+ certDBEntrySMime *entry = NULL, *oldentry = NULL;
+ int64 oldtime;
+ int64 newtime;
+ SECStatus rv;
+ CERTCertificate *oldcert = NULL;
+ PRBool saveit;
+ CERTCertTrust trust;
+ CERTCertTrust tmptrust;
+ char *emailAddr;
+
+ emailAddr = cert->emailAddr;
+
+ PORT_Assert(emailAddr);
+ if ( emailAddr == NULL ) {
+ goto loser;
+ }
+
+ saveit = PR_FALSE;
+
+ oldcert = find_smime_recipient_cert(cert->dbhandle, emailAddr);
+ if (oldcert) {
+ /* see if there is an entry already */
+ oldentry = ReadDBSMimeEntry(cert->dbhandle, emailAddr);
+ }
+
+ /* both profileTime and emailProfile have to exist or not exist */
+ if ( emailProfile == NULL ) {
+ profileTime = NULL;
+ } else if ( profileTime == NULL ) {
+ emailProfile = NULL;
+ }
+
+ if ( oldentry == NULL ) {
+ /* no old entry for this address */
+ PORT_Assert(oldcert == NULL);
+ saveit = PR_TRUE;
+ } else {
+ /* there was already a profile for this email addr */
+ if ( profileTime ) {
+ /* we have an old and new profile - save whichever is more recent*/
+ if ( oldentry->optionsDate.len == 0 ) {
+ /* always replace if old entry doesn't have a time */
+ oldtime = LL_MININT;
+ } else {
+ rv = DER_UTCTimeToTime(&oldtime, &oldentry->optionsDate);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ }
+
+ rv = DER_UTCTimeToTime(&newtime, profileTime);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+
+ if ( LL_CMP(newtime, >, oldtime ) ) {
+ /* this is a newer profile, save it and cert */
+ saveit = PR_TRUE;
+ }
+ } else {
+ /* we don't have a new profile or time */
+ if ( oldentry->optionsDate.len == 0 ) {
+ /* the old entry doesn't have a time either, so compare certs*/
+ if ( CERT_IsNewer(cert, oldcert) ) {
+ /* new cert is newer, use it instead */
+ saveit = PR_TRUE;
+ }
+ } else {
+ if (oldcert) {
+ if (CERT_IsNewer(cert, oldcert)) {
+ saveit = PR_TRUE;
+ }
+ } else {
+ saveit = PR_TRUE;
+ }
+ }
+ }
+ }
+
+ if ( saveit ) {
+ if ( oldcert && ( oldcert != cert ) ) {
+ /* old cert is different from new cert */
+ if ( PORT_Memcmp(oldcert->trust, &trust, sizeof(trust)) == 0 ) {
+ /* old cert is only for e-mail, so delete it */
+ SEC_DeletePermCertificate(oldcert);
+ } else {
+ /* old cert is for other things too, so just change trust */
+ tmptrust = *oldcert->trust;
+ tmptrust.emailFlags &= ( ~CERTDB_VALID_PEER );
+ rv = CERT_ChangeCertTrust(oldcert->dbhandle, oldcert,
+ &tmptrust);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ }
+ }
+
+/* Subroutine */
+ /* now save the entry */
+ entry = NewDBSMimeEntry(emailAddr, &cert->derSubject, emailProfile,
+ profileTime, 0);
+ if ( entry == NULL ) {
+ goto loser;
+ }
+
+ CERT_LockDB(cert->dbhandle);
+
+ rv = DeleteDBSMimeEntry(cert->dbhandle, emailAddr);
+ /* if delete fails, try to write new entry anyway... */
+
+ rv = WriteDBSMimeEntry(cert->dbhandle, entry);
+ if ( rv != SECSuccess ) {
+ CERT_UnlockDB(cert->dbhandle);
+ goto loser;
+ }
+
+ /* link subject entry back here */
+ rv = UpdateSubjectWithEmailAddr(cert, emailAddr);
+ if ( rv != SECSuccess ) {
+ CERT_UnlockDB(cert->dbhandle);
+ goto loser;
+ }
+
+ CERT_UnlockDB(cert->dbhandle);
+/* End Subroutine */
+ }
+
+ rv = SECSuccess;
+ goto done;
+
+loser:
+ rv = SECFailure;
+
+done:
+ if ( oldcert ) {
+ CERT_DestroyCertificate(oldcert);
+ }
+
+ if ( entry ) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+ if ( oldentry ) {
+ DestroyDBEntry((certDBEntry *)oldentry);
+ }
+
+ return(rv);
+}
+
+CERTCertificate *
+CERT_FindCertByEmailAddr(CERTCertDBHandle *handle, char *emailAddr)
+{
+ certDBEntrySMime *entry;
+ CERTCertificate *cert = NULL;
+
+ emailAddr = CERT_FixupEmailAddr(emailAddr);
+ if ( emailAddr == NULL ) {
+ return(NULL);
+ }
+
+ entry = ReadDBSMimeEntry(handle, emailAddr);
+
+ /* XXX - this will have to change when multiple certs per subject
+ * are allowed
+ */
+ if ( entry != NULL ) {
+ cert = CERT_FindCertByName(handle, &entry->subjectName);
+ }
+
+ if ( entry ) {
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+
+ PORT_Free(emailAddr);
+
+ return(cert);
+}
+
+/*
+ * find the smime symmetric capabilities profile for a given cert
+ */
+SECItem *
+CERT_FindSMimeProfile(CERTCertificate *cert)
+{
+ certDBEntrySMime *entry;
+ SECItem *retitem = NULL;
+
+ PORT_Assert(cert->emailAddr != NULL);
+
+ if ( cert->emailAddr == NULL ) {
+ return(NULL);
+ }
+
+ entry = ReadDBSMimeEntry(cert->dbhandle, cert->emailAddr);
+
+ if ( entry ) {
+ retitem = SECITEM_DupItem(&entry->smimeOptions);
+ DestroyDBEntry((certDBEntry *)entry);
+ }
+
+ return(retitem);
+}
+
+CERTCertificate *
+CERT_FindCertByNicknameOrEmailAddr(CERTCertDBHandle *handle, char *name)
+{
+ CERTCertificate *cert;
+ cert = CERT_FindCertByNickname(handle, name);
+ if ( cert == NULL ) {
+ cert = CERT_FindCertByEmailAddr(handle, name);
+ }
+
+ return(cert);
+}
+
+PRBool
+CERT_IsCertRevoked(CERTCertificate *cert)
+{
+ return(PR_FALSE);
+}
+
+CERTCertificate *
+CERT_NextSubjectCert(CERTCertificate *cert)
+{
+ CERTSubjectNode *node;
+ CERTCertificate *retcert = NULL;
+
+ CERT_LockDB(cert->dbhandle);
+
+ node = FindCertSubjectNode(cert);
+ PORT_Assert(node != NULL);
+
+ if ( node->next != NULL ) {
+ retcert = CERT_FindCertByKeyNoLocking(cert->dbhandle,
+ &node->next->certKey);
+ }
+
+ CERT_UnlockDB(cert->dbhandle);
+
+ return(retcert);
+}
+
+CERTCertificate *
+CERT_PrevSubjectCert(CERTCertificate *cert)
+{
+ CERTSubjectNode *node;
+ CERTCertificate *retcert = NULL;
+
+ CERT_LockDB(cert->dbhandle);
+ node = FindCertSubjectNode(cert);
+ PORT_Assert(node != NULL);
+
+ if ( node->prev != NULL ) {
+ retcert = CERT_FindCertByKeyNoLocking(cert->dbhandle,
+ &node->prev->certKey);
+ }
+ CERT_UnlockDB(cert->dbhandle);
+
+ return(retcert);
+}
+
+SECStatus
+CERT_SaveImportedCert(CERTCertificate *cert, SECCertUsage usage,
+ PRBool caOnly, char *nickname)
+{
+ SECStatus rv;
+ PRBool saveit;
+ CERTCertTrust trust;
+ CERTCertTrust tmptrust;
+ PRBool isCA;
+ unsigned int certtype;
+ PRBool freeNickname = PR_FALSE;
+
+ isCA = CERT_IsCACert(cert, NULL);
+ if ( caOnly && ( !isCA ) ) {
+ return(SECSuccess);
+ }
+
+ saveit = PR_TRUE;
+
+ PORT_Memset((void *)&trust, 0, sizeof(trust));
+
+ certtype = cert->nsCertType;
+
+ /* if no CA bits in cert type, then set all CA bits */
+ if ( isCA && ( ! ( certtype & NS_CERT_TYPE_CA ) ) ) {
+ certtype |= NS_CERT_TYPE_CA;
+ }
+
+ /* if no app bits in cert type, then set all app bits */
+ if ( ( !isCA ) && ( ! ( certtype & NS_CERT_TYPE_APP ) ) ) {
+ certtype |= NS_CERT_TYPE_APP;
+ }
+
+ if ( isCA && !nickname ) {
+ nickname = CERT_MakeCANickname(cert);
+ if ( nickname != NULL ) {
+ freeNickname = PR_TRUE;
+ }
+ }
+
+ switch ( usage ) {
+ case certUsageEmailSigner:
+ case certUsageEmailRecipient:
+ if ( isCA ) {
+ if ( certtype & NS_CERT_TYPE_EMAIL_CA ) {
+ trust.emailFlags = CERTDB_VALID_CA;
+ }
+ } else {
+ PORT_Assert(nickname == NULL);
+
+ if ( cert->emailAddr == NULL ) {
+ saveit = PR_FALSE;
+ }
+
+ if ( certtype & NS_CERT_TYPE_EMAIL ) {
+ trust.emailFlags = CERTDB_VALID_PEER;
+ if ( ! ( cert->rawKeyUsage & KU_KEY_ENCIPHERMENT ) ) {
+ /* don't save it if KeyEncipherment is not allowed */
+ saveit = PR_FALSE;
+ }
+ }
+ }
+ break;
+ case certUsageUserCertImport:
+ if ( isCA ) {
+ if ( certtype & NS_CERT_TYPE_SSL_CA ) {
+ trust.sslFlags = CERTDB_VALID_CA;
+ }
+
+ if ( certtype & NS_CERT_TYPE_EMAIL_CA ) {
+ trust.emailFlags = CERTDB_VALID_CA;
+ }
+
+ if ( certtype & NS_CERT_TYPE_OBJECT_SIGNING_CA ) {
+ trust.objectSigningFlags = CERTDB_VALID_CA;
+ }
+
+ } else {
+ if ( certtype & NS_CERT_TYPE_SSL_CLIENT ) {
+ trust.sslFlags = CERTDB_VALID_PEER;
+ }
+
+ if ( certtype & NS_CERT_TYPE_EMAIL ) {
+ trust.emailFlags = CERTDB_VALID_PEER;
+ }
+
+ if ( certtype & NS_CERT_TYPE_OBJECT_SIGNING ) {
+ trust.objectSigningFlags = CERTDB_VALID_PEER;
+ }
+ }
+ break;
+ default: /* XXX added to quiet warnings; no other cases needed? */
+ break;
+ }
+
+ if ( (trust.sslFlags | trust.emailFlags | trust.objectSigningFlags) == 0 ){
+ saveit = PR_FALSE;
+ }
+
+ if ( saveit ) {
+ if ( cert->isperm ) {
+ /* Cert already in the DB. Just adjust flags */
+ tmptrust = *cert->trust;
+ tmptrust.sslFlags |= trust.sslFlags;
+ tmptrust.emailFlags |= trust.emailFlags;
+ tmptrust.objectSigningFlags |= trust.objectSigningFlags;
+
+ rv = CERT_ChangeCertTrust(cert->dbhandle, cert,
+ &tmptrust);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ } else {
+ /* Cert not already in the DB. Add it */
+ rv = CERT_AddTempCertToPerm(cert, nickname, &trust);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ }
+ }
+
+ rv = SECSuccess;
+ goto done;
+
+loser:
+ rv = SECFailure;
+done:
+
+ if ( freeNickname ) {
+ PORT_Free(nickname);
+ }
+
+ return(rv);
+}
+
+SECStatus
+CERT_ChangeCertTrustByUsage(CERTCertDBHandle *certdb,
+ CERTCertificate *cert, SECCertUsage usage)
+{
+ SECStatus rv;
+ CERTCertTrust trust;
+ CERTCertTrust tmptrust;
+ unsigned int certtype;
+ PRBool saveit;
+
+ saveit = PR_TRUE;
+
+ PORT_Memset((void *)&trust, 0, sizeof(trust));
+
+ certtype = cert->nsCertType;
+
+ /* if no app bits in cert type, then set all app bits */
+ if ( ! ( certtype & NS_CERT_TYPE_APP ) ) {
+ certtype |= NS_CERT_TYPE_APP;
+ }
+
+ switch ( usage ) {
+ case certUsageEmailSigner:
+ case certUsageEmailRecipient:
+ if ( certtype & NS_CERT_TYPE_EMAIL ) {
+ trust.emailFlags = CERTDB_VALID_PEER;
+ if ( ! ( cert->rawKeyUsage & KU_KEY_ENCIPHERMENT ) ) {
+ /* don't save it if KeyEncipherment is not allowed */
+ saveit = PR_FALSE;
+ }
+ }
+ break;
+ case certUsageUserCertImport:
+ if ( certtype & NS_CERT_TYPE_EMAIL ) {
+ trust.emailFlags = CERTDB_VALID_PEER;
+ }
+ /* VALID_USER is already set if the cert was imported,
+ * in the case that the cert was already in the database
+ * through SMIME or other means, we should set the USER
+ * flags, if they are not already set.
+ */
+ if( cert->isperm ) {
+ if ( certtype & NS_CERT_TYPE_SSL_CLIENT ) {
+ if( !(cert->trust->sslFlags & CERTDB_USER) ) {
+ trust.sslFlags |= CERTDB_USER;
+ }
+ }
+
+ if ( certtype & NS_CERT_TYPE_EMAIL ) {
+ if( !(cert->trust->emailFlags & CERTDB_USER) ) {
+ trust.emailFlags |= CERTDB_USER;
+ }
+ }
+
+ if ( certtype & NS_CERT_TYPE_OBJECT_SIGNING ) {
+ if( !(cert->trust->objectSigningFlags & CERTDB_USER) ) {
+ trust.objectSigningFlags |= CERTDB_USER;
+ }
+ }
+ }
+ break;
+ default: /* XXX added to quiet warnings; no other cases needed? */
+ break;
+ }
+
+ if ( (trust.sslFlags | trust.emailFlags | trust.objectSigningFlags) == 0 ){
+ saveit = PR_FALSE;
+ }
+
+ if ( saveit && cert->isperm ) {
+ /* Cert already in the DB. Just adjust flags */
+ tmptrust = *cert->trust;
+ tmptrust.sslFlags |= trust.sslFlags;
+ tmptrust.emailFlags |= trust.emailFlags;
+ tmptrust.objectSigningFlags |= trust.objectSigningFlags;
+
+ rv = CERT_ChangeCertTrust(cert->dbhandle, cert,
+ &tmptrust);
+ if ( rv != SECSuccess ) {
+ goto loser;
+ }
+ }
+
+ rv = SECSuccess;
+ goto done;
+
+loser:
+ rv = SECFailure;
+done:
+
+ return(rv);
+}
+
+int
+CERT_GetDBContentVersion(CERTCertDBHandle *handle)
+{
+ certDBEntryContentVersion *entry;
+ int ret;
+
+ entry = ReadDBContentVersionEntry(handle);
+
+ if ( entry == NULL ) {
+ return(0);
+ }
+
+ ret = entry->contentVersion;
+
+ DestroyDBEntry((certDBEntry *)entry);
+
+ return(ret);
+}
+
+void
+CERT_SetDBContentVersion(int version, CERTCertDBHandle *handle)
+{
+ SECStatus rv;
+ certDBEntryContentVersion *entry;
+
+ entry = NewDBContentVersionEntry(0);
+
+ if ( entry == NULL ) {
+ return;
+ }
+
+ rv = DeleteDBContentVersionEntry(handle);
+ rv = WriteDBContentVersionEntry(handle, entry);
+
+ DestroyDBEntry((certDBEntry *)entry);
+
+ return;
+}
+
+/*
+ * Creates or adds to a list of all certs with a give subject name, sorted by
+ * validity time, newest first. Invalid certs are considered older than
+ * valid certs. If validOnly is set, do not include invalid certs on list.
+ */
+CERTCertList *
+CERT_CreateSubjectCertList(CERTCertList *certList, CERTCertDBHandle *handle,
+ SECItem *name, int64 sorttime, PRBool validOnly)
+{
+ CERTCertificate *cert = NULL;
+ CERTSubjectList *subjectList = NULL;
+ CERTSubjectNode *node;
+ SECStatus rv;
+
+ if ( certList == NULL ) {
+ certList = CERT_NewCertList();
+ }
+
+ if ( certList == NULL ) {
+ goto loser;
+ }
+
+ subjectList = FindSubjectList(handle, name, PR_FALSE);
+
+ if ( subjectList != NULL ) {
+ node = subjectList->head;
+ PORT_Assert(node);
+ while (node) {
+ cert = CERT_FindCertByKey(handle, &node->certKey);
+
+ /* if validOnly, then check validity period before adding to list*/
+ if ( ( !validOnly ) ||
+ ( CERT_CheckCertValidTimes(cert, sorttime, PR_FALSE)
+ == secCertTimeValid ) ) {
+ rv = CERT_AddCertToListSorted(certList, cert,
+ CERT_SortCBValidity,
+ (void *)&sorttime);
+ if ( rv != SECSuccess ) {
+ CERT_DestroyCertificate(cert);
+ goto loser;
+ }
+ } else {
+ CERT_DestroyCertificate(cert);
+ }
+
+ node = node->next;
+ }
+ }
+
+ return(certList);
+
+loser:
+ if ( certList != NULL ) {
+ CERT_DestroyCertList(certList);
+ }
+
+ return(NULL);
+}
+
+/*
+ * Creates or adds to a list of all certs with a give nickname, sorted by
+ * validity time, newest first. Invalid certs are considered older than valid
+ * certs. If validOnly is set, do not include invalid certs on list.
+ */
+CERTCertList *
+CERT_CreateNicknameCertList(CERTCertList *certList, CERTCertDBHandle *handle,
+ char *nickname, int64 sorttime, PRBool validOnly)
+{
+ CERTCertificate *cert;
+ CERTCertList *ret;
+
+ cert = CERT_FindCertByNickname(handle, nickname);
+ if ( cert == NULL ) {
+ return(NULL);
+ }
+
+ ret = CERT_CreateSubjectCertList(certList, handle, &cert->derSubject,
+ sorttime, validOnly);
+
+ CERT_DestroyCertificate(cert);
+
+ return(ret);
+}
+
+/*
+ * Creates or adds to a list of all certs with a give email addr, sorted by
+ * validity time, newest first. Invalid certs are considered older than valid
+ * certs. If validOnly is set, do not include invalid certs on list.
+ */
+CERTCertList *
+CERT_CreateEmailAddrCertList(CERTCertList *certList, CERTCertDBHandle *handle,
+ char *emailAddr, int64 sorttime, PRBool validOnly)
+{
+ CERTCertificate *cert;
+ CERTCertList *ret;
+
+ cert = CERT_FindCertByEmailAddr(handle, emailAddr);
+ if ( cert == NULL ) {
+ return(NULL);
+ }
+
+ ret = CERT_CreateSubjectCertList(certList, handle, &cert->derSubject,
+ sorttime, validOnly);
+
+ CERT_DestroyCertificate(cert);
+
+ return(ret);
+}