summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/pki/pki3hack.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /security/nss/lib/pki/pki3hack.c
parentInitial commit. (diff)
downloadfirefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz
firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/lib/pki/pki3hack.c')
-rw-r--r--security/nss/lib/pki/pki3hack.c1510
1 files changed, 1510 insertions, 0 deletions
diff --git a/security/nss/lib/pki/pki3hack.c b/security/nss/lib/pki/pki3hack.c
new file mode 100644
index 0000000000..5556cd1769
--- /dev/null
+++ b/security/nss/lib/pki/pki3hack.c
@@ -0,0 +1,1510 @@
+/* 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);
+ if (token) {
+ /* PK11Slot_SetNSSToken increments the refcount on |token| to 2 */
+ PK11Slot_SetNSSToken(slot, token);
+
+ /* we give our reference to |td->tokenList| */
+ NSSRWLock_LockWrite(td->tokensLock);
+ nssList_Add(td->tokenList, token);
+ NSSRWLock_UnlockWrite(td->tokensLock);
+ } else {
+ PK11Slot_SetNSSToken(slot, NULL);
+ }
+ 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();
+ for (i = 0; i < module->slotCount; i++) {
+ token = PK11Slot_GetNSSToken(module->slots[i]);
+ if (token) {
+ nssToken_NotifyCertsNotVisible(token);
+ NSSRWLock_LockWrite(td->tokensLock);
+ nssList_Remove(td->tokenList, token);
+ NSSRWLock_UnlockWrite(td->tokensLock);
+ PK11Slot_SetNSSToken(module->slots[i], NULL);
+ (void)nssToken_Destroy(token); /* for the |td->tokenList| reference */
+ (void)nssToken_Destroy(token); /* for our PK11Slot_GetNSSToken reference */
+ }
+ }
+ NSSRWLock_LockWrite(td->tokensLock);
+ 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);
+ }
+ /* Read the distrust fields from a nssckbi/builtins certificate and
+ * fill the fields in CERTCertificate structure when any valid date
+ * is found. */
+ if (PK11_IsReadOnly(cc->slot) && PK11_HasRootCerts(cc->slot)) {
+ /* The values are hard-coded and readonly. Read just once. */
+ if (cc->distrust == NULL) {
+ CERTCertDistrust distrustModel;
+ SECItem model = { siUTCTime, NULL, 0 };
+ distrustModel.serverDistrustAfter = model;
+ distrustModel.emailDistrustAfter = model;
+ SECStatus rServer = PK11_ReadAttribute(
+ cc->slot, cc->pkcs11ID, CKA_NSS_SERVER_DISTRUST_AFTER,
+ cc->arena, &distrustModel.serverDistrustAfter);
+ SECStatus rEmail = PK11_ReadAttribute(
+ cc->slot, cc->pkcs11ID, CKA_NSS_EMAIL_DISTRUST_AFTER,
+ cc->arena, &distrustModel.emailDistrustAfter);
+ /* Only allocate the Distrust structure if a valid date is found.
+ * The result length of a encoded valid timestamp is exactly 13 */
+ const unsigned int kDistrustFieldSize = 13;
+ if ((rServer == SECSuccess && rEmail == SECSuccess) &&
+ (distrustModel.serverDistrustAfter.len == kDistrustFieldSize ||
+ distrustModel.emailDistrustAfter.len == kDistrustFieldSize)) {
+ CERTCertDistrust *tmpPtr = PORT_ArenaAlloc(
+ cc->arena, sizeof(CERTCertDistrust));
+ PORT_Memcpy(tmpPtr, &distrustModel,
+ sizeof(CERTCertDistrust));
+ cc->distrust = tmpPtr;
+ }
+ }
+ }
+ }
+ 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 */
+ CERT_LockCertTempPerm(cc);
+ cc->istemp = PR_FALSE; /* CERT_NewTemp will override this */
+ cc->isperm = PR_TRUE; /* by default */
+ /* pointer back */
+ cc->nssCertificate = c;
+ CERT_UnlockCertTempPerm(cc);
+ 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;
+ }
+ CERT_LockCertTempPerm(cc);
+ NSSCertificate *nssCert = cc->nssCertificate;
+ CERT_UnlockCertTempPerm(cc);
+ if (!nssCert || forceUpdate) {
+ fill_CERTCertificateFields(c, cc, forceUpdate);
+ } else if (CERT_GetCertTrust(cc, &certTrust) != SECSuccess) {
+ CERTCertTrust *trust;
+ if (!c->object.cryptoContext) {
+ /* If it's a perm cert, it might have been stored before the
+ * trust, so look for the trust again.
+ */
+ trust = nssTrust_GetCERTCertTrustForCert(c, cc);
+ } else {
+ /* If it's a temp cert, it might have been stored before the
+ * builtin trust module is loaded, so look for the trust
+ * again, but don't set the empty trust if it is not found.
+ */
+ NSSTrust *t = nssTrustDomain_FindTrustForCertificate(c->object.cryptoContext->td, c);
+ if (!t) {
+ goto loser;
+ }
+ trust = cert_trust_from_stan_trust(t, cc->arena);
+ nssTrust_Destroy(t);
+ if (!trust) {
+ goto loser;
+ }
+ }
+
+ 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;
+ CERT_LockCertTempPerm(cc);
+ c = cc->nssCertificate;
+ CERT_UnlockCertTempPerm(cc);
+ 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);
+ /* 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 = PK11Slot_GetNSSToken(cc->slot);
+ if (!instance->token) {
+ nssArena_Destroy(arena);
+ return NULL;
+ }
+ 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);
+ CERT_LockCertTempPerm(cc);
+ cc->nssCertificate = c;
+ CERT_UnlockCertTempPerm(cc);
+ 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 *cctx = c->object.cryptoContext;
+ nssrv = nssCryptoContext_ImportTrust(cctx, 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);
+ if (!tok) {
+ nssrv = PR_FAILURE;
+ goto done;
+ }
+
+ 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) {
+ (void)nssToken_Destroy(tok);
+ 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);
+ (void)nssToken_Destroy(tok);
+ }
+ 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);
+ }
+}