/* 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/. */
/* XXX * Locking is not handled well at all. A single, global lock with sub-locks * in the collection types. Cleanup needed.
*/
/* should it live in its own arena? */ struct nssTDCertificateCacheStr {
PZLock *lock; /* Must not be held when calling nssSlot_IsTokenPresent. See bug 1625791. */
NSSArena *arena;
nssHash *issuerAndSN;
nssHash *subject;
nssHash *nickname;
nssHash *email;
};
/* this should not be exposed in a header, but is here to keep the above * types/functions static
*/
NSS_IMPLEMENT PRStatus
nssTrustDomain_InitializeCache(
NSSTrustDomain *td,
PRUint32 cacheSize)
{
NSSArena *arena;
nssTDCertificateCache *cache = td->cache; #ifdef DEBUG_CACHE
s_log = PR_NewLogModule("nss_cache");
PR_ASSERT(s_log); #endif
PR_ASSERT(!cache);
arena = nssArena_Create(); if (!arena) { return PR_FAILURE;
}
cache = nss_ZNEW(arena, nssTDCertificateCache); if (!cache) {
nssArena_Destroy(arena); return PR_FAILURE;
}
cache->lock = PZ_NewLock(nssILockCache); if (!cache->lock) {
nssArena_Destroy(arena); return PR_FAILURE;
} /* Create the issuer and serial DER --> certificate hash */
cache->issuerAndSN = nssHash_CreateCertificate(arena, cacheSize); if (!cache->issuerAndSN) { goto loser;
} /* Create the subject DER --> subject list hash */
cache->subject = nssHash_CreateItem(arena, cacheSize); if (!cache->subject) { goto loser;
} /* Create the nickname --> subject list hash */
cache->nickname = nssHash_CreateString(arena, cacheSize); if (!cache->nickname) { goto loser;
} /* Create the email --> list of subject lists hash */
cache->email = nssHash_CreateString(arena, cacheSize); if (!cache->email) { goto loser;
}
cache->arena = arena;
td->cache = cache; #ifdef DEBUG_CACHE
PR_LOG(s_log, PR_LOG_DEBUG, ("Cache initialized.")); #endif return PR_SUCCESS;
loser:
PZ_DestroyLock(cache->lock);
nssArena_Destroy(arena);
td->cache = NULL; #ifdef DEBUG_CACHE
PR_LOG(s_log, PR_LOG_DEBUG, ("Cache initialization failed.")); #endif return PR_FAILURE;
}
/* The entries of the hashtable are currently dependent on the certificate(s) * that produced them. That is, the entries will be freed when the cert is * released from the cache. If there are certs in the cache at any time, * including shutdown, the hash table entries will hold memory. In order for * clean shutdown, it is necessary for there to be no certs in the cache.
*/
static PRStatus
remove_email_entry(
nssTDCertificateCache *cache,
NSSCertificate *cert,
nssList *subjectList)
{
PRStatus nssrv = PR_FAILURE;
cache_entry *ce; /* Find the subject list in the email hash */ if (cert->email) {
ce = (cache_entry *)nssHash_Lookup(cache->email, cert->email); if (ce) {
nssList *subjects = ce->entry.list; /* Remove the subject list from the email hash */ if (subjects) {
nssList_Remove(subjects, subjectList); #ifdef DEBUG_CACHE
log_item_dump("removed subject list", &cert->subject);
PR_LOG(s_log, PR_LOG_DEBUG, ("for email %s", cert->email)); #endif if (nssList_Count(subjects) == 0) { /* No more subject lists for email, delete list and * remove hash entry
*/
(void)nssList_Destroy(subjects);
nssHash_Remove(cache->email, cert->email); /* there are no entries left for this address, free space * used for email entries
*/
nssArena_Destroy(ce->arena); #ifdef DEBUG_CACHE
PR_LOG(s_log, PR_LOG_DEBUG, ("removed email %s", cert->email)); #endif
}
}
nssrv = PR_SUCCESS;
}
} return nssrv;
}
#ifdef DEBUG_CACHE
log_cert_ref("attempt to remove cert", cert); #endif
ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, cert); if (!ce || ce->entry.cert != cert) { /* If it's not in the cache, or a different cert is (this is really * for safety reasons, though it shouldn't happen), do nothing
*/ #ifdef DEBUG_CACHE
PR_LOG(s_log, PR_LOG_DEBUG, ("but it wasn't in the cache")); #endif return;
}
(void)remove_issuer_and_serial_entry(td->cache, cert);
(void)remove_subject_entry(td->cache, cert, &subjectList,
&nickname, &arena); if (nssList_Count(subjectList) == 0) {
(void)remove_nickname_entry(td->cache, nickname, subjectList);
(void)remove_email_entry(td->cache, cert, subjectList);
(void)nssList_Destroy(subjectList);
nssHash_Remove(td->cache->subject, &cert->subject); /* there are no entries left for this subject, free the space used * for both the nickname and subject entries
*/ if (arena) {
nssArena_Destroy(arena);
}
}
}
/* * Remove all certs for the given token from the cache. This is * needed if the token is removed.
*/
NSS_IMPLEMENT PRStatus
nssTrustDomain_RemoveTokenCertsFromCache(
NSSTrustDomain *td,
NSSToken *token)
{
NSSCertificate **certs;
PRUint32 i, arrSize = 10; struct token_cert_dtor dtor;
certs = nss_ZNEWARRAY(NULL, NSSCertificate *, arrSize); if (!certs) { return PR_FAILURE;
}
dtor.cache = td->cache;
dtor.token = token;
dtor.certs = certs;
dtor.numCerts = 0;
dtor.arrSize = arrSize;
PZ_Lock(td->cache->lock);
nssHash_Iterate(td->cache->issuerAndSN, remove_token_certs, &dtor); for (i = 0; i < dtor.numCerts; i++) { if (dtor.certs[i]->object.numInstances == 0) {
nssTrustDomain_RemoveCertFromCacheLOCKED(td, dtor.certs[i]);
dtor.certs[i] = NULL; /* skip this cert in the second for loop */
} else { /* make sure it doesn't disappear on us before we finish */
nssCertificate_AddRef(dtor.certs[i]);
}
}
PZ_Unlock(td->cache->lock); for (i = 0; i < dtor.numCerts; i++) { if (dtor.certs[i]) {
STAN_ForceCERTCertificateUpdate(dtor.certs[i]);
nssCertificate_Destroy(dtor.certs[i]);
}
}
nss_ZFreeIf(dtor.certs); return PR_SUCCESS;
}
static PRStatus
add_subject_entry(
NSSArena *arena,
nssTDCertificateCache *cache,
NSSCertificate *cert,
NSSUTF8 *nickname,
nssList **subjectList)
{
PRStatus nssrv;
nssList *list;
cache_entry *ce;
*subjectList = NULL; /* this is only set if a new one is created */
ce = (cache_entry *)nssHash_Lookup(cache->subject, &cert->subject); if (ce) {
ce->hits++;
ce->lastHit = PR_Now(); /* The subject is already in, add this cert to the list */
nssrv = nssList_AddUnique(ce->entry.list, cert); #ifdef DEBUG_CACHE
log_cert_ref("added to existing subject list", cert); #endif
} else {
NSSDER *subject; /* Create a new subject list for the subject */
list = nssList_Create(arena, PR_FALSE); if (!list) { return PR_FAILURE;
}
ce = new_cache_entry(arena, (void *)list, PR_TRUE); if (!ce) { return PR_FAILURE;
} if (nickname) {
ce->nickname = nssUTF8_Duplicate(nickname, arena);
}
nssList_SetSortFunction(list, nssCertificate_SubjectListSort); /* Add the cert entry to this list of subjects */
nssrv = nssList_AddUnique(list, cert); if (nssrv != PR_SUCCESS) { return nssrv;
} /* Add the subject list to the cache */
subject = nssItem_Duplicate(&cert->subject, arena, NULL); if (!subject) { return PR_FAILURE;
}
nssrv = nssHash_Add(cache->subject, subject, ce); if (nssrv != PR_SUCCESS) { return nssrv;
}
*subjectList = list; #ifdef DEBUG_CACHE
log_cert_ref("created subject list", cert); #endif
} return nssrv;
}
static PRStatus
add_nickname_entry(
NSSArena *arena,
nssTDCertificateCache *cache,
NSSUTF8 *certNickname,
nssList *subjectList)
{
PRStatus nssrv = PR_SUCCESS;
cache_entry *ce;
ce = (cache_entry *)nssHash_Lookup(cache->nickname, certNickname); if (ce) { /* This is a collision. A nickname entry already exists for this * subject, but a subject entry didn't. This would imply there are * two subjects using the same nickname, which is not allowed.
*/ return PR_FAILURE;
} else {
NSSUTF8 *nickname;
ce = new_cache_entry(arena, subjectList, PR_FALSE); if (!ce) { return PR_FAILURE;
}
nickname = nssUTF8_Duplicate(certNickname, arena); if (!nickname) { return PR_FAILURE;
}
nssrv = nssHash_Add(cache->nickname, nickname, ce); #ifdef DEBUG_CACHE
log_cert_ref("created nickname for", cert); #endif
} return nssrv;
}
static PRStatus
add_email_entry(
nssTDCertificateCache *cache,
NSSCertificate *cert,
nssList *subjectList)
{
PRStatus nssrv = PR_SUCCESS;
nssList *subjects;
cache_entry *ce;
ce = (cache_entry *)nssHash_Lookup(cache->email, cert->email); if (ce) { /* Already have an entry for this email address, but not subject */
subjects = ce->entry.list;
nssrv = nssList_AddUnique(subjects, subjectList);
ce->hits++;
ce->lastHit = PR_Now(); #ifdef DEBUG_CACHE
log_cert_ref("added subject to email for", cert); #endif
} else {
NSSASCII7 *email;
NSSArena *arena;
arena = nssArena_Create(); if (!arena) { return PR_FAILURE;
} /* Create a new list of subject lists, add this subject */
subjects = nssList_Create(arena, PR_TRUE); if (!subjects) {
nssArena_Destroy(arena); return PR_FAILURE;
} /* Add the new subject to the list */
nssrv = nssList_AddUnique(subjects, subjectList); if (nssrv != PR_SUCCESS) {
nssArena_Destroy(arena); return nssrv;
} /* Add the new entry to the cache */
ce = new_cache_entry(arena, (void *)subjects, PR_TRUE); if (!ce) {
nssArena_Destroy(arena); return PR_FAILURE;
}
email = nssUTF8_Duplicate(cert->email, arena); if (!email) {
nssArena_Destroy(arena); return PR_FAILURE;
}
nssrv = nssHash_Add(cache->email, email, ce); if (nssrv != PR_SUCCESS) {
nssArena_Destroy(arena); return nssrv;
} #ifdef DEBUG_CACHE
log_cert_ref("created email for", cert); #endif
} return nssrv;
}
/* Set cc->trust and cc->nssCertificate before taking td->cache->lock. * Otherwise, the sorter in add_subject_entry may eventually call * nssSlot_IsTokenPresent, which must not occur while the cache lock
* is held. See bugs 1625791 and 1651564 for details. */ if (cert->type == NSSCertificateType_PKIX) {
(void)STAN_GetCERTCertificate(cert);
}
PZ_Lock(td->cache->lock); /* If it exists in the issuer/serial hash, it's already in all */
ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, cert); if (ce) {
ce->hits++;
ce->lastHit = PR_Now();
rvCert = nssCertificate_AddRef(ce->entry.cert); #ifdef DEBUG_CACHE
log_cert_ref("attempted to add cert already in cache", cert); #endif
PZ_Unlock(td->cache->lock);
nss_ZFreeIf(certNickname); /* collision - somebody else already added the cert * to the cache before this thread got around to it.
*/ /* merge the instances of the cert */ if (merge_object_instances(&rvCert->object, &cert->object) != SECSuccess) {
nssCertificate_Destroy(rvCert); return NULL;
}
STAN_ForceCERTCertificateUpdate(rvCert);
nssCertificate_Destroy(cert); return rvCert;
} /* create a new cache entry for this cert within the cert's arena*/
nssrv = add_issuer_and_serial_entry(cert->object.arena, td->cache, cert); if (nssrv != PR_SUCCESS) { goto loser;
}
added++; /* create an arena for the nickname and subject entries */
arena = nssArena_Create(); if (!arena) { goto loser;
} /* create a new subject list for this cert, or add to existing */
nssrv = add_subject_entry(arena, td->cache, cert,
certNickname, &subjectList); if (nssrv != PR_SUCCESS) { goto loser;
}
added++; /* If a new subject entry was created, also need nickname and/or email */ if (subjectList != NULL) { #ifdef nodef
PRBool handle = PR_FALSE; #endif if (certNickname) {
nssrv = add_nickname_entry(arena, td->cache,
certNickname, subjectList); if (nssrv != PR_SUCCESS) { goto loser;
} #ifdef nodef
handle = PR_TRUE; #endif
added++;
} if (cert->email) {
nssrv = add_email_entry(td->cache, cert, subjectList); if (nssrv != PR_SUCCESS) { goto loser;
} #ifdef nodef
handle = PR_TRUE; #endif
added += 2;
} #ifdef nodef /* I think either a nickname or email address must be associated * with the cert. However, certs are passed to NewTemp without * either. This worked in the old code, so it must work now.
*/ if (!handle) { /* Require either nickname or email handle */
nssrv = PR_FAILURE; goto loser;
} #endif
} else { /* A new subject entry was not created. arena is unused. */
nssArena_Destroy(arena);
}
rvCert = cert;
PZ_Unlock(td->cache->lock);
nss_ZFreeIf(certNickname); return rvCert;
loser:
nss_ZFreeIf(certNickname);
certNickname = NULL; /* Remove any handles that have been created */
subjectList = NULL; if (added >= 1) {
(void)remove_issuer_and_serial_entry(td->cache, cert);
} if (added >= 2) {
(void)remove_subject_entry(td->cache, cert, &subjectList,
&certNickname, &arena);
} if (added == 3 || added == 5) {
(void)remove_nickname_entry(td->cache, certNickname, subjectList);
} if (added >= 4) {
(void)remove_email_entry(td->cache, cert, subjectList);
} if (subjectList) {
nssHash_Remove(td->cache->subject, &cert->subject);
nssList_Destroy(subjectList);
} if (arena) {
nssArena_Destroy(arena);
}
PZ_Unlock(td->cache->lock); return NULL;
}
NSS_IMPLEMENT PRStatus
nssTrustDomain_AddCertsToCache(
NSSTrustDomain *td,
NSSCertificate **certs,
PRUint32 numCerts)
{
PRUint32 i;
NSSCertificate *c; for (i = 0; i < numCerts && certs[i]; i++) {
c = add_cert_to_cache(td, certs[i]); if (c == NULL) { return PR_FAILURE;
} else {
certs[i] = c;
}
} return PR_SUCCESS;
}
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.