andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public andre@0: * License, v. 2.0. If a copy of the MPL was not distributed with this andre@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ andre@0: andre@0: #ifndef PKIM_H andre@0: #include "pkim.h" andre@0: #endif /* PKIM_H */ andre@0: andre@0: #ifndef PKIT_H andre@0: #include "pkit.h" andre@0: #endif /* PKIT_H */ andre@0: andre@0: #ifndef NSSPKI_H andre@0: #include "nsspki.h" andre@0: #endif /* NSSPKI_H */ andre@0: andre@0: #ifndef PKI_H andre@0: #include "pki.h" andre@0: #endif /* PKI_H */ andre@0: andre@0: #ifndef NSSBASE_H andre@0: #include "nssbase.h" andre@0: #endif /* NSSBASE_H */ andre@0: andre@0: #ifndef BASE_H andre@0: #include "base.h" andre@0: #endif /* BASE_H */ andre@0: andre@0: #include "cert.h" andre@0: #include "dev.h" andre@0: #include "pki3hack.h" andre@0: andre@0: #ifdef DEBUG_CACHE andre@0: static PRLogModuleInfo *s_log = NULL; andre@0: #endif andre@0: andre@0: #ifdef DEBUG_CACHE andre@0: static void log_item_dump(const char *msg, NSSItem *it) andre@0: { andre@0: char buf[33]; andre@0: int i, j; andre@0: for (i=0; i<10 && isize; i++) { andre@0: sprintf(&buf[2*i], "%02X", ((PRUint8 *)it->data)[i]); andre@0: } andre@0: if (it->size>10) { andre@0: sprintf(&buf[2*i], ".."); andre@0: i += 1; andre@0: for (j=it->size-1; i<=16 && j>10; i++, j--) { andre@0: sprintf(&buf[2*i], "%02X", ((PRUint8 *)it->data)[j]); andre@0: } andre@0: } andre@0: PR_LOG(s_log, PR_LOG_DEBUG, ("%s: %s", msg, buf)); andre@0: } andre@0: #endif andre@0: andre@0: #ifdef DEBUG_CACHE andre@0: static void log_cert_ref(const char *msg, NSSCertificate *c) andre@0: { andre@0: PR_LOG(s_log, PR_LOG_DEBUG, ("%s: %s", msg, andre@0: (c->nickname) ? c->nickname : c->email)); andre@0: log_item_dump("\tserial", &c->serial); andre@0: log_item_dump("\tsubject", &c->subject); andre@0: } andre@0: #endif andre@0: andre@0: /* Certificate cache routines */ andre@0: andre@0: /* XXX andre@0: * Locking is not handled well at all. A single, global lock with sub-locks andre@0: * in the collection types. Cleanup needed. andre@0: */ andre@0: andre@0: /* should it live in its own arena? */ andre@0: struct nssTDCertificateCacheStr andre@0: { andre@0: PZLock *lock; andre@0: NSSArena *arena; andre@0: nssHash *issuerAndSN; andre@0: nssHash *subject; andre@0: nssHash *nickname; andre@0: nssHash *email; andre@0: }; andre@0: andre@0: struct cache_entry_str andre@0: { andre@0: union { andre@0: NSSCertificate *cert; andre@0: nssList *list; andre@0: void *value; andre@0: } entry; andre@0: PRUint32 hits; andre@0: PRTime lastHit; andre@0: NSSArena *arena; andre@0: NSSUTF8 *nickname; andre@0: }; andre@0: andre@0: typedef struct cache_entry_str cache_entry; andre@0: andre@0: static cache_entry * andre@0: new_cache_entry(NSSArena *arena, void *value, PRBool ownArena) andre@0: { andre@0: cache_entry *ce = nss_ZNEW(arena, cache_entry); andre@0: if (ce) { andre@0: ce->entry.value = value; andre@0: ce->hits = 1; andre@0: ce->lastHit = PR_Now(); andre@0: if (ownArena) { andre@0: ce->arena = arena; andre@0: } andre@0: ce->nickname = NULL; andre@0: } andre@0: return ce; andre@0: } andre@0: andre@0: /* this should not be exposed in a header, but is here to keep the above andre@0: * types/functions static andre@0: */ andre@0: NSS_IMPLEMENT PRStatus andre@0: nssTrustDomain_InitializeCache ( andre@0: NSSTrustDomain *td, andre@0: PRUint32 cacheSize andre@0: ) andre@0: { andre@0: NSSArena *arena; andre@0: nssTDCertificateCache *cache = td->cache; andre@0: #ifdef DEBUG_CACHE andre@0: s_log = PR_NewLogModule("nss_cache"); andre@0: PR_ASSERT(s_log); andre@0: #endif andre@0: PR_ASSERT(!cache); andre@0: arena = nssArena_Create(); andre@0: if (!arena) { andre@0: return PR_FAILURE; andre@0: } andre@0: cache = nss_ZNEW(arena, nssTDCertificateCache); andre@0: if (!cache) { andre@0: nssArena_Destroy(arena); andre@0: return PR_FAILURE; andre@0: } andre@0: cache->lock = PZ_NewLock(nssILockCache); andre@0: if (!cache->lock) { andre@0: nssArena_Destroy(arena); andre@0: return PR_FAILURE; andre@0: } andre@0: /* Create the issuer and serial DER --> certificate hash */ andre@0: cache->issuerAndSN = nssHash_CreateCertificate(arena, cacheSize); andre@0: if (!cache->issuerAndSN) { andre@0: goto loser; andre@0: } andre@0: /* Create the subject DER --> subject list hash */ andre@0: cache->subject = nssHash_CreateItem(arena, cacheSize); andre@0: if (!cache->subject) { andre@0: goto loser; andre@0: } andre@0: /* Create the nickname --> subject list hash */ andre@0: cache->nickname = nssHash_CreateString(arena, cacheSize); andre@0: if (!cache->nickname) { andre@0: goto loser; andre@0: } andre@0: /* Create the email --> list of subject lists hash */ andre@0: cache->email = nssHash_CreateString(arena, cacheSize); andre@0: if (!cache->email) { andre@0: goto loser; andre@0: } andre@0: cache->arena = arena; andre@0: td->cache = cache; andre@0: #ifdef DEBUG_CACHE andre@0: PR_LOG(s_log, PR_LOG_DEBUG, ("Cache initialized.")); andre@0: #endif andre@0: return PR_SUCCESS; andre@0: loser: andre@0: PZ_DestroyLock(cache->lock); andre@0: nssArena_Destroy(arena); andre@0: td->cache = NULL; andre@0: #ifdef DEBUG_CACHE andre@0: PR_LOG(s_log, PR_LOG_DEBUG, ("Cache initialization failed.")); andre@0: #endif andre@0: return PR_FAILURE; andre@0: } andre@0: andre@0: /* The entries of the hashtable are currently dependent on the certificate(s) andre@0: * that produced them. That is, the entries will be freed when the cert is andre@0: * released from the cache. If there are certs in the cache at any time, andre@0: * including shutdown, the hash table entries will hold memory. In order for andre@0: * clean shutdown, it is necessary for there to be no certs in the cache. andre@0: */ andre@0: andre@0: extern const NSSError NSS_ERROR_INTERNAL_ERROR; andre@0: extern const NSSError NSS_ERROR_BUSY; andre@0: andre@0: NSS_IMPLEMENT PRStatus andre@0: nssTrustDomain_DestroyCache ( andre@0: NSSTrustDomain *td andre@0: ) andre@0: { andre@0: if (!td->cache) { andre@0: nss_SetError(NSS_ERROR_INTERNAL_ERROR); andre@0: return PR_FAILURE; andre@0: } andre@0: if (nssHash_Count(td->cache->issuerAndSN) > 0) { andre@0: nss_SetError(NSS_ERROR_BUSY); andre@0: return PR_FAILURE; andre@0: } andre@0: PZ_DestroyLock(td->cache->lock); andre@0: nssHash_Destroy(td->cache->issuerAndSN); andre@0: nssHash_Destroy(td->cache->subject); andre@0: nssHash_Destroy(td->cache->nickname); andre@0: nssHash_Destroy(td->cache->email); andre@0: nssArena_Destroy(td->cache->arena); andre@0: td->cache = NULL; andre@0: #ifdef DEBUG_CACHE andre@0: PR_LOG(s_log, PR_LOG_DEBUG, ("Cache destroyed.")); andre@0: #endif andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: static PRStatus andre@0: remove_issuer_and_serial_entry ( andre@0: nssTDCertificateCache *cache, andre@0: NSSCertificate *cert andre@0: ) andre@0: { andre@0: /* Remove the cert from the issuer/serial hash */ andre@0: nssHash_Remove(cache->issuerAndSN, cert); andre@0: #ifdef DEBUG_CACHE andre@0: log_cert_ref("removed issuer/sn", cert); andre@0: #endif andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: static PRStatus andre@0: remove_subject_entry ( andre@0: nssTDCertificateCache *cache, andre@0: NSSCertificate *cert, andre@0: nssList **subjectList, andre@0: NSSUTF8 **nickname, andre@0: NSSArena **arena andre@0: ) andre@0: { andre@0: PRStatus nssrv; andre@0: cache_entry *ce; andre@0: *subjectList = NULL; andre@0: *arena = NULL; andre@0: /* Get the subject list for the cert's subject */ andre@0: ce = (cache_entry *)nssHash_Lookup(cache->subject, &cert->subject); andre@0: if (ce) { andre@0: /* Remove the cert from the subject hash */ andre@0: nssList_Remove(ce->entry.list, cert); andre@0: *subjectList = ce->entry.list; andre@0: *nickname = ce->nickname; andre@0: *arena = ce->arena; andre@0: nssrv = PR_SUCCESS; andre@0: #ifdef DEBUG_CACHE andre@0: log_cert_ref("removed cert", cert); andre@0: log_item_dump("from subject list", &cert->subject); andre@0: #endif andre@0: } else { andre@0: nssrv = PR_FAILURE; andre@0: } andre@0: return nssrv; andre@0: } andre@0: andre@0: static PRStatus andre@0: remove_nickname_entry ( andre@0: nssTDCertificateCache *cache, andre@0: NSSUTF8 *nickname, andre@0: nssList *subjectList andre@0: ) andre@0: { andre@0: PRStatus nssrv; andre@0: if (nickname) { andre@0: nssHash_Remove(cache->nickname, nickname); andre@0: nssrv = PR_SUCCESS; andre@0: #ifdef DEBUG_CACHE andre@0: PR_LOG(s_log, PR_LOG_DEBUG, ("removed nickname %s", nickname)); andre@0: #endif andre@0: } else { andre@0: nssrv = PR_FAILURE; andre@0: } andre@0: return nssrv; andre@0: } andre@0: andre@0: static PRStatus andre@0: remove_email_entry ( andre@0: nssTDCertificateCache *cache, andre@0: NSSCertificate *cert, andre@0: nssList *subjectList andre@0: ) andre@0: { andre@0: PRStatus nssrv = PR_FAILURE; andre@0: cache_entry *ce; andre@0: /* Find the subject list in the email hash */ andre@0: if (cert->email) { andre@0: ce = (cache_entry *)nssHash_Lookup(cache->email, cert->email); andre@0: if (ce) { andre@0: nssList *subjects = ce->entry.list; andre@0: /* Remove the subject list from the email hash */ andre@0: nssList_Remove(subjects, subjectList); andre@0: #ifdef DEBUG_CACHE andre@0: log_item_dump("removed subject list", &cert->subject); andre@0: PR_LOG(s_log, PR_LOG_DEBUG, ("for email %s", cert->email)); andre@0: #endif andre@0: if (nssList_Count(subjects) == 0) { andre@0: /* No more subject lists for email, delete list and andre@0: * remove hash entry andre@0: */ andre@0: (void)nssList_Destroy(subjects); andre@0: nssHash_Remove(cache->email, cert->email); andre@0: /* there are no entries left for this address, free space andre@0: * used for email entries andre@0: */ andre@0: nssArena_Destroy(ce->arena); andre@0: #ifdef DEBUG_CACHE andre@0: PR_LOG(s_log, PR_LOG_DEBUG, ("removed email %s", cert->email)); andre@0: #endif andre@0: } andre@0: nssrv = PR_SUCCESS; andre@0: } andre@0: } andre@0: return nssrv; andre@0: } andre@0: andre@0: NSS_IMPLEMENT void andre@0: nssTrustDomain_RemoveCertFromCacheLOCKED ( andre@0: NSSTrustDomain *td, andre@0: NSSCertificate *cert andre@0: ) andre@0: { andre@0: nssList *subjectList; andre@0: cache_entry *ce; andre@0: NSSArena *arena; andre@0: NSSUTF8 *nickname; andre@0: andre@0: #ifdef DEBUG_CACHE andre@0: log_cert_ref("attempt to remove cert", cert); andre@0: #endif andre@0: ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, cert); andre@0: if (!ce || ce->entry.cert != cert) { andre@0: /* If it's not in the cache, or a different cert is (this is really andre@0: * for safety reasons, though it shouldn't happen), do nothing andre@0: */ andre@0: #ifdef DEBUG_CACHE andre@0: PR_LOG(s_log, PR_LOG_DEBUG, ("but it wasn't in the cache")); andre@0: #endif andre@0: return; andre@0: } andre@0: (void)remove_issuer_and_serial_entry(td->cache, cert); andre@0: (void)remove_subject_entry(td->cache, cert, &subjectList, andre@0: &nickname, &arena); andre@0: if (nssList_Count(subjectList) == 0) { andre@0: (void)remove_nickname_entry(td->cache, nickname, subjectList); andre@0: (void)remove_email_entry(td->cache, cert, subjectList); andre@0: (void)nssList_Destroy(subjectList); andre@0: nssHash_Remove(td->cache->subject, &cert->subject); andre@0: /* there are no entries left for this subject, free the space used andre@0: * for both the nickname and subject entries andre@0: */ andre@0: if (arena) { andre@0: nssArena_Destroy(arena); andre@0: } andre@0: } andre@0: } andre@0: andre@0: NSS_IMPLEMENT void andre@0: nssTrustDomain_LockCertCache ( andre@0: NSSTrustDomain *td andre@0: ) andre@0: { andre@0: PZ_Lock(td->cache->lock); andre@0: } andre@0: andre@0: NSS_IMPLEMENT void andre@0: nssTrustDomain_UnlockCertCache ( andre@0: NSSTrustDomain *td andre@0: ) andre@0: { andre@0: PZ_Unlock(td->cache->lock); andre@0: } andre@0: andre@0: struct token_cert_dtor { andre@0: NSSToken *token; andre@0: nssTDCertificateCache *cache; andre@0: NSSCertificate **certs; andre@0: PRUint32 numCerts, arrSize; andre@0: }; andre@0: andre@0: static void andre@0: remove_token_certs(const void *k, void *v, void *a) andre@0: { andre@0: NSSCertificate *c = (NSSCertificate *)k; andre@0: nssPKIObject *object = &c->object; andre@0: struct token_cert_dtor *dtor = a; andre@0: PRUint32 i; andre@0: nssPKIObject_Lock(object); andre@0: for (i=0; inumInstances; i++) { andre@0: if (object->instances[i]->token == dtor->token) { andre@0: nssCryptokiObject_Destroy(object->instances[i]); andre@0: object->instances[i] = object->instances[object->numInstances-1]; andre@0: object->instances[object->numInstances-1] = NULL; andre@0: object->numInstances--; andre@0: dtor->certs[dtor->numCerts++] = c; andre@0: if (dtor->numCerts == dtor->arrSize) { andre@0: dtor->arrSize *= 2; andre@0: dtor->certs = nss_ZREALLOCARRAY(dtor->certs, andre@0: NSSCertificate *, andre@0: dtor->arrSize); andre@0: } andre@0: break; andre@0: } andre@0: } andre@0: nssPKIObject_Unlock(object); andre@0: return; andre@0: } andre@0: andre@0: /* andre@0: * Remove all certs for the given token from the cache. This is andre@0: * needed if the token is removed. andre@0: */ andre@0: NSS_IMPLEMENT PRStatus andre@0: nssTrustDomain_RemoveTokenCertsFromCache ( andre@0: NSSTrustDomain *td, andre@0: NSSToken *token andre@0: ) andre@0: { andre@0: NSSCertificate **certs; andre@0: PRUint32 i, arrSize = 10; andre@0: struct token_cert_dtor dtor; andre@0: certs = nss_ZNEWARRAY(NULL, NSSCertificate *, arrSize); andre@0: if (!certs) { andre@0: return PR_FAILURE; andre@0: } andre@0: dtor.cache = td->cache; andre@0: dtor.token = token; andre@0: dtor.certs = certs; andre@0: dtor.numCerts = 0; andre@0: dtor.arrSize = arrSize; andre@0: PZ_Lock(td->cache->lock); andre@0: nssHash_Iterate(td->cache->issuerAndSN, remove_token_certs, (void *)&dtor); andre@0: for (i=0; iobject.numInstances == 0) { andre@0: nssTrustDomain_RemoveCertFromCacheLOCKED(td, dtor.certs[i]); andre@0: dtor.certs[i] = NULL; /* skip this cert in the second for loop */ andre@0: } andre@0: } andre@0: PZ_Unlock(td->cache->lock); andre@0: for (i=0; i 0) { andre@0: cached = nss_ZNEWARRAY(NULL, NSSCertificate *, count + 1); andre@0: if (!cached) { andre@0: nssList_Destroy(certList); andre@0: return PR_FAILURE; andre@0: } andre@0: nssList_GetArray(certList, (void **)cached, count); andre@0: for (cp = cached; *cp; cp++) { andre@0: nssCryptokiObject *instance; andre@0: NSSCertificate *c = *cp; andre@0: nssTokenSearchType tokenOnly = nssTokenSearchType_TokenOnly; andre@0: instance = nssToken_FindCertificateByIssuerAndSerialNumber( andre@0: token, andre@0: NULL, andre@0: &c->issuer, andre@0: &c->serial, andre@0: tokenOnly, andre@0: NULL); andre@0: if (instance) { andre@0: nssPKIObject_AddInstance(&c->object, instance); andre@0: STAN_ForceCERTCertificateUpdate(c); andre@0: } andre@0: } andre@0: nssCertificateArray_Destroy(cached); andre@0: } andre@0: nssList_Destroy(certList); andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: static PRStatus andre@0: add_issuer_and_serial_entry ( andre@0: NSSArena *arena, andre@0: nssTDCertificateCache *cache, andre@0: NSSCertificate *cert andre@0: ) andre@0: { andre@0: cache_entry *ce; andre@0: ce = new_cache_entry(arena, (void *)cert, PR_FALSE); andre@0: #ifdef DEBUG_CACHE andre@0: log_cert_ref("added to issuer/sn", cert); andre@0: #endif andre@0: return nssHash_Add(cache->issuerAndSN, cert, (void *)ce); andre@0: } andre@0: andre@0: static PRStatus andre@0: add_subject_entry ( andre@0: NSSArena *arena, andre@0: nssTDCertificateCache *cache, andre@0: NSSCertificate *cert, andre@0: NSSUTF8 *nickname, andre@0: nssList **subjectList andre@0: ) andre@0: { andre@0: PRStatus nssrv; andre@0: nssList *list; andre@0: cache_entry *ce; andre@0: *subjectList = NULL; /* this is only set if a new one is created */ andre@0: ce = (cache_entry *)nssHash_Lookup(cache->subject, &cert->subject); andre@0: if (ce) { andre@0: ce->hits++; andre@0: ce->lastHit = PR_Now(); andre@0: /* The subject is already in, add this cert to the list */ andre@0: nssrv = nssList_AddUnique(ce->entry.list, cert); andre@0: #ifdef DEBUG_CACHE andre@0: log_cert_ref("added to existing subject list", cert); andre@0: #endif andre@0: } else { andre@0: NSSDER *subject; andre@0: /* Create a new subject list for the subject */ andre@0: list = nssList_Create(arena, PR_FALSE); andre@0: if (!list) { andre@0: return PR_FAILURE; andre@0: } andre@0: ce = new_cache_entry(arena, (void *)list, PR_TRUE); andre@0: if (!ce) { andre@0: return PR_FAILURE; andre@0: } andre@0: if (nickname) { andre@0: ce->nickname = nssUTF8_Duplicate(nickname, arena); andre@0: } andre@0: nssList_SetSortFunction(list, nssCertificate_SubjectListSort); andre@0: /* Add the cert entry to this list of subjects */ andre@0: nssrv = nssList_AddUnique(list, cert); andre@0: if (nssrv != PR_SUCCESS) { andre@0: return nssrv; andre@0: } andre@0: /* Add the subject list to the cache */ andre@0: subject = nssItem_Duplicate(&cert->subject, arena, NULL); andre@0: if (!subject) { andre@0: return PR_FAILURE; andre@0: } andre@0: nssrv = nssHash_Add(cache->subject, subject, ce); andre@0: if (nssrv != PR_SUCCESS) { andre@0: return nssrv; andre@0: } andre@0: *subjectList = list; andre@0: #ifdef DEBUG_CACHE andre@0: log_cert_ref("created subject list", cert); andre@0: #endif andre@0: } andre@0: return nssrv; andre@0: } andre@0: andre@0: static PRStatus andre@0: add_nickname_entry ( andre@0: NSSArena *arena, andre@0: nssTDCertificateCache *cache, andre@0: NSSUTF8 *certNickname, andre@0: nssList *subjectList andre@0: ) andre@0: { andre@0: PRStatus nssrv = PR_SUCCESS; andre@0: cache_entry *ce; andre@0: ce = (cache_entry *)nssHash_Lookup(cache->nickname, certNickname); andre@0: if (ce) { andre@0: /* This is a collision. A nickname entry already exists for this andre@0: * subject, but a subject entry didn't. This would imply there are andre@0: * two subjects using the same nickname, which is not allowed. andre@0: */ andre@0: return PR_FAILURE; andre@0: } else { andre@0: NSSUTF8 *nickname; andre@0: ce = new_cache_entry(arena, subjectList, PR_FALSE); andre@0: if (!ce) { andre@0: return PR_FAILURE; andre@0: } andre@0: nickname = nssUTF8_Duplicate(certNickname, arena); andre@0: if (!nickname) { andre@0: return PR_FAILURE; andre@0: } andre@0: nssrv = nssHash_Add(cache->nickname, nickname, ce); andre@0: #ifdef DEBUG_CACHE andre@0: log_cert_ref("created nickname for", cert); andre@0: #endif andre@0: } andre@0: return nssrv; andre@0: } andre@0: andre@0: static PRStatus andre@0: add_email_entry ( andre@0: nssTDCertificateCache *cache, andre@0: NSSCertificate *cert, andre@0: nssList *subjectList andre@0: ) andre@0: { andre@0: PRStatus nssrv = PR_SUCCESS; andre@0: nssList *subjects; andre@0: cache_entry *ce; andre@0: ce = (cache_entry *)nssHash_Lookup(cache->email, cert->email); andre@0: if (ce) { andre@0: /* Already have an entry for this email address, but not subject */ andre@0: subjects = ce->entry.list; andre@0: nssrv = nssList_AddUnique(subjects, subjectList); andre@0: ce->hits++; andre@0: ce->lastHit = PR_Now(); andre@0: #ifdef DEBUG_CACHE andre@0: log_cert_ref("added subject to email for", cert); andre@0: #endif andre@0: } else { andre@0: NSSASCII7 *email; andre@0: NSSArena *arena; andre@0: arena = nssArena_Create(); andre@0: if (!arena) { andre@0: return PR_FAILURE; andre@0: } andre@0: /* Create a new list of subject lists, add this subject */ andre@0: subjects = nssList_Create(arena, PR_TRUE); andre@0: if (!subjects) { andre@0: nssArena_Destroy(arena); andre@0: return PR_FAILURE; andre@0: } andre@0: /* Add the new subject to the list */ andre@0: nssrv = nssList_AddUnique(subjects, subjectList); andre@0: if (nssrv != PR_SUCCESS) { andre@0: nssArena_Destroy(arena); andre@0: return nssrv; andre@0: } andre@0: /* Add the new entry to the cache */ andre@0: ce = new_cache_entry(arena, (void *)subjects, PR_TRUE); andre@0: if (!ce) { andre@0: nssArena_Destroy(arena); andre@0: return PR_FAILURE; andre@0: } andre@0: email = nssUTF8_Duplicate(cert->email, arena); andre@0: if (!email) { andre@0: nssArena_Destroy(arena); andre@0: return PR_FAILURE; andre@0: } andre@0: nssrv = nssHash_Add(cache->email, email, ce); andre@0: if (nssrv != PR_SUCCESS) { andre@0: nssArena_Destroy(arena); andre@0: return nssrv; andre@0: } andre@0: #ifdef DEBUG_CACHE andre@0: log_cert_ref("created email for", cert); andre@0: #endif andre@0: } andre@0: return nssrv; andre@0: } andre@0: andre@0: extern const NSSError NSS_ERROR_CERTIFICATE_IN_CACHE; andre@0: andre@0: static void andre@0: remove_object_instances ( andre@0: nssPKIObject *object, andre@0: nssCryptokiObject **instances, andre@0: int numInstances andre@0: ) andre@0: { andre@0: int i; andre@0: andre@0: for (i = 0; i < numInstances; i++) { andre@0: nssPKIObject_RemoveInstanceForToken(object, instances[i]->token); andre@0: } andre@0: } andre@0: andre@0: static SECStatus andre@0: merge_object_instances ( andre@0: nssPKIObject *to, andre@0: nssPKIObject *from andre@0: ) andre@0: { andre@0: nssCryptokiObject **instances, **ci; andre@0: int i; andre@0: SECStatus rv = SECSuccess; andre@0: andre@0: instances = nssPKIObject_GetInstances(from); andre@0: if (instances == NULL) { andre@0: return SECFailure; andre@0: } andre@0: for (ci = instances, i = 0; *ci; ci++, i++) { andre@0: nssCryptokiObject *instance = nssCryptokiObject_Clone(*ci); andre@0: if (instance) { andre@0: if (nssPKIObject_AddInstance(to, instance) == PR_SUCCESS) { andre@0: continue; andre@0: } andre@0: nssCryptokiObject_Destroy(instance); andre@0: } andre@0: remove_object_instances(to, instances, i); andre@0: rv = SECFailure; andre@0: break; andre@0: } andre@0: nssCryptokiObjectArray_Destroy(instances); andre@0: return rv; andre@0: } andre@0: andre@0: static NSSCertificate * andre@0: add_cert_to_cache ( andre@0: NSSTrustDomain *td, andre@0: NSSCertificate *cert andre@0: ) andre@0: { andre@0: NSSArena *arena = NULL; andre@0: nssList *subjectList = NULL; andre@0: PRStatus nssrv; andre@0: PRUint32 added = 0; andre@0: cache_entry *ce; andre@0: NSSCertificate *rvCert = NULL; andre@0: NSSUTF8 *certNickname = nssCertificate_GetNickname(cert, NULL); andre@0: andre@0: PZ_Lock(td->cache->lock); andre@0: /* If it exists in the issuer/serial hash, it's already in all */ andre@0: ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, cert); andre@0: if (ce) { andre@0: ce->hits++; andre@0: ce->lastHit = PR_Now(); andre@0: rvCert = nssCertificate_AddRef(ce->entry.cert); andre@0: #ifdef DEBUG_CACHE andre@0: log_cert_ref("attempted to add cert already in cache", cert); andre@0: #endif andre@0: PZ_Unlock(td->cache->lock); andre@0: nss_ZFreeIf(certNickname); andre@0: /* collision - somebody else already added the cert andre@0: * to the cache before this thread got around to it. andre@0: */ andre@0: /* merge the instances of the cert */ andre@0: if (merge_object_instances(&rvCert->object, &cert->object) andre@0: != SECSuccess) { andre@0: nssCertificate_Destroy(rvCert); andre@0: return NULL; andre@0: } andre@0: STAN_ForceCERTCertificateUpdate(rvCert); andre@0: nssCertificate_Destroy(cert); andre@0: return rvCert; andre@0: } andre@0: /* create a new cache entry for this cert within the cert's arena*/ andre@0: nssrv = add_issuer_and_serial_entry(cert->object.arena, td->cache, cert); andre@0: if (nssrv != PR_SUCCESS) { andre@0: goto loser; andre@0: } andre@0: added++; andre@0: /* create an arena for the nickname and subject entries */ andre@0: arena = nssArena_Create(); andre@0: if (!arena) { andre@0: goto loser; andre@0: } andre@0: /* create a new subject list for this cert, or add to existing */ andre@0: nssrv = add_subject_entry(arena, td->cache, cert, andre@0: certNickname, &subjectList); andre@0: if (nssrv != PR_SUCCESS) { andre@0: goto loser; andre@0: } andre@0: added++; andre@0: /* If a new subject entry was created, also need nickname and/or email */ andre@0: if (subjectList != NULL) { andre@0: PRBool handle = PR_FALSE; andre@0: if (certNickname) { andre@0: nssrv = add_nickname_entry(arena, td->cache, andre@0: certNickname, subjectList); andre@0: if (nssrv != PR_SUCCESS) { andre@0: goto loser; andre@0: } andre@0: handle = PR_TRUE; andre@0: added++; andre@0: } andre@0: if (cert->email) { andre@0: nssrv = add_email_entry(td->cache, cert, subjectList); andre@0: if (nssrv != PR_SUCCESS) { andre@0: goto loser; andre@0: } andre@0: handle = PR_TRUE; andre@0: added += 2; andre@0: } andre@0: #ifdef nodef andre@0: /* I think either a nickname or email address must be associated andre@0: * with the cert. However, certs are passed to NewTemp without andre@0: * either. This worked in the old code, so it must work now. andre@0: */ andre@0: if (!handle) { andre@0: /* Require either nickname or email handle */ andre@0: nssrv = PR_FAILURE; andre@0: goto loser; andre@0: } andre@0: #endif andre@0: } else { andre@0: /* A new subject entry was not created. arena is unused. */ andre@0: nssArena_Destroy(arena); andre@0: } andre@0: rvCert = cert; andre@0: PZ_Unlock(td->cache->lock); andre@0: nss_ZFreeIf(certNickname); andre@0: return rvCert; andre@0: loser: andre@0: nss_ZFreeIf(certNickname); andre@0: certNickname = NULL; andre@0: /* Remove any handles that have been created */ andre@0: subjectList = NULL; andre@0: if (added >= 1) { andre@0: (void)remove_issuer_and_serial_entry(td->cache, cert); andre@0: } andre@0: if (added >= 2) { andre@0: (void)remove_subject_entry(td->cache, cert, &subjectList, andre@0: &certNickname, &arena); andre@0: } andre@0: if (added == 3 || added == 5) { andre@0: (void)remove_nickname_entry(td->cache, certNickname, subjectList); andre@0: } andre@0: if (added >= 4) { andre@0: (void)remove_email_entry(td->cache, cert, subjectList); andre@0: } andre@0: if (subjectList) { andre@0: nssHash_Remove(td->cache->subject, &cert->subject); andre@0: nssList_Destroy(subjectList); andre@0: } andre@0: if (arena) { andre@0: nssArena_Destroy(arena); andre@0: } andre@0: PZ_Unlock(td->cache->lock); andre@0: return NULL; andre@0: } andre@0: andre@0: NSS_IMPLEMENT PRStatus andre@0: nssTrustDomain_AddCertsToCache ( andre@0: NSSTrustDomain *td, andre@0: NSSCertificate **certs, andre@0: PRUint32 numCerts andre@0: ) andre@0: { andre@0: PRUint32 i; andre@0: NSSCertificate *c; andre@0: for (i=0; icache->lock); andre@0: ce = (cache_entry *)nssHash_Lookup(td->cache->subject, subject); andre@0: if (ce) { andre@0: ce->hits++; andre@0: ce->lastHit = PR_Now(); andre@0: #ifdef DEBUG_CACHE andre@0: PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); andre@0: #endif andre@0: rvArray = collect_subject_certs(ce->entry.list, certListOpt); andre@0: } andre@0: PZ_Unlock(td->cache->lock); andre@0: return rvArray; andre@0: } andre@0: andre@0: /* andre@0: * Find all cached certs with this label. andre@0: */ andre@0: NSS_IMPLEMENT NSSCertificate ** andre@0: nssTrustDomain_GetCertsForNicknameFromCache ( andre@0: NSSTrustDomain *td, andre@0: const NSSUTF8 *nickname, andre@0: nssList *certListOpt andre@0: ) andre@0: { andre@0: NSSCertificate **rvArray = NULL; andre@0: cache_entry *ce; andre@0: #ifdef DEBUG_CACHE andre@0: PR_LOG(s_log, PR_LOG_DEBUG, ("looking for cert by nick %s", nickname)); andre@0: #endif andre@0: PZ_Lock(td->cache->lock); andre@0: ce = (cache_entry *)nssHash_Lookup(td->cache->nickname, nickname); andre@0: if (ce) { andre@0: ce->hits++; andre@0: ce->lastHit = PR_Now(); andre@0: #ifdef DEBUG_CACHE andre@0: PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); andre@0: #endif andre@0: rvArray = collect_subject_certs(ce->entry.list, certListOpt); andre@0: } andre@0: PZ_Unlock(td->cache->lock); andre@0: return rvArray; andre@0: } andre@0: andre@0: /* andre@0: * Find all cached certs with this email address. andre@0: */ andre@0: NSS_IMPLEMENT NSSCertificate ** andre@0: nssTrustDomain_GetCertsForEmailAddressFromCache ( andre@0: NSSTrustDomain *td, andre@0: NSSASCII7 *email, andre@0: nssList *certListOpt andre@0: ) andre@0: { andre@0: NSSCertificate **rvArray = NULL; andre@0: cache_entry *ce; andre@0: nssList *collectList = NULL; andre@0: nssListIterator *iter = NULL; andre@0: nssList *subjectList; andre@0: #ifdef DEBUG_CACHE andre@0: PR_LOG(s_log, PR_LOG_DEBUG, ("looking for cert by email %s", email)); andre@0: #endif andre@0: PZ_Lock(td->cache->lock); andre@0: ce = (cache_entry *)nssHash_Lookup(td->cache->email, email); andre@0: if (ce) { andre@0: ce->hits++; andre@0: ce->lastHit = PR_Now(); andre@0: #ifdef DEBUG_CACHE andre@0: PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); andre@0: #endif andre@0: /* loop over subject lists and get refs for certs */ andre@0: if (certListOpt) { andre@0: collectList = certListOpt; andre@0: } else { andre@0: collectList = nssList_Create(NULL, PR_FALSE); andre@0: if (!collectList) { andre@0: PZ_Unlock(td->cache->lock); andre@0: return NULL; andre@0: } andre@0: } andre@0: iter = nssList_CreateIterator(ce->entry.list); andre@0: if (!iter) { andre@0: PZ_Unlock(td->cache->lock); andre@0: if (!certListOpt) { andre@0: nssList_Destroy(collectList); andre@0: } andre@0: return NULL; andre@0: } andre@0: for (subjectList = (nssList *)nssListIterator_Start(iter); andre@0: subjectList != (nssList *)NULL; andre@0: subjectList = (nssList *)nssListIterator_Next(iter)) { andre@0: (void)collect_subject_certs(subjectList, collectList); andre@0: } andre@0: nssListIterator_Finish(iter); andre@0: nssListIterator_Destroy(iter); andre@0: } andre@0: PZ_Unlock(td->cache->lock); andre@0: if (!certListOpt && collectList) { andre@0: PRUint32 count = nssList_Count(collectList); andre@0: rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count); andre@0: if (rvArray) { andre@0: nssList_GetArray(collectList, (void **)rvArray, count); andre@0: } andre@0: nssList_Destroy(collectList); andre@0: } andre@0: return rvArray; andre@0: } andre@0: andre@0: /* andre@0: * Look for a specific cert in the cache andre@0: */ andre@0: NSS_IMPLEMENT NSSCertificate * andre@0: nssTrustDomain_GetCertForIssuerAndSNFromCache ( andre@0: NSSTrustDomain *td, andre@0: NSSDER *issuer, andre@0: NSSDER *serial andre@0: ) andre@0: { andre@0: NSSCertificate certkey; andre@0: NSSCertificate *rvCert = NULL; andre@0: cache_entry *ce; andre@0: certkey.issuer.data = issuer->data; andre@0: certkey.issuer.size = issuer->size; andre@0: certkey.serial.data = serial->data; andre@0: certkey.serial.size = serial->size; andre@0: #ifdef DEBUG_CACHE andre@0: log_item_dump("looking for cert by issuer/sn, issuer", issuer); andre@0: log_item_dump(" serial", serial); andre@0: #endif andre@0: PZ_Lock(td->cache->lock); andre@0: ce = (cache_entry *)nssHash_Lookup(td->cache->issuerAndSN, &certkey); andre@0: if (ce) { andre@0: ce->hits++; andre@0: ce->lastHit = PR_Now(); andre@0: rvCert = nssCertificate_AddRef(ce->entry.cert); andre@0: #ifdef DEBUG_CACHE andre@0: PR_LOG(s_log, PR_LOG_DEBUG, ("... found, %d hits", ce->hits)); andre@0: #endif andre@0: } andre@0: PZ_Unlock(td->cache->lock); andre@0: return rvCert; andre@0: } andre@0: andre@0: static PRStatus andre@0: issuer_and_serial_from_encoding ( andre@0: NSSBER *encoding, andre@0: NSSDER *issuer, andre@0: NSSDER *serial andre@0: ) andre@0: { andre@0: SECItem derCert, derIssuer, derSerial; andre@0: SECStatus secrv; andre@0: derCert.data = (unsigned char *)encoding->data; andre@0: derCert.len = encoding->size; andre@0: secrv = CERT_IssuerNameFromDERCert(&derCert, &derIssuer); andre@0: if (secrv != SECSuccess) { andre@0: return PR_FAILURE; andre@0: } andre@0: secrv = CERT_SerialNumberFromDERCert(&derCert, &derSerial); andre@0: if (secrv != SECSuccess) { andre@0: return PR_FAILURE; andre@0: } andre@0: issuer->data = derIssuer.data; andre@0: issuer->size = derIssuer.len; andre@0: serial->data = derSerial.data; andre@0: serial->size = derSerial.len; andre@0: return PR_SUCCESS; andre@0: } andre@0: andre@0: /* andre@0: * Look for a specific cert in the cache andre@0: */ andre@0: NSS_IMPLEMENT NSSCertificate * andre@0: nssTrustDomain_GetCertByDERFromCache ( andre@0: NSSTrustDomain *td, andre@0: NSSDER *der andre@0: ) andre@0: { andre@0: PRStatus nssrv = PR_FAILURE; andre@0: NSSDER issuer, serial; andre@0: NSSCertificate *rvCert; andre@0: nssrv = issuer_and_serial_from_encoding(der, &issuer, &serial); andre@0: if (nssrv != PR_SUCCESS) { andre@0: return NULL; andre@0: } andre@0: #ifdef DEBUG_CACHE andre@0: log_item_dump("looking for cert by DER", der); andre@0: #endif andre@0: rvCert = nssTrustDomain_GetCertForIssuerAndSNFromCache(td, andre@0: &issuer, &serial); andre@0: PORT_Free(issuer.data); andre@0: PORT_Free(serial.data); andre@0: return rvCert; andre@0: } andre@0: andre@0: static void cert_iter(const void *k, void *v, void *a) andre@0: { andre@0: nssList *certList = (nssList *)a; andre@0: NSSCertificate *c = (NSSCertificate *)k; andre@0: nssList_Add(certList, nssCertificate_AddRef(c)); andre@0: } andre@0: andre@0: NSS_EXTERN NSSCertificate ** andre@0: nssTrustDomain_GetCertsFromCache ( andre@0: NSSTrustDomain *td, andre@0: nssList *certListOpt andre@0: ) andre@0: { andre@0: NSSCertificate **rvArray = NULL; andre@0: nssList *certList; andre@0: if (certListOpt) { andre@0: certList = certListOpt; andre@0: } else { andre@0: certList = nssList_Create(NULL, PR_FALSE); andre@0: if (!certList) { andre@0: return NULL; andre@0: } andre@0: } andre@0: PZ_Lock(td->cache->lock); andre@0: nssHash_Iterate(td->cache->issuerAndSN, cert_iter, (void *)certList); andre@0: PZ_Unlock(td->cache->lock); andre@0: if (!certListOpt) { andre@0: PRUint32 count = nssList_Count(certList); andre@0: rvArray = nss_ZNEWARRAY(NULL, NSSCertificate *, count); andre@0: nssList_GetArray(certList, (void **)rvArray, count); andre@0: /* array takes the references */ andre@0: nssList_Destroy(certList); andre@0: } andre@0: return rvArray; andre@0: } andre@0: andre@0: NSS_IMPLEMENT void andre@0: nssTrustDomain_DumpCacheInfo ( andre@0: NSSTrustDomain *td, andre@0: void (* cert_dump_iter)(const void *, void *, void *), andre@0: void *arg andre@0: ) andre@0: { andre@0: PZ_Lock(td->cache->lock); andre@0: nssHash_Iterate(td->cache->issuerAndSN, cert_dump_iter, arg); andre@0: PZ_Unlock(td->cache->lock); andre@0: }