Bug 1748341 - only instantiate CERTCertificate in nsNSSCertificate if needed r=jschanck

To avoid doing unnecessary work and to reduce NSS resource contention and
crashes, this patch lazily instantiates the CERTCertificate in
nsNSSCertificate.

Depends on D134979

Differential Revision: https://phabricator.services.mozilla.com/D134980
This commit is contained in:
Dana Keeler 2022-01-12 00:40:14 +00:00
parent b3e9aadfa8
commit c3d9c4a9fb
2 changed files with 160 additions and 76 deletions

View File

@ -64,25 +64,34 @@ extern LazyLogModule gPIPNSSLog;
NS_IMPL_ISUPPORTS(nsNSSCertificate, nsIX509Cert, nsISerializable, nsIClassInfo)
nsNSSCertificate::nsNSSCertificate()
: mCert(nullptr), mCertType(CERT_TYPE_NOT_YET_INITIALIZED) {}
: mCert(Nothing()), mCertType(CERT_TYPE_NOT_YET_INITIALIZED) {}
nsNSSCertificate::nsNSSCertificate(CERTCertificate* cert)
: mCert(nullptr), mCertType(CERT_TYPE_NOT_YET_INITIALIZED) {
: mCert(Nothing()), mCertType(CERT_TYPE_NOT_YET_INITIALIZED) {
if (cert) {
mCert.reset(CERT_DupCertificate(cert));
mDER.AppendElements(mCert->derCert.data, mCert->derCert.len);
mDER.AppendElements(cert->derCert.data, cert->derCert.len);
mCert.emplace(UniqueCERTCertificate(CERT_DupCertificate(cert)));
}
}
nsNSSCertificate::nsNSSCertificate(nsTArray<uint8_t>&& der)
: mDER(std::move(der)), mCertType(CERT_TYPE_NOT_YET_INITIALIZED) {
: mDER(std::move(der)),
mCert(Nothing()),
mCertType(CERT_TYPE_NOT_YET_INITIALIZED) {}
nsresult nsNSSCertificate::InstantiateCert() {
SECItem derItem = {siBuffer, mDER.Elements(),
static_cast<unsigned int>(mDER.Length())};
mCert.reset(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derItem,
nullptr, false, true));
UniqueCERTCertificate cert(CERT_NewTempCertificate(
CERT_GetDefaultCertDB(), &derItem, nullptr, false, true));
if (!cert) {
return NS_ERROR_FAILURE;
}
mCert.emplace(std::move(cert));
return NS_OK;
}
static uint32_t getCertType(CERTCertificate* cert) {
static uint32_t getCertType(UniqueCERTCertificate& cert) {
nsNSSCertTrust trust(cert->trust);
if (cert->nickname && trust.HasAnyUser()) {
return nsIX509Cert::USER_CERT;
@ -96,7 +105,7 @@ static uint32_t getCertType(CERTCertificate* cert) {
if (trust.HasPeer(false, true) && cert->emailAddr) {
return nsIX509Cert::EMAIL_CERT;
}
if (CERT_IsCACert(cert, nullptr)) {
if (CERT_IsCACert(cert.get(), nullptr)) {
return nsIX509Cert::CA_CERT;
}
if (cert->emailAddr) {
@ -108,7 +117,13 @@ static uint32_t getCertType(CERTCertificate* cert) {
nsresult nsNSSCertificate::GetCertType(uint32_t* aCertType) {
if (mCertType == CERT_TYPE_NOT_YET_INITIALIZED) {
// only determine cert type once and cache it
mCertType = getCertType(mCert.get());
if (!mCert) {
nsresult rv = InstantiateCert();
if (NS_FAILED(rv)) {
return rv;
}
}
mCertType = getCertType(*mCert);
}
*aCertType = mCertType;
return NS_OK;
@ -180,14 +195,17 @@ NS_IMETHODIMP
nsNSSCertificate::GetDisplayName(nsAString& aDisplayName) {
aDisplayName.Truncate();
MOZ_ASSERT(mCert, "mCert should not be null in GetDisplayName");
if (!mCert) {
return NS_ERROR_FAILURE;
nsresult rv = InstantiateCert();
if (NS_FAILED(rv)) {
return rv;
}
}
UniquePORTString commonName(CERT_GetCommonName(&mCert->subject));
UniquePORTString organizationalUnitName(CERT_GetOrgUnitName(&mCert->subject));
UniquePORTString organizationName(CERT_GetOrgName(&mCert->subject));
UniquePORTString commonName(CERT_GetCommonName(&(*mCert)->subject));
UniquePORTString organizationalUnitName(
CERT_GetOrgUnitName(&(*mCert)->subject));
UniquePORTString organizationName(CERT_GetOrgName(&(*mCert)->subject));
bool isBuiltInRoot;
nsresult rv = GetIsBuiltInRoot(&isBuiltInRoot);
@ -208,7 +226,7 @@ nsNSSCertificate::GetDisplayName(nsAString& aDisplayName) {
// (the subject really shouldn't be empty), an empty string is returned.
nsAutoCString builtInRootNickname;
if (isBuiltInRoot) {
nsAutoCString fullNickname(mCert->nickname);
nsAutoCString fullNickname((*mCert)->nickname);
int32_t index = fullNickname.Find(":");
if (index != kNotFound) {
// Substring will gracefully handle the case where index is the last
@ -221,7 +239,7 @@ nsNSSCertificate::GetDisplayName(nsAString& aDisplayName) {
const char* nameOptions[] = {
builtInRootNickname.get(), commonName.get(),
organizationalUnitName.get(), organizationName.get(),
mCert->subjectName, mCert->emailAddr};
(*mCert)->subjectName, (*mCert)->emailAddr};
for (auto nameOption : nameOptions) {
if (nameOption) {
@ -238,8 +256,14 @@ nsNSSCertificate::GetDisplayName(nsAString& aDisplayName) {
NS_IMETHODIMP
nsNSSCertificate::GetEmailAddress(nsAString& aEmailAddress) {
if (mCert->emailAddr) {
CopyUTF8toUTF16(MakeStringSpan(mCert->emailAddr), aEmailAddress);
if (!mCert) {
nsresult rv = InstantiateCert();
if (NS_FAILED(rv)) {
return rv;
}
}
if ((*mCert)->emailAddr) {
CopyUTF8toUTF16(MakeStringSpan((*mCert)->emailAddr), aEmailAddress);
} else {
GetPIPNSSBundleString("CertNoEmailAddress", aEmailAddress);
}
@ -248,16 +272,22 @@ nsNSSCertificate::GetEmailAddress(nsAString& aEmailAddress) {
NS_IMETHODIMP
nsNSSCertificate::GetEmailAddresses(nsTArray<nsString>& aAddresses) {
if (!mCert) {
nsresult rv = InstantiateCert();
if (NS_FAILED(rv)) {
return rv;
}
}
uint32_t length = 0;
for (const char* aAddr = CERT_GetFirstEmailAddress(mCert.get()); aAddr;
aAddr = CERT_GetNextEmailAddress(mCert.get(), aAddr)) {
for (const char* aAddr = CERT_GetFirstEmailAddress((*mCert).get()); aAddr;
aAddr = CERT_GetNextEmailAddress((*mCert).get(), aAddr)) {
++(length);
}
aAddresses.SetCapacity(length);
for (const char* aAddr = CERT_GetFirstEmailAddress(mCert.get()); aAddr;
aAddr = CERT_GetNextEmailAddress(mCert.get(), aAddr)) {
for (const char* aAddr = CERT_GetFirstEmailAddress((*mCert).get()); aAddr;
aAddr = CERT_GetNextEmailAddress((*mCert).get(), aAddr)) {
CopyASCIItoUTF16(MakeStringSpan(aAddr), *aAddresses.AppendElement());
}
@ -270,8 +300,14 @@ nsNSSCertificate::ContainsEmailAddress(const nsAString& aEmailAddress,
NS_ENSURE_ARG(result);
*result = false;
for (const char* aAddr = CERT_GetFirstEmailAddress(mCert.get()); aAddr;
aAddr = CERT_GetNextEmailAddress(mCert.get(), aAddr)) {
if (!mCert) {
nsresult rv = InstantiateCert();
if (NS_FAILED(rv)) {
return rv;
}
}
for (const char* aAddr = CERT_GetFirstEmailAddress((*mCert).get()); aAddr;
aAddr = CERT_GetNextEmailAddress((*mCert).get(), aAddr)) {
nsAutoString certAddr;
LossyUTF8ToUTF16(aAddr, strlen(aAddr), certAddr);
ToLowerCase(certAddr);
@ -291,84 +327,113 @@ nsNSSCertificate::ContainsEmailAddress(const nsAString& aEmailAddress,
NS_IMETHODIMP
nsNSSCertificate::GetCommonName(nsAString& aCommonName) {
aCommonName.Truncate();
if (mCert) {
UniquePORTString commonName(CERT_GetCommonName(&mCert->subject));
if (commonName) {
LossyUTF8ToUTF16(commonName.get(), strlen(commonName.get()), aCommonName);
if (!mCert) {
nsresult rv = InstantiateCert();
if (NS_FAILED(rv)) {
return rv;
}
}
UniquePORTString commonName(CERT_GetCommonName(&(*mCert)->subject));
if (commonName) {
LossyUTF8ToUTF16(commonName.get(), strlen(commonName.get()), aCommonName);
}
return NS_OK;
}
NS_IMETHODIMP
nsNSSCertificate::GetOrganization(nsAString& aOrganization) {
aOrganization.Truncate();
if (mCert) {
UniquePORTString organization(CERT_GetOrgName(&mCert->subject));
if (organization) {
LossyUTF8ToUTF16(organization.get(), strlen(organization.get()),
aOrganization);
if (!mCert) {
nsresult rv = InstantiateCert();
if (NS_FAILED(rv)) {
return rv;
}
}
UniquePORTString organization(CERT_GetOrgName(&(*mCert)->subject));
if (organization) {
LossyUTF8ToUTF16(organization.get(), strlen(organization.get()),
aOrganization);
}
return NS_OK;
}
NS_IMETHODIMP
nsNSSCertificate::GetIssuerCommonName(nsAString& aCommonName) {
aCommonName.Truncate();
if (mCert) {
UniquePORTString commonName(CERT_GetCommonName(&mCert->issuer));
if (commonName) {
LossyUTF8ToUTF16(commonName.get(), strlen(commonName.get()), aCommonName);
if (!mCert) {
nsresult rv = InstantiateCert();
if (NS_FAILED(rv)) {
return rv;
}
}
UniquePORTString commonName(CERT_GetCommonName(&(*mCert)->issuer));
if (commonName) {
LossyUTF8ToUTF16(commonName.get(), strlen(commonName.get()), aCommonName);
}
return NS_OK;
}
NS_IMETHODIMP
nsNSSCertificate::GetIssuerOrganization(nsAString& aOrganization) {
aOrganization.Truncate();
if (mCert) {
UniquePORTString organization(CERT_GetOrgName(&mCert->issuer));
if (organization) {
LossyUTF8ToUTF16(organization.get(), strlen(organization.get()),
aOrganization);
if (!mCert) {
nsresult rv = InstantiateCert();
if (NS_FAILED(rv)) {
return rv;
}
}
UniquePORTString organization(CERT_GetOrgName(&(*mCert)->issuer));
if (organization) {
LossyUTF8ToUTF16(organization.get(), strlen(organization.get()),
aOrganization);
}
return NS_OK;
}
NS_IMETHODIMP
nsNSSCertificate::GetIssuerOrganizationUnit(nsAString& aOrganizationUnit) {
aOrganizationUnit.Truncate();
if (mCert) {
UniquePORTString organizationUnit(CERT_GetOrgUnitName(&mCert->issuer));
if (organizationUnit) {
LossyUTF8ToUTF16(organizationUnit.get(), strlen(organizationUnit.get()),
aOrganizationUnit);
if (!mCert) {
nsresult rv = InstantiateCert();
if (NS_FAILED(rv)) {
return rv;
}
}
UniquePORTString organizationUnit(CERT_GetOrgUnitName(&(*mCert)->issuer));
if (organizationUnit) {
LossyUTF8ToUTF16(organizationUnit.get(), strlen(organizationUnit.get()),
aOrganizationUnit);
}
return NS_OK;
}
NS_IMETHODIMP
nsNSSCertificate::GetOrganizationalUnit(nsAString& aOrganizationalUnit) {
aOrganizationalUnit.Truncate();
if (mCert) {
UniquePORTString orgunit(CERT_GetOrgUnitName(&mCert->subject));
if (orgunit) {
LossyUTF8ToUTF16(orgunit.get(), strlen(orgunit.get()),
aOrganizationalUnit);
if (!mCert) {
nsresult rv = InstantiateCert();
if (NS_FAILED(rv)) {
return rv;
}
}
UniquePORTString orgunit(CERT_GetOrgUnitName(&(*mCert)->subject));
if (orgunit) {
LossyUTF8ToUTF16(orgunit.get(), strlen(orgunit.get()), aOrganizationalUnit);
}
return NS_OK;
}
NS_IMETHODIMP
nsNSSCertificate::GetSubjectName(nsAString& _subjectName) {
_subjectName.Truncate();
if (mCert->subjectName) {
LossyUTF8ToUTF16(mCert->subjectName, strlen(mCert->subjectName),
if (!mCert) {
nsresult rv = InstantiateCert();
if (NS_FAILED(rv)) {
return rv;
}
}
if ((*mCert)->subjectName) {
LossyUTF8ToUTF16((*mCert)->subjectName, strlen((*mCert)->subjectName),
_subjectName);
}
return NS_OK;
@ -377,8 +442,15 @@ nsNSSCertificate::GetSubjectName(nsAString& _subjectName) {
NS_IMETHODIMP
nsNSSCertificate::GetIssuerName(nsAString& _issuerName) {
_issuerName.Truncate();
if (mCert->issuerName) {
LossyUTF8ToUTF16(mCert->issuerName, strlen(mCert->issuerName), _issuerName);
if (!mCert) {
nsresult rv = InstantiateCert();
if (NS_FAILED(rv)) {
return rv;
}
}
if ((*mCert)->issuerName) {
LossyUTF8ToUTF16((*mCert)->issuerName, strlen((*mCert)->issuerName),
_issuerName);
}
return NS_OK;
}
@ -386,8 +458,14 @@ nsNSSCertificate::GetIssuerName(nsAString& _issuerName) {
NS_IMETHODIMP
nsNSSCertificate::GetSerialNumber(nsAString& _serialNumber) {
_serialNumber.Truncate();
if (!mCert) {
nsresult rv = InstantiateCert();
if (NS_FAILED(rv)) {
return rv;
}
}
UniquePORTString tmpstr(
CERT_Hexify(&mCert->serialNumber, true /* use colon delimiters */));
CERT_Hexify(&(*mCert)->serialNumber, true /* use colon delimiters */));
if (tmpstr) {
_serialNumber = NS_ConvertASCIItoUTF16(tmpstr.get());
return NS_OK;
@ -429,16 +507,18 @@ nsNSSCertificate::GetSha1Fingerprint(nsAString& _sha1Fingerprint) {
NS_IMETHODIMP
nsNSSCertificate::GetTokenName(nsAString& aTokenName) {
MOZ_ASSERT(mCert);
if (!mCert) {
return NS_ERROR_FAILURE;
nsresult rv = InstantiateCert();
if (NS_FAILED(rv)) {
return rv;
}
}
UniquePK11SlotInfo internalSlot(PK11_GetInternalSlot());
if (!internalSlot) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIPK11Token> token(
new nsPK11Token(mCert->slot ? mCert->slot : internalSlot.get()));
new nsPK11Token((*mCert)->slot ? (*mCert)->slot : internalSlot.get()));
nsAutoCString tmp;
nsresult rv = token->GetTokenName(tmp);
if (NS_FAILED(rv)) {
@ -501,7 +581,13 @@ nsNSSCertificate::GetBase64DERString(nsACString& base64DERString) {
}
CERTCertificate* nsNSSCertificate::GetCert() {
return (mCert) ? CERT_DupCertificate(mCert.get()) : nullptr;
if (!mCert) {
nsresult rv = InstantiateCert();
if (NS_FAILED(rv)) {
return nullptr;
}
}
return CERT_DupCertificate((*mCert).get());
}
NS_IMETHODIMP
@ -521,7 +607,6 @@ nsNSSCertificate::GetValidity(nsIX509CertValidity** aValidity) {
// |SerializeToIPC|.
NS_IMETHODIMP
nsNSSCertificate::Write(nsIObjectOutputStream* aStream) {
NS_ENSURE_STATE(mCert);
// This field used to be the cached EV status, but it is no longer necessary.
nsresult rv = aStream->Write32(0);
if (NS_FAILED(rv)) {
@ -538,7 +623,9 @@ nsNSSCertificate::Write(nsIObjectOutputStream* aStream) {
// |DeserializeFromIPC|.
NS_IMETHODIMP
nsNSSCertificate::Read(nsIObjectInputStream* aStream) {
NS_ENSURE_STATE(!mCert);
if (!mDER.IsEmpty() || mCert.isSome()) {
return NS_ERROR_ALREADY_INITIALIZED;
}
// This field is no longer used.
uint32_t unusedCachedEVStatus;
@ -557,16 +644,11 @@ nsNSSCertificate::Read(nsIObjectInputStream* aStream) {
if (NS_FAILED(rv)) {
return rv;
}
SECItem derItem = {siBuffer, mDER.Elements(),
static_cast<unsigned int>(mDER.Length())};
mCert.reset(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derItem,
nullptr, false, true));
return NS_OK;
}
void nsNSSCertificate::SerializeToIPC(IPC::Message* aMsg) {
bool hasCert = static_cast<bool>(mCert);
bool hasCert = !mDER.IsEmpty();
WriteParam(aMsg, hasCert);
if (!hasCert) {
@ -578,6 +660,10 @@ void nsNSSCertificate::SerializeToIPC(IPC::Message* aMsg) {
bool nsNSSCertificate::DeserializeFromIPC(const IPC::Message* aMsg,
PickleIterator* aIter) {
if (!mDER.IsEmpty() || mCert.isSome()) {
return false;
}
bool hasCert = false;
if (!ReadParam(aMsg, aIter, &hasCert)) {
return false;
@ -590,11 +676,6 @@ bool nsNSSCertificate::DeserializeFromIPC(const IPC::Message* aMsg,
if (!ReadParam(aMsg, aIter, &mDER)) {
return false;
}
SECItem derItem = {siBuffer, mDER.Elements(),
static_cast<unsigned int>(mDER.Length())};
mCert.reset(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derItem,
nullptr, false, true));
return true;
}

View File

@ -8,6 +8,7 @@
#include "ScopedNSSTypes.h"
#include "certt.h"
#include "mozilla/Maybe.h"
#include "nsCOMPtr.h"
#include "nsIClassInfo.h"
#include "nsISerializable.h"
@ -33,10 +34,12 @@ class nsNSSCertificate final : public nsIX509Cert,
virtual ~nsNSSCertificate() = default;
nsTArray<uint8_t> mDER;
mozilla::UniqueCERTCertificate mCert;
mozilla::Maybe<mozilla::UniqueCERTCertificate> mCert;
uint32_t mCertType;
nsresult GetCertificateHash(nsAString& aFingerprint, SECOidTag aHashAlg);
nsresult InstantiateCert();
};
#define NS_X509CERT_CID \