/* 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); } }