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