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