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