diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /security/nss/lib/pki/pki3hack.c | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'security/nss/lib/pki/pki3hack.c')
-rw-r--r-- | security/nss/lib/pki/pki3hack.c | 1441 |
1 files changed, 1441 insertions, 0 deletions
diff --git a/security/nss/lib/pki/pki3hack.c b/security/nss/lib/pki/pki3hack.c new file mode 100644 index 000000000..0826b7f5e --- /dev/null +++ b/security/nss/lib/pki/pki3hack.c @@ -0,0 +1,1441 @@ +/* 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/. */ + +/* + * Hacks to integrate NSS 3.4 and NSS 4.0 certificates. + */ + +#ifndef NSSPKI_H +#include "nsspki.h" +#endif /* NSSPKI_H */ + +#ifndef PKI_H +#include "pki.h" +#endif /* PKI_H */ + +#ifndef PKIM_H +#include "pkim.h" +#endif /* PKIM_H */ + +#ifndef DEV_H +#include "dev.h" +#endif /* DEV_H */ + +#ifndef DEVNSS3HACK_H +#include "dev3hack.h" +#endif /* DEVNSS3HACK_H */ + +#ifndef PKINSS3HACK_H +#include "pki3hack.h" +#endif /* PKINSS3HACK_H */ + +#include "secitem.h" +#include "certdb.h" +#include "certt.h" +#include "cert.h" +#include "certi.h" +#include "pk11func.h" +#include "pkistore.h" +#include "secmod.h" +#include "nssrwlk.h" + +NSSTrustDomain *g_default_trust_domain = NULL; + +NSSCryptoContext *g_default_crypto_context = NULL; + +NSSTrustDomain * +STAN_GetDefaultTrustDomain() +{ + return g_default_trust_domain; +} + +NSSCryptoContext * +STAN_GetDefaultCryptoContext() +{ + return g_default_crypto_context; +} + +extern const NSSError NSS_ERROR_ALREADY_INITIALIZED; +extern const NSSError NSS_ERROR_INTERNAL_ERROR; + +NSS_IMPLEMENT PRStatus +STAN_InitTokenForSlotInfo(NSSTrustDomain *td, PK11SlotInfo *slot) +{ + NSSToken *token; + if (!td) { + td = g_default_trust_domain; + if (!td) { + /* we're called while still initting. slot will get added + * appropriately through normal init processes */ + return PR_SUCCESS; + } + } + token = nssToken_CreateFromPK11SlotInfo(td, slot); + PK11Slot_SetNSSToken(slot, token); + /* Don't add nonexistent token to TD's token list */ + if (token) { + NSSRWLock_LockWrite(td->tokensLock); + nssList_Add(td->tokenList, token); + NSSRWLock_UnlockWrite(td->tokensLock); + } + return PR_SUCCESS; +} + +NSS_IMPLEMENT PRStatus +STAN_ResetTokenInterator(NSSTrustDomain *td) +{ + if (!td) { + td = g_default_trust_domain; + if (!td) { + /* we're called while still initting. slot will get added + * appropriately through normal init processes */ + return PR_SUCCESS; + } + } + NSSRWLock_LockWrite(td->tokensLock); + nssListIterator_Destroy(td->tokens); + td->tokens = nssList_CreateIterator(td->tokenList); + NSSRWLock_UnlockWrite(td->tokensLock); + return PR_SUCCESS; +} + +NSS_IMPLEMENT PRStatus +STAN_LoadDefaultNSS3TrustDomain( + void) +{ + NSSTrustDomain *td; + SECMODModuleList *mlp; + SECMODListLock *moduleLock = SECMOD_GetDefaultModuleListLock(); + int i; + + if (g_default_trust_domain || g_default_crypto_context) { + /* Stan is already initialized or a previous shutdown failed. */ + nss_SetError(NSS_ERROR_ALREADY_INITIALIZED); + return PR_FAILURE; + } + td = NSSTrustDomain_Create(NULL, NULL, NULL, NULL); + if (!td) { + return PR_FAILURE; + } + /* + * Deadlock warning: we should never acquire the moduleLock while + * we hold the tokensLock. We can use the NSSRWLock Rank feature to + * guarrentee this. tokensLock have a higher rank than module lock. + */ + td->tokenList = nssList_Create(td->arena, PR_TRUE); + if (!td->tokenList) { + goto loser; + } + SECMOD_GetReadLock(moduleLock); + NSSRWLock_LockWrite(td->tokensLock); + for (mlp = SECMOD_GetDefaultModuleList(); mlp != NULL; mlp = mlp->next) { + for (i = 0; i < mlp->module->slotCount; i++) { + STAN_InitTokenForSlotInfo(td, mlp->module->slots[i]); + } + } + td->tokens = nssList_CreateIterator(td->tokenList); + NSSRWLock_UnlockWrite(td->tokensLock); + SECMOD_ReleaseReadLock(moduleLock); + if (!td->tokens) { + goto loser; + } + g_default_crypto_context = NSSTrustDomain_CreateCryptoContext(td, NULL); + if (!g_default_crypto_context) { + goto loser; + } + g_default_trust_domain = td; + return PR_SUCCESS; + +loser: + NSSTrustDomain_Destroy(td); + return PR_FAILURE; +} + +/* + * must be called holding the ModuleListLock (either read or write). + */ +NSS_IMPLEMENT SECStatus +STAN_AddModuleToDefaultTrustDomain( + SECMODModule *module) +{ + NSSTrustDomain *td; + int i; + td = STAN_GetDefaultTrustDomain(); + for (i = 0; i < module->slotCount; i++) { + STAN_InitTokenForSlotInfo(td, module->slots[i]); + } + STAN_ResetTokenInterator(td); + return SECSuccess; +} + +/* + * must be called holding the ModuleListLock (either read or write). + */ +NSS_IMPLEMENT SECStatus +STAN_RemoveModuleFromDefaultTrustDomain( + SECMODModule *module) +{ + NSSToken *token; + NSSTrustDomain *td; + int i; + td = STAN_GetDefaultTrustDomain(); + NSSRWLock_LockWrite(td->tokensLock); + for (i = 0; i < module->slotCount; i++) { + token = PK11Slot_GetNSSToken(module->slots[i]); + if (token) { + nssToken_NotifyCertsNotVisible(token); + nssList_Remove(td->tokenList, token); + PK11Slot_SetNSSToken(module->slots[i], NULL); + nssToken_Destroy(token); + } + } + nssListIterator_Destroy(td->tokens); + td->tokens = nssList_CreateIterator(td->tokenList); + NSSRWLock_UnlockWrite(td->tokensLock); + return SECSuccess; +} + +NSS_IMPLEMENT PRStatus +STAN_Shutdown() +{ + PRStatus status = PR_SUCCESS; + if (g_default_trust_domain) { + if (NSSTrustDomain_Destroy(g_default_trust_domain) == PR_SUCCESS) { + g_default_trust_domain = NULL; + } else { + status = PR_FAILURE; + } + } + if (g_default_crypto_context) { + if (NSSCryptoContext_Destroy(g_default_crypto_context) == PR_SUCCESS) { + g_default_crypto_context = NULL; + } else { + status = PR_FAILURE; + } + } + return status; +} + +/* this function should not be a hack; it will be needed in 4.0 (rename) */ +NSS_IMPLEMENT NSSItem * +STAN_GetCertIdentifierFromDER(NSSArena *arenaOpt, NSSDER *der) +{ + NSSItem *rvKey; + SECItem secDER; + SECItem secKey = { 0 }; + SECStatus secrv; + PLArenaPool *arena; + + SECITEM_FROM_NSSITEM(&secDER, der); + + /* nss3 call uses nss3 arena's */ + arena = PORT_NewArena(256); + if (!arena) { + return NULL; + } + secrv = CERT_KeyFromDERCert(arena, &secDER, &secKey); + if (secrv != SECSuccess) { + PORT_FreeArena(arena, PR_FALSE); + return NULL; + } + rvKey = nssItem_Create(arenaOpt, NULL, secKey.len, (void *)secKey.data); + PORT_FreeArena(arena, PR_FALSE); + return rvKey; +} + +NSS_IMPLEMENT PRStatus +nssPKIX509_GetIssuerAndSerialFromDER(NSSDER *der, + NSSDER *issuer, NSSDER *serial) +{ + SECItem derCert = { 0 }; + SECItem derIssuer = { 0 }; + SECItem derSerial = { 0 }; + SECStatus secrv; + derCert.data = (unsigned char *)der->data; + derCert.len = der->size; + secrv = CERT_IssuerNameFromDERCert(&derCert, &derIssuer); + if (secrv != SECSuccess) { + return PR_FAILURE; + } + secrv = CERT_SerialNumberFromDERCert(&derCert, &derSerial); + if (secrv != SECSuccess) { + PORT_Free(derSerial.data); + return PR_FAILURE; + } + issuer->data = derIssuer.data; + issuer->size = derIssuer.len; + serial->data = derSerial.data; + serial->size = derSerial.len; + return PR_SUCCESS; +} + +static NSSItem * +nss3certificate_getIdentifier(nssDecodedCert *dc) +{ + NSSItem *rvID; + CERTCertificate *c = (CERTCertificate *)dc->data; + rvID = nssItem_Create(NULL, NULL, c->certKey.len, c->certKey.data); + return rvID; +} + +static void * +nss3certificate_getIssuerIdentifier(nssDecodedCert *dc) +{ + CERTCertificate *c = (CERTCertificate *)dc->data; + return (void *)c->authKeyID; +} + +static nssCertIDMatch +nss3certificate_matchIdentifier(nssDecodedCert *dc, void *id) +{ + CERTCertificate *c = (CERTCertificate *)dc->data; + CERTAuthKeyID *authKeyID = (CERTAuthKeyID *)id; + SECItem skid; + nssCertIDMatch match = nssCertIDMatch_Unknown; + + /* keyIdentifier */ + if (authKeyID->keyID.len > 0 && + CERT_FindSubjectKeyIDExtension(c, &skid) == SECSuccess) { + PRBool skiEqual; + skiEqual = SECITEM_ItemsAreEqual(&authKeyID->keyID, &skid); + PORT_Free(skid.data); + if (skiEqual) { + /* change the state to positive match, but keep going */ + match = nssCertIDMatch_Yes; + } else { + /* exit immediately on failure */ + return nssCertIDMatch_No; + } + } + + /* issuer/serial (treated as pair) */ + if (authKeyID->authCertIssuer) { + SECItem *caName = NULL; + SECItem *caSN = &authKeyID->authCertSerialNumber; + + caName = (SECItem *)CERT_GetGeneralNameByType( + authKeyID->authCertIssuer, + certDirectoryName, PR_TRUE); + if (caName != NULL && + SECITEM_ItemsAreEqual(&c->derIssuer, caName) && + SECITEM_ItemsAreEqual(&c->serialNumber, caSN)) { + match = nssCertIDMatch_Yes; + } else { + match = nssCertIDMatch_Unknown; + } + } + return match; +} + +static PRBool +nss3certificate_isValidIssuer(nssDecodedCert *dc) +{ + CERTCertificate *c = (CERTCertificate *)dc->data; + unsigned int ignore; + return CERT_IsCACert(c, &ignore); +} + +static NSSUsage * +nss3certificate_getUsage(nssDecodedCert *dc) +{ + /* CERTCertificate *c = (CERTCertificate *)dc->data; */ + return NULL; +} + +static PRBool +nss3certificate_isValidAtTime(nssDecodedCert *dc, NSSTime *time) +{ + SECCertTimeValidity validity; + CERTCertificate *c = (CERTCertificate *)dc->data; + validity = CERT_CheckCertValidTimes(c, NSSTime_GetPRTime(time), PR_TRUE); + if (validity == secCertTimeValid) { + return PR_TRUE; + } + return PR_FALSE; +} + +static PRBool +nss3certificate_isNewerThan(nssDecodedCert *dc, nssDecodedCert *cmpdc) +{ + /* I know this isn't right, but this is glue code anyway */ + if (cmpdc->type == dc->type) { + CERTCertificate *certa = (CERTCertificate *)dc->data; + CERTCertificate *certb = (CERTCertificate *)cmpdc->data; + return CERT_IsNewer(certa, certb); + } + return PR_FALSE; +} + +/* CERT_FilterCertListByUsage */ +static PRBool +nss3certificate_matchUsage(nssDecodedCert *dc, const NSSUsage *usage) +{ + CERTCertificate *cc; + unsigned int requiredKeyUsage = 0; + unsigned int requiredCertType = 0; + SECStatus secrv; + PRBool match; + PRBool ca; + + /* This is for NSS 3.3 functions that do not specify a usage */ + if (usage->anyUsage) { + return PR_TRUE; + } + ca = usage->nss3lookingForCA; + secrv = CERT_KeyUsageAndTypeForCertUsage(usage->nss3usage, ca, + &requiredKeyUsage, + &requiredCertType); + if (secrv != SECSuccess) { + return PR_FALSE; + } + cc = (CERTCertificate *)dc->data; + secrv = CERT_CheckKeyUsage(cc, requiredKeyUsage); + match = (PRBool)(secrv == SECSuccess); + if (match) { + unsigned int certType = 0; + if (ca) { + (void)CERT_IsCACert(cc, &certType); + } else { + certType = cc->nsCertType; + } + if (!(certType & requiredCertType)) { + match = PR_FALSE; + } + } + return match; +} + +static PRBool +nss3certificate_isTrustedForUsage(nssDecodedCert *dc, const NSSUsage *usage) +{ + CERTCertificate *cc; + PRBool ca; + SECStatus secrv; + unsigned int requiredFlags; + unsigned int trustFlags; + SECTrustType trustType; + CERTCertTrust trust; + + /* This is for NSS 3.3 functions that do not specify a usage */ + if (usage->anyUsage) { + return PR_FALSE; /* XXX is this right? */ + } + cc = (CERTCertificate *)dc->data; + ca = usage->nss3lookingForCA; + if (!ca) { + PRBool trusted; + unsigned int failedFlags; + secrv = cert_CheckLeafTrust(cc, usage->nss3usage, + &failedFlags, &trusted); + return secrv == SECSuccess && trusted; + } + secrv = CERT_TrustFlagsForCACertUsage(usage->nss3usage, &requiredFlags, + &trustType); + if (secrv != SECSuccess) { + return PR_FALSE; + } + secrv = CERT_GetCertTrust(cc, &trust); + if (secrv != SECSuccess) { + return PR_FALSE; + } + if (trustType == trustTypeNone) { + /* normally trustTypeNone usages accept any of the given trust bits + * being on as acceptable. */ + trustFlags = trust.sslFlags | trust.emailFlags | + trust.objectSigningFlags; + } else { + trustFlags = SEC_GET_TRUST_FLAGS(&trust, trustType); + } + return (trustFlags & requiredFlags) == requiredFlags; +} + +static NSSASCII7 * +nss3certificate_getEmailAddress(nssDecodedCert *dc) +{ + CERTCertificate *cc = (CERTCertificate *)dc->data; + return (cc && cc->emailAddr && cc->emailAddr[0]) + ? (NSSASCII7 *)cc->emailAddr + : NULL; +} + +static PRStatus +nss3certificate_getDERSerialNumber(nssDecodedCert *dc, + NSSDER *serial, NSSArena *arena) +{ + CERTCertificate *cc = (CERTCertificate *)dc->data; + SECItem derSerial = { 0 }; + SECStatus secrv; + secrv = CERT_SerialNumberFromDERCert(&cc->derCert, &derSerial); + if (secrv == SECSuccess) { + (void)nssItem_Create(arena, serial, derSerial.len, derSerial.data); + PORT_Free(derSerial.data); + return PR_SUCCESS; + } + return PR_FAILURE; +} + +/* Returns NULL if "encoding" cannot be decoded. */ +NSS_IMPLEMENT nssDecodedCert * +nssDecodedPKIXCertificate_Create( + NSSArena *arenaOpt, + NSSDER *encoding) +{ + nssDecodedCert *rvDC = NULL; + CERTCertificate *cert; + SECItem secDER; + + SECITEM_FROM_NSSITEM(&secDER, encoding); + cert = CERT_DecodeDERCertificate(&secDER, PR_TRUE, NULL); + if (cert) { + rvDC = nss_ZNEW(arenaOpt, nssDecodedCert); + if (rvDC) { + rvDC->type = NSSCertificateType_PKIX; + rvDC->data = (void *)cert; + rvDC->getIdentifier = nss3certificate_getIdentifier; + rvDC->getIssuerIdentifier = nss3certificate_getIssuerIdentifier; + rvDC->matchIdentifier = nss3certificate_matchIdentifier; + rvDC->isValidIssuer = nss3certificate_isValidIssuer; + rvDC->getUsage = nss3certificate_getUsage; + rvDC->isValidAtTime = nss3certificate_isValidAtTime; + rvDC->isNewerThan = nss3certificate_isNewerThan; + rvDC->matchUsage = nss3certificate_matchUsage; + rvDC->isTrustedForUsage = nss3certificate_isTrustedForUsage; + rvDC->getEmailAddress = nss3certificate_getEmailAddress; + rvDC->getDERSerialNumber = nss3certificate_getDERSerialNumber; + } else { + CERT_DestroyCertificate(cert); + } + } + return rvDC; +} + +static nssDecodedCert * +create_decoded_pkix_cert_from_nss3cert( + NSSArena *arenaOpt, + CERTCertificate *cc) +{ + nssDecodedCert *rvDC = nss_ZNEW(arenaOpt, nssDecodedCert); + if (rvDC) { + rvDC->type = NSSCertificateType_PKIX; + rvDC->data = (void *)cc; + rvDC->getIdentifier = nss3certificate_getIdentifier; + rvDC->getIssuerIdentifier = nss3certificate_getIssuerIdentifier; + rvDC->matchIdentifier = nss3certificate_matchIdentifier; + rvDC->isValidIssuer = nss3certificate_isValidIssuer; + rvDC->getUsage = nss3certificate_getUsage; + rvDC->isValidAtTime = nss3certificate_isValidAtTime; + rvDC->isNewerThan = nss3certificate_isNewerThan; + rvDC->matchUsage = nss3certificate_matchUsage; + rvDC->isTrustedForUsage = nss3certificate_isTrustedForUsage; + rvDC->getEmailAddress = nss3certificate_getEmailAddress; + rvDC->getDERSerialNumber = nss3certificate_getDERSerialNumber; + } + return rvDC; +} + +NSS_IMPLEMENT PRStatus +nssDecodedPKIXCertificate_Destroy(nssDecodedCert *dc) +{ + CERTCertificate *cert = (CERTCertificate *)dc->data; + + /* The decoder may only be half initialized (the case where we find we + * could not decode the certificate). In this case, there is not cert to + * free, just free the dc structure. */ + if (cert) { + PRBool freeSlot = cert->ownSlot; + PK11SlotInfo *slot = cert->slot; + PLArenaPool *arena = cert->arena; + /* zero cert before freeing. Any stale references to this cert + * after this point will probably cause an exception. */ + PORT_Memset(cert, 0, sizeof *cert); + /* free the arena that contains the cert. */ + PORT_FreeArena(arena, PR_FALSE); + if (slot && freeSlot) { + PK11_FreeSlot(slot); + } + } + nss_ZFreeIf(dc); + return PR_SUCCESS; +} + +/* see pk11cert.c:pk11_HandleTrustObject */ +static unsigned int +get_nss3trust_from_nss4trust(nssTrustLevel t) +{ + unsigned int rt = 0; + if (t == nssTrustLevel_Trusted) { + rt |= CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED; + } + if (t == nssTrustLevel_TrustedDelegator) { + rt |= CERTDB_VALID_CA | CERTDB_TRUSTED_CA; + } + if (t == nssTrustLevel_NotTrusted) { + rt |= CERTDB_TERMINAL_RECORD; + } + if (t == nssTrustLevel_ValidDelegator) { + rt |= CERTDB_VALID_CA; + } + return rt; +} + +static CERTCertTrust * +cert_trust_from_stan_trust(NSSTrust *t, PLArenaPool *arena) +{ + CERTCertTrust *rvTrust; + unsigned int client; + if (!t) { + return NULL; + } + rvTrust = PORT_ArenaAlloc(arena, sizeof(CERTCertTrust)); + if (!rvTrust) + return NULL; + rvTrust->sslFlags = get_nss3trust_from_nss4trust(t->serverAuth); + client = get_nss3trust_from_nss4trust(t->clientAuth); + if (client & (CERTDB_TRUSTED_CA | CERTDB_NS_TRUSTED_CA)) { + client &= ~(CERTDB_TRUSTED_CA | CERTDB_NS_TRUSTED_CA); + rvTrust->sslFlags |= CERTDB_TRUSTED_CLIENT_CA; + } + rvTrust->sslFlags |= client; + rvTrust->emailFlags = get_nss3trust_from_nss4trust(t->emailProtection); + rvTrust->objectSigningFlags = get_nss3trust_from_nss4trust(t->codeSigning); + return rvTrust; +} + +CERTCertTrust * +nssTrust_GetCERTCertTrustForCert(NSSCertificate *c, CERTCertificate *cc) +{ + CERTCertTrust *rvTrust = NULL; + NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); + NSSTrust *t; + t = nssTrustDomain_FindTrustForCertificate(td, c); + if (t) { + rvTrust = cert_trust_from_stan_trust(t, cc->arena); + if (!rvTrust) { + nssTrust_Destroy(t); + return NULL; + } + nssTrust_Destroy(t); + } else { + rvTrust = PORT_ArenaAlloc(cc->arena, sizeof(CERTCertTrust)); + if (!rvTrust) { + return NULL; + } + memset(rvTrust, 0, sizeof(*rvTrust)); + } + if (NSSCertificate_IsPrivateKeyAvailable(c, NULL, NULL)) { + rvTrust->sslFlags |= CERTDB_USER; + rvTrust->emailFlags |= CERTDB_USER; + rvTrust->objectSigningFlags |= CERTDB_USER; + } + return rvTrust; +} + +static nssCryptokiInstance * +get_cert_instance(NSSCertificate *c) +{ + nssCryptokiObject *instance, **ci; + nssCryptokiObject **instances = nssPKIObject_GetInstances(&c->object); + if (!instances) { + return NULL; + } + instance = NULL; + for (ci = instances; *ci; ci++) { + if (!instance) { + instance = nssCryptokiObject_Clone(*ci); + } else { + /* This only really works for two instances... But 3.4 can't + * handle more anyway. The logic is, if there are multiple + * instances, prefer the one that is not internal (e.g., on + * a hardware device. + */ + if (PK11_IsInternal(instance->token->pk11slot)) { + nssCryptokiObject_Destroy(instance); + instance = nssCryptokiObject_Clone(*ci); + } + } + } + nssCryptokiObjectArray_Destroy(instances); + return instance; +} + +char * +STAN_GetCERTCertificateNameForInstance( + PLArenaPool *arenaOpt, + NSSCertificate *c, + nssCryptokiInstance *instance) +{ + NSSCryptoContext *context = c->object.cryptoContext; + PRStatus nssrv; + int nicklen, tokenlen, len; + NSSUTF8 *tokenName = NULL; + NSSUTF8 *stanNick = NULL; + char *nickname = NULL; + char *nick; + + if (instance) { + stanNick = instance->label; + } else if (context) { + stanNick = c->object.tempName; + } + if (stanNick) { + /* fill other fields needed by NSS3 functions using CERTCertificate */ + if (instance && (!PK11_IsInternalKeySlot(instance->token->pk11slot) || + PORT_Strchr(stanNick, ':') != NULL)) { + tokenName = nssToken_GetName(instance->token); + tokenlen = nssUTF8_Size(tokenName, &nssrv); + } else { + /* don't use token name for internal slot; 3.3 didn't */ + tokenlen = 0; + } + nicklen = nssUTF8_Size(stanNick, &nssrv); + len = tokenlen + nicklen; + if (arenaOpt) { + nickname = PORT_ArenaAlloc(arenaOpt, len); + } else { + nickname = PORT_Alloc(len); + } + nick = nickname; + if (tokenName) { + memcpy(nick, tokenName, tokenlen - 1); + nick += tokenlen - 1; + *nick++ = ':'; + } + memcpy(nick, stanNick, nicklen - 1); + nickname[len - 1] = '\0'; + } + return nickname; +} + +char * +STAN_GetCERTCertificateName(PLArenaPool *arenaOpt, NSSCertificate *c) +{ + char *result; + nssCryptokiInstance *instance = get_cert_instance(c); + /* It's OK to call this function, even if instance is NULL */ + result = STAN_GetCERTCertificateNameForInstance(arenaOpt, c, instance); + if (instance) + nssCryptokiObject_Destroy(instance); + return result; +} + +static void +fill_CERTCertificateFields(NSSCertificate *c, CERTCertificate *cc, PRBool forced) +{ + CERTCertTrust *trust = NULL; + NSSTrust *nssTrust; + NSSCryptoContext *context = c->object.cryptoContext; + nssCryptokiInstance *instance; + NSSUTF8 *stanNick = NULL; + + /* We are holding the base class object's lock on entry of this function + * This lock protects writes to fields of the CERTCertificate . + * It is also needed by some functions to compute values such as trust. + */ + instance = get_cert_instance(c); + + if (instance) { + stanNick = instance->label; + } else if (context) { + stanNick = c->object.tempName; + } + /* fill other fields needed by NSS3 functions using CERTCertificate */ + if ((!cc->nickname && stanNick) || forced) { + PRStatus nssrv; + int nicklen, tokenlen, len; + NSSUTF8 *tokenName = NULL; + char *nick; + if (instance && + (!PK11_IsInternalKeySlot(instance->token->pk11slot) || + (stanNick && PORT_Strchr(stanNick, ':') != NULL))) { + tokenName = nssToken_GetName(instance->token); + tokenlen = nssUTF8_Size(tokenName, &nssrv); + } else { + /* don't use token name for internal slot; 3.3 didn't */ + tokenlen = 0; + } + if (stanNick) { + nicklen = nssUTF8_Size(stanNick, &nssrv); + len = tokenlen + nicklen; + nick = PORT_ArenaAlloc(cc->arena, len); + if (tokenName) { + memcpy(nick, tokenName, tokenlen - 1); + nick[tokenlen - 1] = ':'; + memcpy(nick + tokenlen, stanNick, nicklen - 1); + } else { + memcpy(nick, stanNick, nicklen - 1); + } + nick[len - 1] = '\0'; + cc->nickname = nick; + } else { + cc->nickname = NULL; + } + } + if (context) { + /* trust */ + nssTrust = nssCryptoContext_FindTrustForCertificate(context, c); + if (!nssTrust) { + /* chicken and egg issue: + * + * c->issuer and c->serial are empty at this point, but + * nssTrustDomain_FindTrustForCertificate use them to look up + * up the trust object, so we point them to cc->derIssuer and + * cc->serialNumber. + * + * Our caller will fill these in with proper arena copies when we + * return. */ + c->issuer.data = cc->derIssuer.data; + c->issuer.size = cc->derIssuer.len; + c->serial.data = cc->serialNumber.data; + c->serial.size = cc->serialNumber.len; + nssTrust = nssTrustDomain_FindTrustForCertificate(context->td, c); + } + if (nssTrust) { + trust = cert_trust_from_stan_trust(nssTrust, cc->arena); + if (trust) { + /* we should destroy cc->trust before replacing it, but it's + allocated in cc->arena, so memory growth will occur on each + refresh */ + CERT_LockCertTrust(cc); + cc->trust = trust; + CERT_UnlockCertTrust(cc); + } + nssTrust_Destroy(nssTrust); + } + } else if (instance) { + /* slot */ + if (cc->slot != instance->token->pk11slot) { + if (cc->slot) { + PK11_FreeSlot(cc->slot); + } + cc->slot = PK11_ReferenceSlot(instance->token->pk11slot); + } + cc->ownSlot = PR_TRUE; + /* pkcs11ID */ + cc->pkcs11ID = instance->handle; + /* trust */ + trust = nssTrust_GetCERTCertTrustForCert(c, cc); + if (trust) { + /* we should destroy cc->trust before replacing it, but it's + allocated in cc->arena, so memory growth will occur on each + refresh */ + CERT_LockCertTrust(cc); + cc->trust = trust; + CERT_UnlockCertTrust(cc); + } + } + if (instance) { + nssCryptokiObject_Destroy(instance); + } + /* database handle is now the trust domain */ + cc->dbhandle = c->object.trustDomain; + /* subjectList ? */ + /* istemp and isperm are supported in NSS 3.4 */ + cc->istemp = PR_FALSE; /* CERT_NewTemp will override this */ + cc->isperm = PR_TRUE; /* by default */ + /* pointer back */ + cc->nssCertificate = c; + if (trust) { + /* force the cert type to be recomputed to include trust info */ + PRUint32 nsCertType = cert_ComputeCertType(cc); + + /* Assert that it is safe to cast &cc->nsCertType to "PRInt32 *" */ + PORT_Assert(sizeof(cc->nsCertType) == sizeof(PRInt32)); + PR_ATOMIC_SET((PRInt32 *)&cc->nsCertType, nsCertType); + } +} + +static CERTCertificate * +stan_GetCERTCertificate(NSSCertificate *c, PRBool forceUpdate) +{ + nssDecodedCert *dc = NULL; + CERTCertificate *cc = NULL; + CERTCertTrust certTrust; + + /* make sure object does not go away until we finish */ + nssPKIObject_AddRef(&c->object); + nssPKIObject_Lock(&c->object); + + dc = c->decoding; + if (!dc) { + dc = nssDecodedPKIXCertificate_Create(NULL, &c->encoding); + if (!dc) { + goto loser; + } + cc = (CERTCertificate *)dc->data; + PORT_Assert(cc); /* software error */ + if (!cc) { + nssDecodedPKIXCertificate_Destroy(dc); + nss_SetError(NSS_ERROR_INTERNAL_ERROR); + goto loser; + } + PORT_Assert(!c->decoding); + if (!c->decoding) { + c->decoding = dc; + } else { + /* this should never happen. Fail. */ + nssDecodedPKIXCertificate_Destroy(dc); + nss_SetError(NSS_ERROR_INTERNAL_ERROR); + goto loser; + } + } + cc = (CERTCertificate *)dc->data; + PORT_Assert(cc); + if (!cc) { + nss_SetError(NSS_ERROR_INTERNAL_ERROR); + goto loser; + } + if (!cc->nssCertificate || forceUpdate) { + fill_CERTCertificateFields(c, cc, forceUpdate); + } else if (CERT_GetCertTrust(cc, &certTrust) != SECSuccess && + !c->object.cryptoContext) { + /* if it's a perm cert, it might have been stored before the + * trust, so look for the trust again. But a temp cert can be + * ignored. + */ + CERTCertTrust *trust = NULL; + trust = nssTrust_GetCERTCertTrustForCert(c, cc); + + CERT_LockCertTrust(cc); + cc->trust = trust; + CERT_UnlockCertTrust(cc); + } + +loser: + nssPKIObject_Unlock(&c->object); + nssPKIObject_Destroy(&c->object); + return cc; +} + +NSS_IMPLEMENT CERTCertificate * +STAN_ForceCERTCertificateUpdate(NSSCertificate *c) +{ + if (c->decoding) { + return stan_GetCERTCertificate(c, PR_TRUE); + } + return NULL; +} + +NSS_IMPLEMENT CERTCertificate * +STAN_GetCERTCertificate(NSSCertificate *c) +{ + return stan_GetCERTCertificate(c, PR_FALSE); +} +/* + * many callers of STAN_GetCERTCertificate() intend that + * the CERTCertificate returned inherits the reference to the + * NSSCertificate. For these callers it's convenient to have + * this function 'own' the reference and either return a valid + * CERTCertificate structure which inherits the reference or + * destroy the reference to NSSCertificate and returns NULL. + */ +NSS_IMPLEMENT CERTCertificate * +STAN_GetCERTCertificateOrRelease(NSSCertificate *c) +{ + CERTCertificate *nss3cert = stan_GetCERTCertificate(c, PR_FALSE); + if (!nss3cert) { + nssCertificate_Destroy(c); + } + return nss3cert; +} + +static nssTrustLevel +get_stan_trust(unsigned int t, PRBool isClientAuth) +{ + if (isClientAuth) { + if (t & CERTDB_TRUSTED_CLIENT_CA) { + return nssTrustLevel_TrustedDelegator; + } + } else { + if (t & CERTDB_TRUSTED_CA || t & CERTDB_NS_TRUSTED_CA) { + return nssTrustLevel_TrustedDelegator; + } + } + if (t & CERTDB_TRUSTED) { + return nssTrustLevel_Trusted; + } + if (t & CERTDB_TERMINAL_RECORD) { + return nssTrustLevel_NotTrusted; + } + if (t & CERTDB_VALID_CA) { + return nssTrustLevel_ValidDelegator; + } + return nssTrustLevel_MustVerify; +} + +NSS_EXTERN NSSCertificate * +STAN_GetNSSCertificate(CERTCertificate *cc) +{ + NSSCertificate *c; + nssCryptokiInstance *instance; + nssPKIObject *pkiob; + NSSArena *arena; + c = cc->nssCertificate; + if (c) { + return c; + } + /* i don't think this should happen. but if it can, need to create + * NSSCertificate from CERTCertificate values here. */ + /* Yup, it can happen. */ + arena = NSSArena_Create(); + if (!arena) { + return NULL; + } + c = nss_ZNEW(arena, NSSCertificate); + if (!c) { + nssArena_Destroy(arena); + return NULL; + } + NSSITEM_FROM_SECITEM(&c->encoding, &cc->derCert); + c->type = NSSCertificateType_PKIX; + pkiob = nssPKIObject_Create(arena, NULL, cc->dbhandle, NULL, nssPKIMonitor); + if (!pkiob) { + nssArena_Destroy(arena); + return NULL; + } + c->object = *pkiob; + nssItem_Create(arena, + &c->issuer, cc->derIssuer.len, cc->derIssuer.data); + nssItem_Create(arena, + &c->subject, cc->derSubject.len, cc->derSubject.data); + if (PR_TRUE) { + /* CERTCertificate stores serial numbers decoded. I need the DER + * here. sigh. + */ + SECItem derSerial; + SECStatus secrv; + secrv = CERT_SerialNumberFromDERCert(&cc->derCert, &derSerial); + if (secrv == SECFailure) { + nssArena_Destroy(arena); + return NULL; + } + nssItem_Create(arena, &c->serial, derSerial.len, derSerial.data); + PORT_Free(derSerial.data); + } + if (cc->emailAddr && cc->emailAddr[0]) { + c->email = nssUTF8_Create(arena, + nssStringType_PrintableString, + (NSSUTF8 *)cc->emailAddr, + PORT_Strlen(cc->emailAddr)); + } + if (cc->slot) { + instance = nss_ZNEW(arena, nssCryptokiInstance); + if (!instance) { + nssArena_Destroy(arena); + return NULL; + } + instance->token = nssToken_AddRef(PK11Slot_GetNSSToken(cc->slot)); + instance->handle = cc->pkcs11ID; + instance->isTokenObject = PR_TRUE; + if (cc->nickname) { + instance->label = nssUTF8_Create(arena, + nssStringType_UTF8String, + (NSSUTF8 *)cc->nickname, + PORT_Strlen(cc->nickname)); + } + nssPKIObject_AddInstance(&c->object, instance); + } + c->decoding = create_decoded_pkix_cert_from_nss3cert(NULL, cc); + cc->nssCertificate = c; + return c; +} + +static NSSToken * +stan_GetTrustToken( + NSSCertificate *c) +{ + NSSToken *ttok = NULL; + NSSToken *rtok = NULL; + NSSToken *tok = NULL; + nssCryptokiObject **ip; + nssCryptokiObject **instances = nssPKIObject_GetInstances(&c->object); + if (!instances) { + return PR_FALSE; + } + for (ip = instances; *ip; ip++) { + nssCryptokiObject *instance = *ip; + nssCryptokiObject *to = + nssToken_FindTrustForCertificate(instance->token, NULL, + &c->encoding, &c->issuer, &c->serial, + nssTokenSearchType_TokenOnly); + NSSToken *ctok = instance->token; + PRBool ro = PK11_IsReadOnly(ctok->pk11slot); + + if (to) { + nssCryptokiObject_Destroy(to); + ttok = ctok; + if (!ro) { + break; + } + } else { + if (!rtok && ro) { + rtok = ctok; + } + if (!tok && !ro) { + tok = ctok; + } + } + } + nssCryptokiObjectArray_Destroy(instances); + return ttok ? ttok : (tok ? tok : rtok); +} + +NSS_EXTERN PRStatus +STAN_ChangeCertTrust(CERTCertificate *cc, CERTCertTrust *trust) +{ + PRStatus nssrv; + NSSCertificate *c = STAN_GetNSSCertificate(cc); + NSSToken *tok; + NSSTrustDomain *td; + NSSTrust *nssTrust; + NSSArena *arena; + CERTCertTrust *oldTrust; + CERTCertTrust *newTrust; + nssListIterator *tokens; + PRBool moving_object; + nssCryptokiObject *newInstance; + nssPKIObject *pkiob; + + if (c == NULL) { + return PR_FAILURE; + } + oldTrust = nssTrust_GetCERTCertTrustForCert(c, cc); + if (oldTrust) { + if (memcmp(oldTrust, trust, sizeof(CERTCertTrust)) == 0) { + /* ... and the new trust is no different, done) */ + return PR_SUCCESS; + } else { + /* take over memory already allocated in cc's arena */ + newTrust = oldTrust; + } + } else { + newTrust = PORT_ArenaAlloc(cc->arena, sizeof(CERTCertTrust)); + } + memcpy(newTrust, trust, sizeof(CERTCertTrust)); + CERT_LockCertTrust(cc); + cc->trust = newTrust; + CERT_UnlockCertTrust(cc); + /* Set the NSSCerticate's trust */ + arena = nssArena_Create(); + if (!arena) + return PR_FAILURE; + nssTrust = nss_ZNEW(arena, NSSTrust); + if (!nssTrust) { + nssArena_Destroy(arena); + return PR_FAILURE; + } + pkiob = nssPKIObject_Create(arena, NULL, cc->dbhandle, NULL, nssPKILock); + if (!pkiob) { + nssArena_Destroy(arena); + return PR_FAILURE; + } + nssTrust->object = *pkiob; + nssTrust->certificate = c; + nssTrust->serverAuth = get_stan_trust(trust->sslFlags, PR_FALSE); + nssTrust->clientAuth = get_stan_trust(trust->sslFlags, PR_TRUE); + nssTrust->emailProtection = get_stan_trust(trust->emailFlags, PR_FALSE); + nssTrust->codeSigning = get_stan_trust(trust->objectSigningFlags, PR_FALSE); + nssTrust->stepUpApproved = + (PRBool)(trust->sslFlags & CERTDB_GOVT_APPROVED_CA); + if (c->object.cryptoContext != NULL) { + /* The cert is in a context, set the trust there */ + NSSCryptoContext *cc = c->object.cryptoContext; + nssrv = nssCryptoContext_ImportTrust(cc, nssTrust); + if (nssrv != PR_SUCCESS) { + goto done; + } + if (c->object.numInstances == 0) { + /* The context is the only instance, finished */ + goto done; + } + } + td = STAN_GetDefaultTrustDomain(); + tok = stan_GetTrustToken(c); + moving_object = PR_FALSE; + if (tok && PK11_IsReadOnly(tok->pk11slot)) { + NSSRWLock_LockRead(td->tokensLock); + tokens = nssList_CreateIterator(td->tokenList); + if (!tokens) { + nssrv = PR_FAILURE; + NSSRWLock_UnlockRead(td->tokensLock); + goto done; + } + for (tok = (NSSToken *)nssListIterator_Start(tokens); + tok != (NSSToken *)NULL; + tok = (NSSToken *)nssListIterator_Next(tokens)) { + if (!PK11_IsReadOnly(tok->pk11slot)) + break; + } + nssListIterator_Finish(tokens); + nssListIterator_Destroy(tokens); + NSSRWLock_UnlockRead(td->tokensLock); + moving_object = PR_TRUE; + } + if (tok) { + if (moving_object) { + /* this is kind of hacky. the softoken needs the cert + * object in order to store trust. forcing it to be perm + */ + NSSUTF8 *nickname = nssCertificate_GetNickname(c, NULL); + NSSASCII7 *email = NULL; + + if (PK11_IsInternal(tok->pk11slot)) { + email = c->email; + } + newInstance = nssToken_ImportCertificate(tok, NULL, + NSSCertificateType_PKIX, + &c->id, + nickname, + &c->encoding, + &c->issuer, + &c->subject, + &c->serial, + email, + PR_TRUE); + nss_ZFreeIf(nickname); + nickname = NULL; + if (!newInstance) { + nssrv = PR_FAILURE; + goto done; + } + nssPKIObject_AddInstance(&c->object, newInstance); + } + newInstance = nssToken_ImportTrust(tok, NULL, &c->encoding, + &c->issuer, &c->serial, + nssTrust->serverAuth, + nssTrust->clientAuth, + nssTrust->codeSigning, + nssTrust->emailProtection, + nssTrust->stepUpApproved, PR_TRUE); + /* If the selected token can't handle trust, dump the trust on + * the internal token */ + if (!newInstance && !PK11_IsInternalKeySlot(tok->pk11slot)) { + PK11SlotInfo *slot = PK11_GetInternalKeySlot(); + NSSUTF8 *nickname = nssCertificate_GetNickname(c, NULL); + NSSASCII7 *email = c->email; + tok = PK11Slot_GetNSSToken(slot); + PK11_FreeSlot(slot); + + newInstance = nssToken_ImportCertificate(tok, NULL, + NSSCertificateType_PKIX, + &c->id, + nickname, + &c->encoding, + &c->issuer, + &c->subject, + &c->serial, + email, + PR_TRUE); + nss_ZFreeIf(nickname); + nickname = NULL; + if (!newInstance) { + nssrv = PR_FAILURE; + goto done; + } + nssPKIObject_AddInstance(&c->object, newInstance); + newInstance = nssToken_ImportTrust(tok, NULL, &c->encoding, + &c->issuer, &c->serial, + nssTrust->serverAuth, + nssTrust->clientAuth, + nssTrust->codeSigning, + nssTrust->emailProtection, + nssTrust->stepUpApproved, PR_TRUE); + } + if (newInstance) { + nssCryptokiObject_Destroy(newInstance); + nssrv = PR_SUCCESS; + } else { + nssrv = PR_FAILURE; + } + } else { + nssrv = PR_FAILURE; + } +done: + (void)nssTrust_Destroy(nssTrust); + return nssrv; +} + +/* +** Delete trust objects matching the given slot. +** Returns error if a device fails to delete. +** +** This function has the side effect of moving the +** surviving entries to the front of the object list +** and nullifying the rest. +*/ +static PRStatus +DeleteCertTrustMatchingSlot(PK11SlotInfo *pk11slot, nssPKIObject *tObject) +{ + int numNotDestroyed = 0; /* the ones skipped plus the failures */ + int failureCount = 0; /* actual deletion failures by devices */ + unsigned int index; + + nssPKIObject_AddRef(tObject); + nssPKIObject_Lock(tObject); + /* Keep going even if a module fails to delete. */ + for (index = 0; index < tObject->numInstances; index++) { + nssCryptokiObject *instance = tObject->instances[index]; + if (!instance) { + continue; + } + + /* ReadOnly and not matched treated the same */ + if (PK11_IsReadOnly(instance->token->pk11slot) || + pk11slot != instance->token->pk11slot) { + tObject->instances[numNotDestroyed++] = instance; + continue; + } + + /* Here we have found a matching one */ + tObject->instances[index] = NULL; + if (nssToken_DeleteStoredObject(instance) == PR_SUCCESS) { + nssCryptokiObject_Destroy(instance); + } else { + tObject->instances[numNotDestroyed++] = instance; + failureCount++; + } + } + if (numNotDestroyed == 0) { + nss_ZFreeIf(tObject->instances); + tObject->numInstances = 0; + } else { + tObject->numInstances = numNotDestroyed; + } + + nssPKIObject_Unlock(tObject); + nssPKIObject_Destroy(tObject); + + return failureCount == 0 ? PR_SUCCESS : PR_FAILURE; +} + +/* +** Delete trust objects matching the slot of the given certificate. +** Returns an error if any device fails to delete. +*/ +NSS_EXTERN PRStatus +STAN_DeleteCertTrustMatchingSlot(NSSCertificate *c) +{ + PRStatus nssrv = PR_SUCCESS; + + unsigned int i; + nssPKIObject *tobject = NULL; + nssPKIObject *cobject = &c->object; + + NSSTrustDomain *td = STAN_GetDefaultTrustDomain(); + NSSTrust *nssTrust = nssTrustDomain_FindTrustForCertificate(td, c); + if (!nssTrust) { + return PR_FAILURE; + } + + tobject = &nssTrust->object; + + /* Iterate through the cert and trust object instances looking for + * those with matching pk11 slots to delete. Even if some device + * can't delete we keep going. Keeping a status variable for the + * loop so that once it's failed the other gets set. + */ + NSSRWLock_LockRead(td->tokensLock); + nssPKIObject_AddRef(cobject); + nssPKIObject_Lock(cobject); + for (i = 0; i < cobject->numInstances; i++) { + nssCryptokiObject *cInstance = cobject->instances[i]; + if (cInstance && !PK11_IsReadOnly(cInstance->token->pk11slot)) { + PRStatus status; + if (!tobject->numInstances || !tobject->instances) + continue; + status = DeleteCertTrustMatchingSlot(cInstance->token->pk11slot, tobject); + if (status == PR_FAILURE) { + /* set the outer one but keep going */ + nssrv = PR_FAILURE; + } + } + } + nssTrust_Destroy(nssTrust); + nssPKIObject_Unlock(cobject); + nssPKIObject_Destroy(cobject); + NSSRWLock_UnlockRead(td->tokensLock); + return nssrv; +} + +/* CERT_TraversePermCertsForSubject */ +NSS_IMPLEMENT PRStatus +nssTrustDomain_TraverseCertificatesBySubject( + NSSTrustDomain *td, + NSSDER *subject, + PRStatus (*callback)(NSSCertificate *c, void *arg), + void *arg) +{ + PRStatus nssrv = PR_SUCCESS; + NSSArena *tmpArena; + NSSCertificate **subjectCerts; + NSSCertificate *c; + PRIntn i; + tmpArena = NSSArena_Create(); + if (!tmpArena) { + return PR_FAILURE; + } + subjectCerts = NSSTrustDomain_FindCertificatesBySubject(td, subject, NULL, + 0, tmpArena); + if (subjectCerts) { + for (i = 0, c = subjectCerts[i]; c; i++) { + nssrv = callback(c, arg); + if (nssrv != PR_SUCCESS) + break; + } + } + nssArena_Destroy(tmpArena); + return nssrv; +} + +/* CERT_TraversePermCertsForNickname */ +NSS_IMPLEMENT PRStatus +nssTrustDomain_TraverseCertificatesByNickname( + NSSTrustDomain *td, + NSSUTF8 *nickname, + PRStatus (*callback)(NSSCertificate *c, void *arg), + void *arg) +{ + PRStatus nssrv = PR_SUCCESS; + NSSArena *tmpArena; + NSSCertificate **nickCerts; + NSSCertificate *c; + PRIntn i; + tmpArena = NSSArena_Create(); + if (!tmpArena) { + return PR_FAILURE; + } + nickCerts = NSSTrustDomain_FindCertificatesByNickname(td, nickname, NULL, + 0, tmpArena); + if (nickCerts) { + for (i = 0, c = nickCerts[i]; c; i++) { + nssrv = callback(c, arg); + if (nssrv != PR_SUCCESS) + break; + } + } + nssArena_Destroy(tmpArena); + return nssrv; +} + +static void +cert_dump_iter(const void *k, void *v, void *a) +{ + NSSCertificate *c = (NSSCertificate *)k; + CERTCertificate *cert = STAN_GetCERTCertificate(c); + printf("[%2d] \"%s\"\n", c->object.refCount, cert->subjectName); +} + +void +nss_DumpCertificateCacheInfo() +{ + NSSTrustDomain *td; + NSSCryptoContext *cc; + td = STAN_GetDefaultTrustDomain(); + cc = STAN_GetDefaultCryptoContext(); + printf("\n\nCertificates in the cache:\n"); + nssTrustDomain_DumpCacheInfo(td, cert_dump_iter, NULL); + printf("\n\nCertificates in the temporary store:\n"); + if (cc->certStore) { + nssCertificateStore_DumpStoreInfo(cc->certStore, cert_dump_iter, NULL); + } +} |