/* 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/. */ #ifndef PKIM_H #include "pkim.h" #endif /* PKIM_H */ #ifndef PKIT_H #include "pkit.h" #endif /* PKIT_H */ #ifndef NSSPKI_H #include "nsspki.h" #endif /* NSSPKI_H */ #ifndef PKI_H #include "pki.h" #endif /* PKI_H */ #ifndef NSSBASE_H #include "nssbase.h" #endif /* NSSBASE_H */ #ifndef BASE_H #include "base.h" #endif /* BASE_H */ #include "cert.h" #include "dev.h" #include "pki3hack.h" #ifdef DEBUG_CACHE static PRLogModuleInfo *s_log = NULL; #endif #ifdef DEBUG_CACHE static void log_item_dump(const char *msg, NSSItem *it) { char buf[33]; int i, j; for (i = 0; i < 10 && i < it->size; i++) { snprintf(&buf[2 * i], sizeof(buf) - 2 * i, "%02X", ((PRUint8 *)it->data)[i]); } if (it->size > 10) { snprintf(&buf[2 * i], sizeof(buf) - 2 * i, ".."); i += 1; for (j = it->size - 1; i <= 16 && j > 10; i++, j--) { snprintf(&buf[2 * i], sizeof(buf) - 2 * i, "%02X", ((PRUint8 *)it->data)[j]); } } PR_LOG(s_log, PR_LOG_DEBUG, ("%s: %s", msg, buf)); } #endif #ifdef DEBUG_CACHE static void log_cert_ref(const char *msg, NSSCertificate *c) { PR_LOG(s_log, PR_LOG_DEBUG, ("%s: %s", msg, (c->nickname) ? c->nickname : c->email)); log_item_dump("\tserial", &c->serial); log_item_dump("\tsubject", &c->subject); } #endif /* Certificate cache routines */ /* 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; }; struct cache_entry_str { union { NSSCertificate *cert; nssList *list; void *value; } entry; PRUint32 hits; PRTime lastHit; NSSArena *arena; NSSUTF8 *nickname; }; typedef struct cache_entry_str cache_entry; static cache_entry * new_cache_entry(NSSArena *arena, void *value, PRBool ownArena) { cache_entry *ce = nss_ZNEW(arena, cache_entry); if (ce) { ce->entry.value = value; ce->hits = 1; ce->lastHit = PR_Now(); if (ownArena) { ce->arena = arena; } ce->nickname = NULL; } return ce; } /* 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. */ extern const NSSError NSS_ERROR_INTERNAL_ERROR; extern const NSSError NSS_ERROR_BUSY; NSS_IMPLEMENT PRStatus nssTrustDomain_DestroyCache(NSSTrustDomain *td) { if (!td->cache) { nss_SetError(NSS_ERROR_INTERNAL_ERROR); return PR_FAILURE; } if (nssHash_Count(td->cache->issuerAndSN) > 0) { nss_SetError(NSS_ERROR_BUSY); return PR_FAILURE; } PZ_DestroyLock(td->cache->lock); nssHash_Destroy(td->cache->issuerAndSN); nssHash_Destroy(td->cache->subject); nssHash_Destroy(td->cache->nickname); nssHash_Destroy(td->cache->email); nssArena_Destroy(td->cache->arena); td->cache = NULL; #ifdef DEBUG_CACHE PR_LOG(s_log, PR_LOG_DEBUG, ("Cache destroyed.")); #endif return PR_SUCCESS; } static PRStatus remove_issuer_and_serial_entry( nssTDCertificateCache *cache, NSSCertificate *cert) { /* Remove the cert from the issuer/serial hash */ nssHash_Remove(cache->issuerAndSN, cert); #ifdef DEBUG_CACHE log_cert_ref("removed issuer/sn", cert); #endif return PR_SUCCESS; } static PRStatus remove_subject_entry( nssTDCertificateCache *cache, NSSCertificate *cert, nssList **subjectList, NSSUTF8 **nickname, NSSArena **arena) { PRStatus nssrv; cache_entry *ce; *subjectList = NULL; *arena = NULL; /* Get the subject list for the cert's subject */ ce = (cache_entry *)nssHash_Lookup(cache->subject, &cert->subject); if (ce) { /* Remove the cert from the subject hash */ nssList_Remove(ce->entry.list, cert); *subjectList = ce->entry.list; *nickname = ce->nickname; *arena = ce->arena; nssrv = PR_SUCCESS; #ifdef DEBUG_CACHE log_cert_ref("removed cert", cert); log_item_dump("from subject list", &cert->subject); #endif } else { nssrv = PR_FAILURE; } return nssrv; } static PRStatus remove_nickname_entry( nssTDCertificateCache *cache, NSSUTF8 *nickname, nssList *subjectList) { PRStatus nssrv; if (nickname) { nssHash_Remove(cache->nickname, nickname); nssrv = PR_SUCCESS; #ifdef DEBUG_CACHE PR_LOG(s_log, PR_LOG_DEBUG, ("removed nickname %s", nickname)); #endif } else { nssrv = PR_FAILURE; } return nssrv; } 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; } NSS_IMPLEMENT void nssTrustDomain_RemoveCertFromCacheLOCKED( NSSTrustDomain *td, NSSCertificate *cert) { nssList *subjectList; cache_entry *ce; NSSArena *arena; NSSUTF8 *nickname = NULL; #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); } } } NSS_IMPLEMENT void nssTrustDomain_LockCertCache(NSSTrustDomain *td) { PZ_Lock(td->cache->lock); } NSS_IMPLEMENT void nssTrustDomain_UnlockCertCache(NSSTrustDomain *td) { PZ_Unlock(td->cache->lock); } struct token_cert_dtor { NSSToken *token; nssTDCertificateCache *cache; NSSCertificate **certs; PRUint32 numCerts, arrSize; }; static void remove_token_certs(const void *k, void *v, void *a) { NSSCertificate *c = (NSSCertificate *)k; nssPKIObject *object = &c->object; struct token_cert_dtor *dtor = a; PRUint32 i; nssPKIObject_AddRef(object); nssPKIObject_Lock(object); for (i = 0; i < object->numInstances; i++) { if (object->instances[i]->token == dtor->token) { nssCryptokiObject_Destroy(object->instances[i]); object->instances[i] = object->instances[object->numInstances - 1]; object->instances[object->numInstances - 1] = NULL; object->numInstances--; dtor->certs[dtor->numCerts++] = c; if (dtor->numCerts == dtor->arrSize) { dtor->arrSize *= 2; dtor->certs = nss_ZREALLOCARRAY(dtor->certs, NSSCertificate *, dtor->arrSize); } break; } } nssPKIObject_Unlock(object); nssPKIObject_Destroy(object); return; } /* * 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; } NSS_IMPLEMENT PRStatus nssTrustDomain_UpdateCachedTokenCerts( NSSTrustDomain *td, NSSToken *token) { NSSCertificate **cp, **cached = NULL; nssList *certList; PRUint32 count; certList = nssList_Create(NULL, PR_FALSE); if (!certList) return PR_FAILURE; (void)nssTrustDomain_GetCertsFromCache(td, certList); count = nssList_Count(certList); if (count > 0) { cached = nss_ZNEWARRAY(NULL, NSSCertificate *, count + 1); if (!cached) { nssList_Destroy(certList); return PR_FAILURE; } nssList_GetArray(certList, (void **)cached, count); for (cp = cached; *cp; cp++) { nssCryptokiObject *instance; NSSCertificate *c = *cp; nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; instance = nssToken_FindCertificateByIssuerAndSerialNumber( token, NULL, &c->issuer, &c->serial, tokenOnly, NULL); if (instance) { nssPKIObject_AddInstance(&c->object, instance); STAN_ForceCERTCertificateUpdate(c); } } nssCertificateArray_Destroy(cached); } nssList_Destroy(certList); return PR_SUCCESS; } static PRStatus add_issuer_and_serial_entry( NSSArena *arena, nssTDCertificateCache *cache, NSSCertificate *cert) { cache_entry *ce; ce = new_cache_entry(arena, (void *)cert, PR_FALSE); #ifdef DEBUG_CACHE log_cert_ref("added to issuer/sn", cert); #endif return nssHash_Add(cache->issuerAndSN, cert, (void *)ce); } 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; } extern const NSSError NSS_ERROR_CERTIFICATE_IN_CACHE; static void remove_object_instances( nssPKIObject *object, nssCryptokiObject **instances, int numInstances) { int i; for (i = 0; i < numInstances; i++) { nssPKIObject_RemoveInstanceForToken(object, instances[i]->token); } } static SECStatus merge_object_instances( nssPKIObject *to, nssPKIObject *from) { nssCryptokiObject **instances, **ci; int i; SECStatus rv = SECSuccess; instances = nssPKIObject_GetInstances(from); if (instances == NULL) { return SECFailure; } for (ci = instances, i = 0; *ci; ci++, i++) { nssCryptokiObject *instance = nssCryptokiObject_Clone(*ci); if (instance) { if (nssPKIObject_AddInstance(to, instance) == PR_SUCCESS) { continue; } nssCryptokiObject_Destroy(instance); } remove_object_instances(to, instances, i); rv = SECFailure; break; } nssCryptokiObjectArray_Destroy(instances); return rv; } static NSSCertificate * add_cert_to_cache( NSSTrustDomain *td, NSSCertificate *cert) { NSSArena *arena = NULL; nssList *subjectList = NULL; PRStatus nssrv; PRUint32 added = 0; cache_entry *ce; NSSCertificate *rvCert = NULL; NSSUTF8 *certNickname = nssCertificate_GetNickname(cert, NULL); /* 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; } static NSSCertificate ** collect_subject_certs( nssList *subjectList, nssList *rvCertListOpt) { NSSCertificate *c; NSSCertificate **rvArray = NULL; PRUint32 count; nssCertificateList_AddReferences(subjectList); if (rvCertListOpt) { nssListIterator *iter = nssList_CreateIterator(subjectList); if (!iter) { return (NSSCertificate **)NULL; } for (c = (NSSCertificate *)nssListIterator_Start(iter); c != (NSSCertificate *)NULL; c = (NSSCertificate *)nssListIterator_Next(iter)) { nssList_Add(rvCertListOpt, c); } nssListIterator_Finish(iter); nssListIterator_Destroy(iter); } else { count = nssList_Count(subjectList); rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count + 1); if (!rvArray) { return (NSSCertificate **)NULL; } nssList_GetArray(subjectList, (void **)rvArray, count); } return rvArray; } /* * Find all cached certs with this subject. */ NSS_IMPLEMENT NSSCertificate ** nssTrustDomain_GetCertsForSubjectFromCache( NSSTrustDomain *td, NSSDER *subject, nssList *certListOpt) { NSSCertificate **rvArray = NULL; cache_entry *ce; #ifdef DEBUG_CACHE log_item_dump("looking for cert by subject", subject); #endif PZ_Lock(td->cache->lock); ce = (cache_entry *)nssHash_Lookup(td->cache->subject, subject); if (ce) { ce->hits++; ce->lastHit = PR_Now(); #ifdef DEBUG_CACHE PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); #endif rvArray = collect_subject_certs(ce->entry.list, certListOpt); } PZ_Unlock(td->cache->lock); return rvArray; } /* * Find all cached certs with this label. */ NSS_IMPLEMENT NSSCertificate ** nssTrustDomain_GetCertsForNicknameFromCache( NSSTrustDomain *td, const NSSUTF8 *nickname, nssList *certListOpt) { NSSCertificate **rvArray = NULL; cache_entry *ce; #ifdef DEBUG_CACHE PR_LOG(s_log, PR_LOG_DEBUG, ("looking for cert by nick %s", nickname)); #endif PZ_Lock(td->cache->lock); ce = (cache_entry *)nssHash_Lookup(td->cache->nickname, nickname); if (ce) { ce->hits++; ce->lastHit = PR_Now(); #ifdef DEBUG_CACHE PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); #endif rvArray = collect_subject_certs(ce->entry.list, certListOpt); } PZ_Unlock(td->cache->lock); return rvArray; } /* * Find all cached certs with this email address. */ NSS_IMPLEMENT NSSCertificate ** nssTrustDomain_GetCertsForEmailAddressFromCache( NSSTrustDomain *td, NSSASCII7 *email, nssList *certListOpt) { NSSCertificate **rvArray = NULL; cache_entry *ce; nssList *collectList = NULL; nssListIterator *iter = NULL; nssList *subjectList; #ifdef DEBUG_CACHE PR_LOG(s_log, PR_LOG_DEBUG, ("looking for cert by email %s", email)); #endif PZ_Lock(td->cache->lock); ce = (cache_entry *)nssHash_Lookup(td->cache->email, email); if (ce) { ce->hits++; ce->lastHit = PR_Now(); #ifdef DEBUG_CACHE PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); #endif /* loop over subject lists and get refs for certs */ if (certListOpt) { collectList = certListOpt; } else { collectList = nssList_Create(NULL, PR_FALSE); if (!collectList) { PZ_Unlock(td->cache->lock); return NULL; } } iter = nssList_CreateIterator(ce->entry.list); if (!iter) { PZ_Unlock(td->cache->lock); if (!certListOpt) { nssList_Destroy(collectList); } return NULL; } for (subjectList = (nssList *)nssListIterator_Start(iter); subjectList != (nssList *)NULL; subjectList = (nssList *)nssListIterator_Next(iter)) { (void)collect_subject_certs(subjectList, collectList); } nssListIterator_Finish(iter); nssListIterator_Destroy(iter); } PZ_Unlock(td->cache->lock); if (!certListOpt && collectList) { PRUint32 count = nssList_Count(collectList); rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count); if (rvArray) { nssList_GetArray(collectList, (void **)rvArray, count); } nssList_Destroy(collectList); } return rvArray; } /* * Look for a specific cert in the cache */ NSS_IMPLEMENT NSSCertificate * nssTrustDomain_GetCertForIssuerAndSNFromCache( NSSTrustDomain *td, NSSDER *issuer, NSSDER *serial) { NSSCertificate certkey; NSSCertificate *rvCert = NULL; cache_entry *ce; certkey.issuer.data = issuer->data; certkey.issuer.size = issuer->size; certkey.serial.data = serial->data; certkey.serial.size = serial->size; #ifdef DEBUG_CACHE log_item_dump("looking for cert by issuer/sn, issuer", issuer); log_item_dump(" serial", serial); #endif PZ_Lock(td->cache->lock); ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, &certkey); if (ce) { ce->hits++; ce->lastHit = PR_Now(); rvCert = nssCertificate_AddRef(ce->entry.cert); #ifdef DEBUG_CACHE PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); #endif } PZ_Unlock(td->cache->lock); return rvCert; } /* * Look for a specific cert in the cache */ NSS_IMPLEMENT NSSCertificate * nssTrustDomain_GetCertByDERFromCache( NSSTrustDomain *td, NSSDER *der) { PRStatus nssrv = PR_FAILURE; NSSDER issuer, serial; NSSCertificate *rvCert; nssrv = nssPKIX509_GetIssuerAndSerialFromDER(der, &issuer, &serial); if (nssrv != PR_SUCCESS) { return NULL; } #ifdef DEBUG_CACHE log_item_dump("looking for cert by DER", der); #endif rvCert = nssTrustDomain_GetCertForIssuerAndSNFromCache(td, &issuer, &serial); PORT_Free(issuer.data); PORT_Free(serial.data); return rvCert; } static void cert_iter(const void *k, void *v, void *a) { nssList *certList = (nssList *)a; NSSCertificate *c = (NSSCertificate *)k; nssList_Add(certList, nssCertificate_AddRef(c)); } NSS_EXTERN NSSCertificate ** nssTrustDomain_GetCertsFromCache( NSSTrustDomain *td, nssList *certListOpt) { NSSCertificate **rvArray = NULL; nssList *certList; if (certListOpt) { certList = certListOpt; } else { certList = nssList_Create(NULL, PR_FALSE); if (!certList) { return NULL; } } PZ_Lock(td->cache->lock); nssHash_Iterate(td->cache->issuerAndSN, cert_iter, (void *)certList); PZ_Unlock(td->cache->lock); if (!certListOpt) { PRUint32 count = nssList_Count(certList); rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count); nssList_GetArray(certList, (void **)rvArray, count); /* array takes the references */ nssList_Destroy(certList); } return rvArray; } NSS_IMPLEMENT void nssTrustDomain_DumpCacheInfo( NSSTrustDomain *td, void (*cert_dump_iter)(const void *, void *, void *), void *arg) { PZ_Lock(td->cache->lock); nssHash_Iterate(td->cache->issuerAndSN, cert_dump_iter, arg); PZ_Unlock(td->cache->lock); }