/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* * the following functions are wrappers for the db library that implement * a global lock to make the database thread safe.
*/ static PZLock *dbLock = NULL; static PZLock *certRefCountLock = NULL; static PZLock *certTrustLock = NULL; static PZLock *freeListLock = NULL;
SECStatus
nsslowcert_InitLocks(void)
{ if (freeListLock == NULL) {
freeListLock = PZ_NewLock(nssILockRefLock); if (freeListLock == NULL) { return SECFailure;
}
} if (certRefCountLock == NULL) {
certRefCountLock = PZ_NewLock(nssILockRefLock); if (certRefCountLock == NULL) { return SECFailure;
}
} if (certTrustLock == NULL) {
certTrustLock = PZ_NewLock(nssILockCertDB); if (certTrustLock == NULL) { return SECFailure;
}
}
return SECSuccess;
}
/* * Acquire the global lock on the cert database. * This lock is currently used for the following operations: * adding or deleting a cert to either the temp or perm databases * converting a temp to perm or perm to temp * changing (maybe just adding!?) the trust of a cert * chaning the DB status checking Configuration
*/ staticvoid
nsslowcert_LockDB(NSSLOWCERTCertDBHandle *handle)
{
PZ_EnterMonitor(handle->dbMon); return;
}
/* * Acquire the cert reference count lock * There is currently one global lock for all certs, but I'm putting a cert * arg here so that it will be easy to make it per-cert in the future if * that turns out to be necessary.
*/ staticvoid
nsslowcert_LockCertRefCount(NSSLOWCERTCertificate *cert)
{
PORT_Assert(certRefCountLock != NULL);
/* * Acquire the cert trust lock * There is currently one global lock for all certs, but I'm putting a cert * arg here so that it will be easy to make it per-cert in the future if * that turns out to be necessary.
*/ staticvoid
nsslowcert_LockCertTrust(NSSLOWCERTCertificate *cert)
{
PORT_Assert(certTrustLock != NULL);
/* * Acquire the cert reference count lock * There is currently one global lock for all certs, but I'm putting a cert * arg here so that it will be easy to make it per-cert in the future if * that turns out to be necessary.
*/ staticvoid
nsslowcert_LockFreeList(void)
{
PORT_Assert(freeListLock != NULL);
/* must be one of our certDBEntry from the free list */ if (arena == NULL) {
certDBEntryCert *certEntry; if (entry->common.type != certDBEntryTypeCert) { return;
}
certEntry = (certDBEntryCert *)entry;
/* Zero out the entry struct, so that any further attempts to use it
* will cause an exception (e.g. null pointer reference). */
PORT_Memset(&entry->common, 0, sizeof entry->common);
PORT_FreeArena(arena, PR_FALSE);
/* read entry from database */
ret = certdb_Get(handle->permCertDB, &key, &data, 0); if (ret != 0) {
PORT_SetError(SEC_ERROR_BAD_DATABASE); goto loser;
}
/* validate the entry */ if (data.size < SEC_DB_ENTRY_HEADER_LEN) {
PORT_SetError(SEC_ERROR_BAD_DATABASE); goto loser;
}
buf = (unsignedchar *)data.data; /* version 7 has the same schema, we may be using a v7 db if we openned
* the databases readonly. */ if (!((buf[0] == (unsignedchar)CERT_DB_FILE_VERSION) ||
(buf[0] == (unsignedchar)CERT_DB_V7_FILE_VERSION))) {
PORT_SetError(SEC_ERROR_BAD_DATABASE); goto loser;
} if (buf[1] != (unsignedchar)entry->type) {
PORT_SetError(SEC_ERROR_BAD_DATABASE); goto loser;
}
/* copy out header information */
entry->version = (unsignedint)buf[0];
entry->type = (certDBEntryType)buf[1];
entry->flags = (unsignedint)buf[2];
/* format body of entry for return to caller */
dbentry->len = data.size - SEC_DB_ENTRY_HEADER_LEN; if (dbentry->len) { if (arena) {
dbentry->data = (unsignedchar *)
PORT_ArenaAlloc(arena, dbentry->len); if (dbentry->data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser;
}
if (entry->nickname) {
nn = entry->nickname;
} else {
nn = &zbuf;
}
nnlen = PORT_Strlen(nn) + 1;
/* allocate space for encoded database record, including space * for low level header
*/
dbitem->len = entry->derCert.len + nnlen + DB_CERT_ENTRY_HEADER_LEN +
SEC_DB_ENTRY_HEADER_LEN;
/* allow updates of old versions of the database */ switch (entry->common.version) { case 5:
headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
lenoff = 3; break; case 6: /* should not get here */
PORT_Assert(0);
headerlen = DB_CERT_V6_ENTRY_HEADER_LEN;
lenoff = 3; break; case 7: case 8:
headerlen = DB_CERT_ENTRY_HEADER_LEN;
lenoff = 6; break; default: /* better not get here */
PORT_Assert(0);
headerlen = DB_CERT_V5_ENTRY_HEADER_LEN;
lenoff = 3; break;
}
/* is record long enough for header? */ if (dbentry->len < headerlen) {
PORT_SetError(SEC_ERROR_BAD_DATABASE); goto loser;
}
/* * Create a new certDBEntryCert from existing data
*/ static certDBEntryCert *
NewDBCertEntry(SECItem *derCert, char *nickname,
NSSLOWCERTCertTrust *trust, int flags)
{
certDBEntryCert *entry;
PLArenaPool *arena = NULL; int nnlen;
if (nnlen) {
entry->nickname = (char *)PORT_ArenaAlloc(arena, nnlen); if (!entry->nickname) { goto loser;
}
PORT_Memcpy(entry->nickname, nickname, nnlen);
} else {
entry->nickname = 0;
}
return (entry);
loser:
/* allocation error, free arena and return */ if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
PORT_SetError(SEC_ERROR_NO_MEMORY); return (0);
}
/* * Decode a version 4 DBCert from the byte stream database format * and construct a current database entry struct
*/ static certDBEntryCert *
DecodeV4DBCertEntry(unsignedchar *buf, int len)
{
certDBEntryCert *entry; int certlen; int nnlen;
PLArenaPool *arena;
/* make sure length is at least long enough for the header */ if (len < DBCERT_V4_HEADER_LEN) {
PORT_SetError(SEC_ERROR_BAD_DATABASE); return (0);
}
/* get other lengths */
certlen = buf[3] << 8 | buf[4];
nnlen = buf[5] << 8 | buf[6];
/* make sure DB entry is the right size */ if ((certlen + nnlen + DBCERT_V4_HEADER_LEN) != len) {
PORT_SetError(SEC_ERROR_BAD_DATABASE); return (0);
}
if (entry->url) {
nnlen = PORT_Strlen(entry->url) + 1;
}
/* allocate space for encoded database record, including space * for low level header
*/
dbitem->len = entry->derCrl.len + nnlen + SEC_DB_ENTRY_HEADER_LEN + DB_CRL_ENTRY_HEADER_LEN;
/* * Create a new certDBEntryRevocation from existing data
*/ static certDBEntryRevocation *
NewDBCrlEntry(SECItem *derCrl, char *url, certDBEntryType crlType, int flags)
{
certDBEntryRevocation *entry;
PLArenaPool *arena = NULL; int nnlen;
/* * Encode a database nickname record
*/ static SECStatus
EncodeDBNicknameEntry(certDBEntryNickname *entry, PLArenaPool *arena,
SECItem *dbitem)
{ unsignedchar *buf;
/* allocate space for encoded database record, including space * for low level header
*/
dbitem->len = entry->subjectName.len + DB_NICKNAME_ENTRY_HEADER_LEN +
SEC_DB_ENTRY_HEADER_LEN;
dbitem->data = (unsignedchar *)PORT_ArenaAlloc(arena, dbitem->len); if (dbitem->data == NULL) { goto loser;
}
/* fill in database record */
buf = &dbitem->data[SEC_DB_ENTRY_HEADER_LEN];
buf[0] = (PRUint8)(entry->subjectName.len >> 8);
buf[1] = (PRUint8)(entry->subjectName.len);
PORT_Memcpy(&buf[DB_NICKNAME_ENTRY_HEADER_LEN], entry->subjectName.data,
entry->subjectName.len);
return (SECSuccess);
loser: return (SECFailure);
}
/* * Encode a database key for a nickname record
*/ static SECStatus
EncodeDBNicknameKey(char *nickname, PLArenaPool *arena,
SECItem *dbkey)
{ unsignedint nnlen;
nnlen = PORT_Strlen(nickname) + 1; /* includes null */
/* now get the database key and format it */
dbkey->len = nnlen + SEC_DB_KEY_HEADER_LEN; if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE) goto loser;
dbkey->data = (unsignedchar *)PORT_ArenaAlloc(arena, dbkey->len); if (dbkey->data == NULL) { goto loser;
}
PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], nickname, nnlen);
dbkey->data[0] = certDBEntryTypeNickname;
/* if no smime options, then there should not be an options date either */
PORT_Assert(!((entry->smimeOptions.len == 0) &&
(entry->optionsDate.len != 0)));
/* * Encode a database key for a SMIME record
*/ static SECStatus
EncodeDBSMimeKey(char *emailAddr, PLArenaPool *arena,
SECItem *dbkey)
{ unsignedint addrlen;
addrlen = PORT_Strlen(emailAddr) + 1; /* includes null */
/* now get the database key and format it */
dbkey->len = addrlen + SEC_DB_KEY_HEADER_LEN; if (dbkey->len > NSS_MAX_LEGACY_DB_KEY_SIZE) goto loser;
dbkey->data = (unsignedchar *)PORT_ArenaAlloc(arena, dbkey->len); if (dbkey->data == NULL) { goto loser;
}
PORT_Memcpy(&dbkey->data[SEC_DB_KEY_HEADER_LEN], emailAddr, addrlen);
dbkey->data[0] = certDBEntryTypeSMimeProfile;
return (SECSuccess);
loser: return (SECFailure);
}
/* * Decode a database SMIME record
*/ static SECStatus
DecodeDBSMimeEntry(certDBEntrySMime *entry, SECItem *dbentry, char *emailAddr)
{ int lenDiff;
/* is record long enough for header? */ if (dbentry->len < DB_SMIME_ENTRY_HEADER_LEN) {
PORT_SetError(SEC_ERROR_BAD_DATABASE); goto loser;
}
/* both options and options date must either exist or not exist */ if (((entry->optionsDate.len == 0) ||
(entry->smimeOptions.len == 0)) &&
entry->smimeOptions.len != entry->optionsDate.len) {
PORT_SetError(SEC_ERROR_BAD_DATABASE); goto loser;
}
/* if we have an old style email entry, there is only one */
entry->nemailAddrs = 0; if (eaddrlen > 1) { /* null terminator is stored */
entry->emailAddrs = PORT_ArenaNewArray(arena, char *, 2); if (entry->emailAddrs == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser;
}
entry->emailAddrs[0] = (char *)PORT_ArenaAlloc(arena, eaddrlen); if (entry->emailAddrs[0] == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser;
}
PORT_Memcpy(entry->emailAddrs[0],
&dbentry->data[DB_SUBJECT_ENTRY_HEADER_LEN + nnlen],
eaddrlen);
entry->nemailAddrs = 1;
} else {
entry->emailAddrs = NULL;
}
/* collect the lengths of the certKeys and keyIDs, and total the * overall length.
*/
tmpbuf = &dbentry->data[keyidoff]; for (i = 0; i < ncerts; i++) { unsignedint itemlen = (tmpbuf[0] << 8) | tmpbuf[1];
entry->certKeys[i].len = itemlen;
len += itemlen;
tmpbuf += 2;
} for (i = 0; i < ncerts; i++) { unsignedint itemlen = (tmpbuf[0] << 8) | tmpbuf[1];
entry->keyIDs[i].len = itemlen;
len += itemlen;
tmpbuf += 2;
}
/* is encoded entry large enough ? */ if (len > dbentry->len) {
PORT_SetError(SEC_ERROR_BAD_DATABASE); goto loser;
}
for (i = 0; i < ncerts; i++) { unsignedint kLen = entry->certKeys[i].len;
entry->certKeys[i].data = (unsignedchar *)PORT_ArenaAlloc(arena, kLen); if (entry->certKeys[i].data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser;
}
PORT_Memcpy(entry->certKeys[i].data, tmpbuf, kLen);
tmpbuf += kLen;
} for (i = 0; i < ncerts; i++) { unsignedint iLen = entry->keyIDs[i].len;
entry->keyIDs[i].data = (unsignedchar *)PORT_ArenaAlloc(arena, iLen); if (entry->keyIDs[i].data == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser;
}
PORT_Memcpy(entry->keyIDs[i].data, tmpbuf, iLen);
tmpbuf += iLen;
}
end = dbentry->data + dbentry->len; if ((eaddrlen == 0) && (end - tmpbuf > 1)) { /* read in the additional email addresses */
entry->nemailAddrs = (((unsignedint)tmpbuf[0]) << 8) | tmpbuf[1];
tmpbuf += 2; if (end - tmpbuf < 2 * (int)entry->nemailAddrs) goto loser;
entry->emailAddrs = PORT_ArenaNewArray(arena, char *, entry->nemailAddrs); if (entry->emailAddrs == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser;
} for (i = 0; i < entry->nemailAddrs; i++) { int nameLen; if (end - tmpbuf < 2) { goto loser;
}
nameLen = (((int)tmpbuf[0]) << 8) | tmpbuf[1];
tmpbuf += 2; if (end - tmpbuf < nameLen) { goto loser;
}
entry->emailAddrs[i] = PORT_ArenaAlloc(arena, nameLen); if (entry->emailAddrs == NULL) {
PORT_SetError(SEC_ERROR_NO_MEMORY); goto loser;
}
PORT_Memcpy(entry->emailAddrs[i], tmpbuf, nameLen);
tmpbuf += nameLen;
} if (tmpbuf != end) goto loser;
}
PORT_ArenaUnmark(arena, mark); return (SECSuccess);
/* delete the subject entry */
DeleteDBSubjectEntry(dbhandle, derSubject);
/* write the new one */
rv = WriteDBSubjectEntry(dbhandle, entry);
done: if (entry)
DestroyDBEntry((certDBEntry *)entry); if (emailAddr)
PORT_Free(emailAddr); return rv;
}
/* * writes a nickname to an existing subject entry that does not currently * have one
*/ static SECStatus
AddNicknameToSubject(NSSLOWCERTCertDBHandle *dbhandle,
NSSLOWCERTCertificate *cert, char *nickname)
{
certDBEntrySubject *entry;
SECStatus rv;
/* now get the database key and format it */
dbkey.len = SEC_DB_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
dbkey.data = (unsignedchar *)PORT_ArenaAlloc(tmparena, dbkey.len); if (dbkey.data == NULL) { goto loser;
}
PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_VERSION_KEY,
SEC_DB_VERSION_KEY_LEN);
/* now get the database key and format it */
dbkey.len = SEC_DB_VERSION_KEY_LEN + SEC_DB_KEY_HEADER_LEN;
dbkey.data = (unsignedchar *)PORT_ArenaAlloc(tmparena, dbkey.len); if (dbkey.data == NULL) { goto loser;
}
PORT_Memcpy(&dbkey.data[SEC_DB_KEY_HEADER_LEN], SEC_DB_VERSION_KEY,
SEC_DB_VERSION_KEY_LEN);
/* now write it to the database */
rv = WriteDBEntry(handle, &entry->common, &dbkey, &dbitem); if (rv != SECSuccess) { goto loser;
}
loser: if (tmparena) {
PORT_FreeArena(tmparena, PR_FALSE);
} return (SECFailure);
}
/* * cert is no longer a perm cert, but will remain a temp cert
*/ static SECStatus
RemovePermSubjectNode(NSSLOWCERTCertificate *cert)
{
certDBEntrySubject *entry; unsignedint i;
SECStatus rv;
if (entry->ncerts > 1) { for (i = 0; i < entry->ncerts; i++) { if (SECITEM_CompareItem(&entry->certKeys[i], &cert->certKey) ==
SECEqual) { /* copy rest of list forward one entry */ for (i = i + 1; i < entry->ncerts; i++) {
entry->certKeys[i - 1] = entry->certKeys[i];
entry->keyIDs[i - 1] = entry->keyIDs[i];
}
entry->ncerts--;
DeleteDBSubjectEntry(cert->dbhandle, &cert->derSubject);
rv = WriteDBSubjectEntry(cert->dbhandle, entry); break;
}
}
} else { /* no entries left, delete the perm entry in the DB */ if (entry->emailAddrs) { /* if the subject had an email record, then delete it too */ for (i = 0; i < entry->nemailAddrs; i++) {
DeleteDBSMimeEntry(cert->dbhandle, entry->emailAddrs[i]);
}
} if (entry->nickname) {
DeleteDBNicknameEntry(cert->dbhandle, entry->nickname);
}
/* Step 1: copy certs older than "cert" into new entry. */ for (i = 0, new_i = 0; i < ncerts; i++) {
NSSLOWCERTCertificate *cmpcert;
PRBool isNewer;
cmpcert = nsslowcert_FindCertByKey(cert->dbhandle,
&entry->certKeys[i]); /* The entry has been corrupted, remove it from the list */ if (!cmpcert) { continue;
}
if (nnentry) {
DestroyDBEntry((certDBEntry *)nnentry);
} if (smentry) {
DestroyDBEntry((certDBEntry *)smentry);
}
return (rv);
}
int
nsslowcert_NumPermCertsForNickname(NSSLOWCERTCertDBHandle *handle, char *nickname)
{
certDBEntryNickname *entry; int ret;
entry = ReadDBNicknameEntry(handle, nickname);
if (entry) {
ret = nsslowcert_NumPermCertsForSubject(handle, &entry->subjectName);
DestroyDBEntry((certDBEntry *)entry);
} else {
ret = 0;
} return (ret);
}
/* * add a nickname to a cert that doesn't have one
*/ static SECStatus
AddNicknameToPermCert(NSSLOWCERTCertDBHandle *dbhandle,
NSSLOWCERTCertificate *cert, char *nickname)
{
certDBEntryCert *entry; int rv;
/* * add a nickname to a cert that is already in the perm database, but doesn't * have one yet (it is probably an e-mail cert).
*/
SECStatus
nsslowcert_AddPermNickname(NSSLOWCERTCertDBHandle *dbhandle,
NSSLOWCERTCertificate *cert, char *nickname)
{
SECStatus rv = SECFailure;
certDBEntrySubject *entry = NULL;
certDBEntryNickname *nicknameEntry = NULL;
nsslowcert_LockDB(dbhandle);
entry = ReadDBSubjectEntry(dbhandle, &cert->derSubject); if (entry == NULL) goto loser;
if (entry->nickname == NULL) {
/* no nickname for subject */
rv = AddNicknameToSubject(dbhandle, cert, nickname); if (rv != SECSuccess) { goto loser;
}
rv = AddNicknameToPermCert(dbhandle, cert, nickname); if (rv != SECSuccess) { goto loser;
}
nicknameEntry = NewDBNicknameEntry(nickname, &cert->derSubject, 0); if (nicknameEntry == NULL) { goto loser;
}
rv = WriteDBNicknameEntry(dbhandle, nicknameEntry); if (rv != SECSuccess) { goto loser;
}
} else { /* subject already has a nickname */
rv = AddNicknameToPermCert(dbhandle, cert, entry->nickname); if (rv != SECSuccess) { goto loser;
} /* make sure nickname entry exists. If the database was corrupted,
* we may have lost the nickname entry. Add it back now */
nicknameEntry = ReadDBNicknameEntry(dbhandle, entry->nickname); if (nicknameEntry == NULL) {
nicknameEntry = NewDBNicknameEntry(entry->nickname,
&cert->derSubject, 0); if (nicknameEntry == NULL) { goto loser;
}
if (donnentry) {
nicknameEntry = NewDBNicknameEntry(nickname, &cert->derSubject, 0); if (nicknameEntry == NULL) { goto loser;
}
}
rv = WriteDBCertEntry(handle, certEntry); if (rv != SECSuccess) { goto loser;
}
state = 1;
if (nicknameEntry) {
rv = WriteDBNicknameEntry(handle, nicknameEntry); if (rv != SECSuccess) { goto loser;
}
}
state = 2;
/* "Change" handles if necessary */
cert->dbhandle = handle;
/* add to or create new subject entry */ if (subjectEntry) { /* REWRITE BASED ON SUBJECT ENTRY */
rv = AddPermSubjectNode(subjectEntry, cert, nickname); if (rv != SECSuccess) { goto loser;
}
} else { /* make a new subject entry - this case is only used when updating * an old version of the database. This is OK because the oldnickname * db format didn't allow multiple certs with the same subject.
*/ /* where does subjectKeyID and certKey come from? */
subjectEntry = NewDBSubjectEntry(&cert->derSubject, &cert->certKey,
&cert->subjectKeyID, nickname,
NULL, 0); if (subjectEntry == NULL) { goto loser;
}
rv = WriteDBSubjectEntry(handle, subjectEntry); if (rv != SECSuccess) { goto loser;
}
}
state = 3;
if (nicknameEntry) {
DestroyDBEntry((certDBEntry *)nicknameEntry);
}
if (subjectEntry) {
DestroyDBEntry((certDBEntry *)subjectEntry);
}
return (certEntry);
loser: /* don't leave partial entry in the database */ if (state > 0) {
DeleteDBCertEntry(handle, &cert->certKey);
} if ((state > 1) && donnentry) {
DeleteDBNicknameEntry(handle, nickname);
} if (certEntry) {
DestroyDBEntry((certDBEntry *)certEntry);
} if (nicknameEntry) {
DestroyDBEntry((certDBEntry *)nicknameEntry);
} if (subjectEntry) {
DestroyDBEntry((certDBEntry *)subjectEntry);
}
return (NULL);
}
/* forward declaration */ static SECStatus
UpdateV7DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb);
/* * version 8 uses the same schema as version 7. The only differences are * 1) version 8 db uses the blob shim to store data entries > 32k. * 2) version 8 db sets the db block size to 32k. * both of these are dealt with by the handle.
*/
/* * we could just blindly sequence through reading key data pairs and writing * them back out, but some cert.db's have gotten quite large and may have some * subtle corruption problems, so instead we cycle through the certs and * CRL's and S/MIME profiles and rebuild our subject lists from those records.
*/ static SECStatus
UpdateV7DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
{
DBT key, data; int ret;
NSSLOWCERTCertificate *cert;
PRBool isKRL = PR_FALSE;
certDBEntryType entryType;
SECItem dbEntry, dbKey;
certDBEntryRevocation crlEntry;
certDBEntryCert certEntry;
certDBEntrySMime smimeEntry;
SECStatus rv;
ret = (*updatedb->seq)(updatedb, &key, &data, R_FIRST);
switch (entryType) { /* these entries will get regenerated as we read the
* rest of the data from the database */ case certDBEntryTypeVersion: case certDBEntryTypeSubject: case certDBEntryTypeContentVersion: case certDBEntryTypeNickname: /* smime profiles need entries created after the certs have
* been imported, loop over them in a second run */ case certDBEntryTypeSMimeProfile: break;
case certDBEntryTypeCert: /* decode Entry */
certEntry.common.version = (unsignedint)dataBuf[0];
certEntry.common.type = entryType;
certEntry.common.flags = (unsignedint)dataBuf[2];
rv = DecodeDBCertEntry(&certEntry, &dbEntry); if (rv != SECSuccess) { break;
} /* should we check for existing duplicates? */
cert = nsslowcert_DecodeDERCertificate(&certEntry.derCert,
certEntry.nickname); if (cert) {
nsslowcert_UpdatePermCert(handle, cert, certEntry.nickname,
&certEntry.trust);
nsslowcert_DestroyCertificate(cert);
} /* free any data the decode may have allocated. */
pkcs11_freeStaticData(certEntry.derCert.data,
certEntry.derCertSpace);
pkcs11_freeNickname(certEntry.nickname, certEntry.nicknameSpace); break;
case certDBEntryTypeKeyRevocation:
isKRL = PR_TRUE; /* fall through */ case certDBEntryTypeRevocation:
crlEntry.common.version = (unsignedint)dataBuf[0];
crlEntry.common.type = entryType;
crlEntry.common.flags = (unsignedint)dataBuf[2];
crlEntry.common.arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (crlEntry.common.arena == NULL) { break;
}
rv = DecodeDBCrlEntry(&crlEntry, &dbEntry); if (rv != SECSuccess) { break;
}
nsslowcert_UpdateCrl(handle, &crlEntry.derCrl, &dbKey,
crlEntry.url, isKRL); /* free data allocated by the decode */
PORT_FreeArena(crlEntry.common.arena, PR_FALSE);
crlEntry.common.arena = NULL; break;
/* a database update is a good time to go back and verify the integrity of
* the keys and certs */
handle->dbVerify = PR_TRUE; return (SECSuccess);
}
/* * NOTE - Version 6 DB did not go out to the real world in a release, * so we can remove this function in a later release.
*/ static SECStatus
UpdateV6DB(NSSLOWCERTCertDBHandle *handle, DB *updatedb)
{ int ret;
DBT key, data; unsignedchar *buf, *tmpbuf = NULL;
certDBEntryType type;
certDBEntryNickname *nnEntry = NULL;
certDBEntrySubject *subjectEntry = NULL;
certDBEntrySMime *emailEntry = NULL; char *nickname; char *emailAddr;
/* * Sequence through the old database and copy all of the entries * to the new database. Subject name entries will have the new * fields inserted into them (with zero length).
*/
ret = (*updatedb->seq)(updatedb, &key, &data, R_FIRST); if (ret) { return (SECFailure);
}
do {
buf = (unsignedchar *)data.data;
if (data.size >= 3) { if (buf[0] == 6) { /* version number */
type = (certDBEntryType)buf[1]; if (type == certDBEntryTypeSubject) { /* expando subjecto entrieo */
tmpbuf = (unsignedchar *)PORT_Alloc(data.size + 4); if (tmpbuf) { /* copy header stuff */
PORT_Memcpy(tmpbuf, buf, SEC_DB_ENTRY_HEADER_LEN + 2); /* insert 4 more bytes of zero'd header */
PORT_Memset(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 2],
0, 4); /* copy rest of the data */
PORT_Memcpy(&tmpbuf[SEC_DB_ENTRY_HEADER_LEN + 6],
&buf[SEC_DB_ENTRY_HEADER_LEN + 2],
data.size - (SEC_DB_ENTRY_HEADER_LEN + 2));
/* update the record version number */
buf[0] = CERT_DB_FILE_VERSION;
/* copy to the new database */
ret = certdb_Put(handle->permCertDB, &key, &data, 0); if (tmpbuf) {
PORT_Free(tmpbuf);
tmpbuf = NULL;
} if (ret) { return SECFailure;
}
}
}
} while ((*updatedb->seq)(updatedb, &key, &data, R_NEXT) == 0);
ret = certdb_Sync(handle->permCertDB, 0); if (ret) { return SECFailure;
}
ret = (*updatedb->seq)(updatedb, &key, &data, R_FIRST); if (ret) { return (SECFailure);
}
do {
buf = (unsignedchar *)data.data;
if (data.size >= 3) { if (buf[0] == CERT_DB_FILE_VERSION) { /* version number */
type = (certDBEntryType)buf[1]; if (type == certDBEntryTypeNickname) {
nickname = &((char *)key.data)[1];
/* get the matching nickname entry in the new DB */
nnEntry = ReadDBNicknameEntry(handle, nickname); if (nnEntry == NULL) { goto endloop;
}
/* find the subject entry pointed to by nickname */
subjectEntry = ReadDBSubjectEntry(handle,
&nnEntry->subjectName); if (subjectEntry == NULL) { goto endloop;
}
/* get the matching smime entry in the new DB */
emailEntry = nsslowcert_ReadDBSMimeEntry(handle, emailAddr); if (emailEntry == NULL) { goto endloop;
}
/* find the subject entry pointed to by nickname */
subjectEntry = ReadDBSubjectEntry(handle,
&emailEntry->subjectName); if (subjectEntry == NULL) { goto endloop;
}
/* SSL user certs can be used for email if they have an email addr */ if (cert->emailAddr && (trust->sslFlags & CERTDB_USER) &&
(trust->emailFlags == 0)) {
trust->emailFlags = CERTDB_USER;
} /* servers didn't set the user flags on the server cert.. */ if (PORT_Strcmp(cert->dbEntry->nickname, "Server-Cert") == 0) {
trust->sslFlags |= CERTDB_USER;
}
ret = certdb_Get(handle->permCertDB, &namekey, &tmpdata, 0); if (ret == 0) { goto loser;
}
PORT_FreeArena(arena, PR_FALSE);
return (PR_FALSE);
loser: if (arena) {
PORT_FreeArena(arena, PR_FALSE);
}
return (PR_TRUE);
}
/* * return true if a nickname conflict exists * NOTE: caller must have already made sure that this exact cert * doesn't exist in the DB
*/ static PRBool
nsslowcert_CertNicknameConflict(char *nickname, SECItem *derSubject,
NSSLOWCERTCertDBHandle *handle)
{
PRBool rv;
certDBEntryNickname *entry;
if (nickname == NULL) { return (PR_FALSE);
}
entry = ReadDBNicknameEntry(handle, nickname);
if (entry == NULL) { /* no entry for this nickname, so no conflict */ return (PR_FALSE);
}
rv = PR_TRUE; if (SECITEM_CompareItem(derSubject, &entry->subjectName) == SECEqual) { /* if subject names are the same, then no conflict */
rv = PR_FALSE;
}
/* * open an old database that needs to be updated
*/ static DB *
nsslowcert_openolddb(NSSLOWCERTDBNameFunc namecb, void *cbarg, int version)
{ char *tmpname;
DB *updatedb = NULL;
tmpname = (*namecb)(cbarg, version); /* get v6 db name */ if (tmpname) {
updatedb = dbopen(tmpname, NO_RDONLY, 0600, DB_HASH, 0);
PORT_Free(tmpname);
} return updatedb;
}
static SECStatus
openNewCertDB(constchar *appName, constchar *prefix, constchar *certdbname,
NSSLOWCERTCertDBHandle *handle, NSSLOWCERTDBNameFunc namecb, void *cbarg)
{
SECStatus rv;
certDBEntryVersion *versionEntry = NULL;
DB *updatedb = NULL; int status = RDB_FAIL;
staticint
nsslowcert_GetVersionNumber(NSSLOWCERTCertDBHandle *handle)
{
certDBEntryVersion *versionEntry = NULL; int version = 0;
versionEntry = ReadDBVersionEntry(handle); if (versionEntry == NULL) { return 0;
}
version = versionEntry->common.version;
DestroyDBEntry((certDBEntry *)versionEntry); return version;
}
/* * Open the certificate database and index databases. Create them if * they are not there or bad.
*/ static SECStatus
nsslowcert_OpenPermCertDB(NSSLOWCERTCertDBHandle *handle, PRBool readOnly, constchar *appName, constchar *prefix,
NSSLOWCERTDBNameFunc namecb, void *cbarg)
{
SECStatus rv; int openflags; char *certdbname; int version = 0;
/* * first open the permanent file based database.
*/ if (appName) {
handle->permCertDB = rdbopen(appName, prefix, "cert", openflags, NULL);
} else {
handle->permCertDB = dbsopen(certdbname, openflags, 0600, DB_HASH, 0);
}
/* check for correct version number */ if (handle->permCertDB) {
version = nsslowcert_GetVersionNumber(handle); if ((version != CERT_DB_FILE_VERSION) &&
!(appName && version == CERT_DB_V7_FILE_VERSION)) { goto loser;
}
} elseif (readOnly) { /* don't create if readonly */ /* Try openning a version 7 database */
handle->permCertDB = nsslowcert_openolddb(namecb, cbarg, 7); if (!handle->permCertDB) { goto loser;
} if (nsslowcert_GetVersionNumber(handle) != 7) { goto loser;
}
} else { /* if first open fails, try to create a new DB */
rv = openNewCertDB(appName, prefix, certdbname, handle, namecb, cbarg); if (rv == SECWouldBlock) { /* only the rdb version can fail with wouldblock */
handle->permCertDB =
rdbopen(appName, prefix, "cert", openflags, NULL);
/* check for correct version number */ if (!handle->permCertDB) { goto loser;
}
version = nsslowcert_GetVersionNumber(handle); if ((version != CERT_DB_FILE_VERSION) &&
!(appName && version == CERT_DB_V7_FILE_VERSION)) { goto loser;
}
} elseif (rv != SECSuccess) { goto loser;
}
}
PORT_Free(certdbname);
return (SECSuccess);
loser:
PORT_SetError(SEC_ERROR_BAD_DATABASE);
if (handle->permCertDB) {
certdb_Close(handle->permCertDB);
handle->permCertDB = 0;
}
PORT_Free(certdbname);
return (SECFailure);
}
/* * delete all DB records associated with a particular certificate
*/ static SECStatus
DeletePermCert(NSSLOWCERTCertificate *cert)
{
SECStatus rv;
SECStatus ret;
ret = SECSuccess;
rv = DeleteDBCertEntry(cert->dbhandle, &cert->certKey); if (rv != SECSuccess) {
ret = SECFailure;
}
rv = RemovePermSubjectNode(cert);
return (ret);
}
/* * Delete a certificate from the permanent database.
*/
SECStatus
nsslowcert_DeletePermCertificate(NSSLOWCERTCertificate *cert)
{
SECStatus rv;
nsslowcert_LockDB(cert->dbhandle);
/* delete the records from the permanent database */
rv = DeletePermCert(cert);
/* get rid of dbcert and stuff pointing to it */
DestroyDBEntry((certDBEntry *)cert->dbEntry);
cert->dbEntry = NULL;
cert->trust = NULL;
/* * Traverse all of the entries in the database of a particular type * call the given function for each one.
*/
SECStatus
nsslowcert_TraverseDBEntries(NSSLOWCERTCertDBHandle *handle,
certDBEntryType type,
SECStatus (*callback)(SECItem *data, SECItem *key,
certDBEntryType type, void *pdata), void *udata)
{
DBT data;
DBT key;
SECStatus rv = SECSuccess; int ret;
SECItem dataitem;
SECItem keyitem; unsignedchar *buf; unsignedchar *keybuf;
ret = certdb_Seq(handle->permCertDB, &key, &data, R_FIRST); if (ret) { return (SECFailure);
} /* here, ret is zero and rv is SECSuccess. * Below here, ret is a count of successful calls to the callback function.
*/ do {
buf = (unsignedchar *)data.data;
rv = (*callback)(&dataitem, &keyitem, type, udata); if (rv == SECSuccess) {
++ret;
}
}
} while (certdb_Seq(handle->permCertDB, &key, &data, R_NEXT) == 0); /* If any callbacks succeeded, or no calls to callbacks were made, * then report success. Otherwise, report failure.
*/ return (ret ? SECSuccess : rv);
} /* * Decode a certificate and enter it into the temporary certificate database. * Deal with nicknames correctly * * This is the private entry point.
*/ static NSSLOWCERTCertificate *
DecodeACert(NSSLOWCERTCertDBHandle *handle, certDBEntryCert *entry)
{
NSSLOWCERTCertificate *cert = NULL;
/* arena stored in entry destroyed by nsslowcert_DestroyCertificate */
nsslowcert_DestroyCertificateNoLocking(cert);
return (rv);
loser: if (arena) {
PORT_FreeArena(arena, PR_FALSE);
} return (SECFailure);
}
/* * Traverse all of the certificates in the permanent database and * call the given function for each one; expect the caller to have lock.
*/ static SECStatus
TraversePermCertsNoLocking(NSSLOWCERTCertDBHandle *handle,
SECStatus (*certfunc)(NSSLOWCERTCertificate *cert,
SECItem *k, void *pdata), void *udata)
{
SECStatus rv;
PermCertCallbackState mystate;
/* * Traverse all of the certificates in the permanent database and * call the given function for each one.
*/
SECStatus
nsslowcert_TraversePermCerts(NSSLOWCERTCertDBHandle *handle,
SECStatus (*certfunc)(NSSLOWCERTCertificate *cert, SECItem *k, void *pdata), void *udata)
{
SECStatus rv;
/* * Close the database
*/ void
nsslowcert_ClosePermCertDB(NSSLOWCERTCertDBHandle *handle)
{ if (handle) { if (handle->permCertDB) {
certdb_Close(handle->permCertDB);
handle->permCertDB = NULL;
} if (handle->dbMon) {
PZ_DestroyMonitor(handle->dbMon);
handle->dbMon = NULL;
}
PORT_Free(handle);
} return;
}
/* * Get the trust attributes from a certificate
*/
SECStatus
nsslowcert_GetCertTrust(NSSLOWCERTCertificate *cert, NSSLOWCERTCertTrust *trust)
{
SECStatus rv;
/* * Change the trust attributes of a certificate and make them permanent * in the database.
*/
SECStatus
nsslowcert_ChangeCertTrust(NSSLOWCERTCertDBHandle *handle,
NSSLOWCERTCertificate *cert, NSSLOWCERTCertTrust *trust)
{
certDBEntryCert *entry; int rv;
SECStatus ret;
nsslowcert_LockDB(handle);
nsslowcert_LockCertTrust(cert); /* only set the trust on permanent certs */ if (cert->trust == NULL) {
ret = SECFailure; goto done;
}
*cert->trust = *trust; if (cert->dbEntry == NULL) {
ret = SECSuccess; /* not in permanent database */ goto done;
}
entry = cert->dbEntry;
entry->trust = *trust;
rv = WriteDBCertEntry(handle, entry); if (rv) {
ret = SECFailure; goto done;
}
ret = nsslowcert_UpdatePermCert(dbhandle, cert, nickname, trust);
nsslowcert_UnlockDB(dbhandle); return (ret);
}
/* * Open the certificate database and index databases. Create them if * they are not there or bad.
*/
SECStatus
nsslowcert_OpenCertDB(NSSLOWCERTCertDBHandle *handle, PRBool readOnly, constchar *appName, constchar *prefix,
NSSLOWCERTDBNameFunc namecb, void *cbarg, PRBool openVolatile)
{ int rv;
loser: if (trust == NULL) { if (entry) {
DestroyDBEntry((certDBEntry *)entry);
}
}
if (locked) {
nsslowcert_UnlockDB(handle);
}
return (trust);
}
/* * Lookup a certificate in the databases without locking
*/
NSSLOWCERTCertificate *
nsslowcert_FindCertByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
{ return (FindCertByKey(handle, certKey, PR_FALSE));
}
/* * Lookup a trust object in the databases without locking
*/
NSSLOWCERTTrust *
nsslowcert_FindTrustByKey(NSSLOWCERTCertDBHandle *handle, const SECItem *certKey)
{ return (FindTrustByKey(handle, certKey, PR_FALSE));
}
/* * Generate a key from an issuerAndSerialNumber, and find the * associated cert in the database.
*/
NSSLOWCERTCertificate *
nsslowcert_FindCertByIssuerAndSN(NSSLOWCERTCertDBHandle *handle, NSSLOWCERTIssuerAndSN *issuerAndSN)
{
SECItem certKey;
SECItem *sn = &issuerAndSN->serialNumber;
SECItem *issuer = &issuerAndSN->derIssuer;
NSSLOWCERTCertificate *cert; int data_len = sn->len; int index = 0;
/* automatically detect DER encoded serial numbers and remove the der * encoding since the database expects unencoded data.
* if it's DER encoded, there must be at least 3 bytes, tag, len, data */ if ((sn->len >= 3) && (sn->data[0] == 0x2)) { /* remove the der encoding of the serial number before generating the
* key.. */ int data_left = sn->len - 2;
data_len = sn->data[1];
index = 2;
/* extended length ? (not very likely for a serial number) */ if (data_len & 0x80) { int len_count = data_len & 0x7f;
data_len = 0;
data_left -= len_count; if (data_left > 0) { while (len_count--) {
data_len = (data_len << 8) | sn->data[index++];
}
}
} /* XXX leaving any leading zeros on the serial number for backwards * compatibility
*/ /* not a valid der, must be just an unlucky serial number value */ if (data_len != data_left) {
data_len = sn->len;
index = 0;
}
}
/* * Generate a key from an issuerAndSerialNumber, and find the * associated cert in the database.
*/
NSSLOWCERTTrust *
nsslowcert_FindTrustByIssuerAndSN(NSSLOWCERTCertDBHandle *handle,
NSSLOWCERTIssuerAndSN *issuerAndSN)
{
SECItem certKey;
SECItem *sn = &issuerAndSN->serialNumber;
SECItem *issuer = &issuerAndSN->derIssuer;
NSSLOWCERTTrust *trust; unsignedchar keyBuf[512]; int data_len = sn->len; int index = 0; int len;
/* automatically detect DER encoded serial numbers and remove the der * encoding since the database expects unencoded data.
* if it's DER encoded, there must be at least 3 bytes, tag, len, data */ if ((sn->len >= 3) && (sn->data[0] == 0x2)) { /* remove the der encoding of the serial number before generating the
* key.. */ int data_left = sn->len - 2;
data_len = sn->data[1];
index = 2;
/* extended length ? (not very likely for a serial number) */ if (data_len & 0x80) { int len_count = data_len & 0x7f;
data_len = 0;
data_left -= len_count; if (data_left > 0) { while (len_count--) {
data_len = (data_len << 8) | sn->data[index++];
}
}
} /* XXX leaving any leading zeros on the serial number for backwards * compatibility
*/ /* not a valid der, must be just an unlucky serial number value */ if (data_len != data_left) {
data_len = sn->len;
index = 0;
}
}
/* * look for the given DER certificate in the database
*/
NSSLOWCERTCertificate *
nsslowcert_FindCertByDERCert(NSSLOWCERTCertDBHandle *handle, SECItem *derCert)
{
PLArenaPool *arena;
SECItem certKey;
SECStatus rv;
NSSLOWCERTCertificate *cert = NULL;
/* create a scratch arena */
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); if (arena == NULL) { return (NULL);
}
/* extract the database key from the cert */
rv = nsslowcert_KeyFromDERCert(arena, derCert, &certKey); if (rv != SECSuccess) { goto loser;
}
/* find the certificate */
cert = nsslowcert_FindCertByKey(handle, &certKey);
staticvoid
DestroyCertificate(NSSLOWCERTCertificate *cert, PRBool lockdb)
{ int refCount;
NSSLOWCERTCertDBHandle *handle;
if (cert) {
handle = cert->dbhandle;
/* * handle may be NULL, for example if the cert was created with * nsslowcert_DecodeDERCertificate.
*/ if (lockdb && handle) {
nsslowcert_LockDB(handle);
}
/* zero cert before freeing. Any stale references to this cert
* after this point will probably cause an exception. */
PORT_Memset(cert, 0, sizeof *cert);
/* use reflock to protect the free list */
nsslowcert_LockFreeList(); if (certListCount > MAX_CERT_LIST_COUNT) {
PORT_Free(cert);
} else {
certListCount++;
cert->next = certListHead;
certListHead = cert;
}
nsslowcert_UnlockFreeList();
cert = NULL;
} if (lockdb && handle) {
nsslowcert_UnlockDB(handle);
}
}
/* * Lookup a CRL in the databases. We mirror the same fast caching data base * caching stuff used by certificates....?
*/
certDBEntryRevocation *
nsslowcert_FindCrlByKey(NSSLOWCERTCertDBHandle *handle,
SECItem *crlKey, PRBool isKRL)
{
SECItem keyitem;
SECStatus rv;
PLArenaPool *arena = NULL;
certDBEntryRevocation *entry = NULL;
certDBEntryType crlType = isKRL ? certDBEntryTypeKeyRevocation
: certDBEntryTypeRevocation;
PRBool
nsslowcert_hasTrust(NSSLOWCERTCertTrust *trust)
{ if (trust == NULL) { return PR_FALSE;
} /* if we only have CERTDB__USER and CERTDB_TRUSTED_UNKNOWN bits, then
* we don't have a trust record. */ return !(((trust->sslFlags & ~(CERTDB_USER | CERTDB_TRUSTED_UNKNOWN)) == 0) &&
((trust->emailFlags & ~(CERTDB_USER | CERTDB_TRUSTED_UNKNOWN)) == 0) &&
((trust->objectSigningFlags & ~(CERTDB_USER | CERTDB_TRUSTED_UNKNOWN)) == 0));
}
/* * This function has the logic that decides if another person's cert and * email profile from an S/MIME message should be saved. It can deal with * the case when there is no profile.
*/ static SECStatus
nsslowcert_UpdateSMimeProfile(NSSLOWCERTCertDBHandle *dbhandle, char *emailAddr, SECItem *derSubject, SECItem *emailProfile,
SECItem *profileTime)
{
certDBEntrySMime *entry = NULL;
SECStatus rv = SECFailure;
;
if (entry) { /* keep our old db entry consistant for old applications. */ if (!SECITEM_ItemsAreEqual(derSubject, &entry->subjectName)) {
nsslowcert_UpdateSubjectEmailAddr(dbhandle, &entry->subjectName,
emailAddr, nsslowcert_remove);
}
DestroyDBEntry((certDBEntry *)entry);
entry = NULL;
}
/* now save the entry */
entry = NewDBSMimeEntry(emailAddr, derSubject, emailProfile,
profileTime, 0); if (entry == NULL) {
rv = SECFailure; goto loser;
}
nsslowcert_LockDB(dbhandle);
rv = DeleteDBSMimeEntry(dbhandle, emailAddr); /* if delete fails, try to write new entry anyway... */
/* link subject entry back here */
rv = nsslowcert_UpdateSubjectEmailAddr(dbhandle, derSubject, emailAddr,
nsslowcert_add); if (rv != SECSuccess) {
nsslowcert_UnlockDB(dbhandle); goto loser;
}
switch (entryType) { case certDBEntryTypeContentVersion: /* This type appears to be unused */ case certDBEntryTypeVersion: /* This type has only the common hdr */
rv = SECSuccess; break;
case certDBEntryTypeSubject:
rv = DecodeDBSubjectEntry(&entry->subject, &dbEntry, dbKey); break;
case certDBEntryTypeNickname:
rv = DecodeDBNicknameEntry(&entry->nickname, &dbEntry,
(char *)dbKey->data); break;
/* smime profiles need entries created after the certs have
* been imported, loop over them in a second run */ case certDBEntryTypeSMimeProfile:
rv = DecodeDBSMimeEntry(&entry->smime, &dbEntry, (char *)dbKey->data); break;
case certDBEntryTypeCert:
rv = DecodeDBCertEntry(&entry->cert, &dbEntry); break;
case certDBEntryTypeKeyRevocation: case certDBEntryTypeRevocation:
rv = DecodeDBCrlEntry(&entry->revocation, &dbEntry); break;
loser: if (arena)
PORT_FreeArena(arena, PR_FALSE); return NULL;
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.96Bemerkung:
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-05-09)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.