mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 19:04:45 +00:00
bug 1473573 - import intermediate certificates as well as roots r=jcj
Differential Revision: https://phabricator.services.mozilla.com/D18630 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
7f2cafcb86
commit
86b72ab902
@ -89,7 +89,7 @@ CertVerifier::CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc,
|
||||
NetscapeStepUpPolicy netscapeStepUpPolicy,
|
||||
CertificateTransparencyMode ctMode,
|
||||
DistrustedCAPolicy distrustedCAPolicy,
|
||||
const Vector<Vector<uint8_t>>& thirdPartyRoots)
|
||||
const Vector<EnterpriseCert>& thirdPartyCerts)
|
||||
: mOCSPDownloadConfig(odc),
|
||||
mOCSPStrict(osc == ocspStrict),
|
||||
mOCSPTimeoutSoft(ocspTimeoutSoft),
|
||||
@ -102,19 +102,24 @@ CertVerifier::CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc,
|
||||
mCTMode(ctMode),
|
||||
mDistrustedCAPolicy(distrustedCAPolicy) {
|
||||
LoadKnownCTLogs();
|
||||
for (const auto& root : thirdPartyRoots) {
|
||||
Vector<uint8_t> rootCopy;
|
||||
if (rootCopy.append(root.begin(), root.end())) {
|
||||
// Best-effort. If we run out of memory, users might see untrusted issuer
|
||||
// errors, but the browser will probably crash before then.
|
||||
Unused << mThirdPartyRoots.append(std::move(rootCopy));
|
||||
for (const auto& root : thirdPartyCerts) {
|
||||
EnterpriseCert rootCopy;
|
||||
// Best-effort. If we run out of memory, users might see untrusted issuer
|
||||
// errors, but the browser will probably crash before then.
|
||||
if (NS_SUCCEEDED(rootCopy.Init(root))) {
|
||||
Unused << mThirdPartyCerts.append(std::move(rootCopy));
|
||||
}
|
||||
}
|
||||
for (const auto& root : mThirdPartyRoots) {
|
||||
Input rootInput;
|
||||
if (rootInput.Init(root.begin(), root.length()) == Success) {
|
||||
// Best effort again.
|
||||
Unused << mThirdPartyRootInputs.append(rootInput);
|
||||
for (const auto& root : mThirdPartyCerts) {
|
||||
Input input;
|
||||
if (root.GetInput(input) == Success) {
|
||||
// mThirdPartyCerts consists of roots and intermediates.
|
||||
if (root.GetIsRoot()) {
|
||||
// Best effort again.
|
||||
Unused << mThirdPartyRootInputs.append(input);
|
||||
} else {
|
||||
Unused << mThirdPartyIntermediateInputs.append(input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -528,7 +533,7 @@ Result CertVerifier::VerifyCert(
|
||||
MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
|
||||
SHA1Mode::Allowed, NetscapeStepUpPolicy::NeverMatch,
|
||||
mDistrustedCAPolicy, originAttributes, mThirdPartyRootInputs,
|
||||
builtChain, nullptr, nullptr);
|
||||
mThirdPartyIntermediateInputs, builtChain, nullptr, nullptr);
|
||||
rv = BuildCertChain(
|
||||
trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
|
||||
KeyUsage::digitalSignature, KeyPurposeId::id_kp_clientAuth,
|
||||
@ -601,7 +606,8 @@ Result CertVerifier::VerifyCert(
|
||||
MIN_RSA_BITS, ValidityCheckingMode::CheckForEV,
|
||||
sha1ModeConfigurations[i], mNetscapeStepUpPolicy,
|
||||
mDistrustedCAPolicy, originAttributes, mThirdPartyRootInputs,
|
||||
builtChain, pinningTelemetryInfo, hostname);
|
||||
mThirdPartyIntermediateInputs, builtChain, pinningTelemetryInfo,
|
||||
hostname);
|
||||
rv = BuildCertChainForOneKeyUsage(
|
||||
trustDomain, certDER, time,
|
||||
KeyUsage::digitalSignature, // (EC)DHE
|
||||
@ -682,8 +688,8 @@ Result CertVerifier::VerifyCert(
|
||||
mPinningMode, keySizeOptions[i],
|
||||
ValidityCheckingMode::CheckingOff, sha1ModeConfigurations[j],
|
||||
mNetscapeStepUpPolicy, mDistrustedCAPolicy, originAttributes,
|
||||
mThirdPartyRootInputs, builtChain, pinningTelemetryInfo,
|
||||
hostname);
|
||||
mThirdPartyRootInputs, mThirdPartyIntermediateInputs, builtChain,
|
||||
pinningTelemetryInfo, hostname);
|
||||
rv = BuildCertChainForOneKeyUsage(
|
||||
trustDomain, certDER, time,
|
||||
KeyUsage::digitalSignature, //(EC)DHE
|
||||
@ -751,8 +757,8 @@ Result CertVerifier::VerifyCert(
|
||||
mOCSPTimeoutHard, mCertShortLifetimeInDays, pinningDisabled,
|
||||
MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
|
||||
SHA1Mode::Allowed, mNetscapeStepUpPolicy, mDistrustedCAPolicy,
|
||||
originAttributes, mThirdPartyRootInputs, builtChain, nullptr,
|
||||
nullptr);
|
||||
originAttributes, mThirdPartyRootInputs,
|
||||
mThirdPartyIntermediateInputs, builtChain, nullptr, nullptr);
|
||||
rv = BuildCertChain(trustDomain, certDER, time, EndEntityOrCA::MustBeCA,
|
||||
KeyUsage::keyCertSign, KeyPurposeId::id_kp_serverAuth,
|
||||
CertPolicyId::anyPolicy, stapledOCSPResponse);
|
||||
@ -766,7 +772,7 @@ Result CertVerifier::VerifyCert(
|
||||
MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
|
||||
SHA1Mode::Allowed, NetscapeStepUpPolicy::NeverMatch,
|
||||
mDistrustedCAPolicy, originAttributes, mThirdPartyRootInputs,
|
||||
builtChain, nullptr, nullptr);
|
||||
mThirdPartyIntermediateInputs, builtChain, nullptr, nullptr);
|
||||
rv = BuildCertChain(
|
||||
trustDomain, certDER, time, EndEntityOrCA::MustBeEndEntity,
|
||||
KeyUsage::digitalSignature, KeyPurposeId::id_kp_emailProtection,
|
||||
@ -790,7 +796,7 @@ Result CertVerifier::VerifyCert(
|
||||
MIN_RSA_BITS_WEAK, ValidityCheckingMode::CheckingOff,
|
||||
SHA1Mode::Allowed, NetscapeStepUpPolicy::NeverMatch,
|
||||
mDistrustedCAPolicy, originAttributes, mThirdPartyRootInputs,
|
||||
builtChain, nullptr, nullptr);
|
||||
mThirdPartyIntermediateInputs, builtChain, nullptr, nullptr);
|
||||
rv = BuildCertChain(trustDomain, certDER, time,
|
||||
EndEntityOrCA::MustBeEndEntity,
|
||||
KeyUsage::keyEncipherment, // RSA
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "BRNameMatchingPolicy.h"
|
||||
#include "CTPolicyEnforcer.h"
|
||||
#include "CTVerifyResult.h"
|
||||
#include "EnterpriseRoots.h"
|
||||
#include "OCSPCache.h"
|
||||
#include "RootCertificateTelemetryUtils.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
@ -207,7 +208,7 @@ class CertVerifier {
|
||||
NetscapeStepUpPolicy netscapeStepUpPolicy,
|
||||
CertificateTransparencyMode ctMode,
|
||||
DistrustedCAPolicy distrustedCAPolicy,
|
||||
const Vector<Vector<uint8_t>>& thirdPartyRoots);
|
||||
const Vector<EnterpriseCert>& thirdPartyCerts);
|
||||
~CertVerifier();
|
||||
|
||||
void ClearOCSPCache() { mOCSPCache.Clear(); }
|
||||
@ -227,10 +228,12 @@ class CertVerifier {
|
||||
private:
|
||||
OCSPCache mOCSPCache;
|
||||
// We keep a copy of the bytes of each third party root to own.
|
||||
Vector<Vector<uint8_t>> mThirdPartyRoots;
|
||||
Vector<EnterpriseCert> mThirdPartyCerts;
|
||||
// This is a reusable, precomputed list of Inputs corresponding to each root
|
||||
// in mThirdPartyRoots that wasn't too long to make an Input out of.
|
||||
// in mThirdPartyCerts that wasn't too long to make an Input out of.
|
||||
Vector<mozilla::pkix::Input> mThirdPartyRootInputs;
|
||||
// Similarly, but with intermediates.
|
||||
Vector<mozilla::pkix::Input> mThirdPartyIntermediateInputs;
|
||||
|
||||
// We only have a forward declarations of these classes (see above)
|
||||
// so we must allocate dynamically.
|
||||
|
@ -61,6 +61,7 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(
|
||||
DistrustedCAPolicy distrustedCAPolicy,
|
||||
const OriginAttributes& originAttributes,
|
||||
const Vector<Input>& thirdPartyRootInputs,
|
||||
const Vector<Input>& thirdPartyIntermediateInputs,
|
||||
/*out*/ UniqueCERTCertList& builtChain,
|
||||
/*optional*/ PinningTelemetryInfo* pinningTelemetryInfo,
|
||||
/*optional*/ const char* hostname)
|
||||
@ -80,6 +81,7 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(
|
||||
mSawDistrustedCAByPolicyError(false),
|
||||
mOriginAttributes(originAttributes),
|
||||
mThirdPartyRootInputs(thirdPartyRootInputs),
|
||||
mThirdPartyIntermediateInputs(thirdPartyIntermediateInputs),
|
||||
mBuiltChain(builtChain),
|
||||
mPinningTelemetryInfo(pinningTelemetryInfo),
|
||||
mHostname(hostname),
|
||||
@ -126,6 +128,13 @@ Result NSSCertDBTrustDomain::FindIssuer(Input encodedIssuerName,
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& thirdPartyIntermediateInput :
|
||||
mThirdPartyIntermediateInputs) {
|
||||
if (!intermediateCandidates.append(thirdPartyIntermediateInput)) {
|
||||
return Result::FATAL_ERROR_NO_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle imposed name constraints, if any.
|
||||
ScopedAutoSECItem nameConstraints;
|
||||
Input nameConstraintsInput;
|
||||
|
@ -108,6 +108,7 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain {
|
||||
DistrustedCAPolicy distrustedCAPolicy,
|
||||
const OriginAttributes& originAttributes,
|
||||
const Vector<mozilla::pkix::Input>& thirdPartyRootInputs,
|
||||
const Vector<mozilla::pkix::Input>& thirdPartyIntermediateInputs,
|
||||
/*out*/ UniqueCERTCertList& builtChain,
|
||||
/*optional*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr,
|
||||
/*optional*/ const char* hostname = nullptr);
|
||||
@ -217,6 +218,8 @@ class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain {
|
||||
bool mSawDistrustedCAByPolicyError;
|
||||
const OriginAttributes& mOriginAttributes;
|
||||
const Vector<mozilla::pkix::Input>& mThirdPartyRootInputs; // non-owning
|
||||
const Vector<mozilla::pkix::Input>&
|
||||
mThirdPartyIntermediateInputs; // non-owning
|
||||
UniqueCERTCertList& mBuiltChain; // non-owning
|
||||
PinningTelemetryInfo* mPinningTelemetryInfo;
|
||||
const char* mHostname; // non-owning - only used for pinning checks
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozpkix/Result.h"
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
# include <Security/Security.h>
|
||||
@ -19,8 +20,36 @@ extern LazyLogModule gPIPNSSLog;
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
nsresult EnterpriseCert::Init(const uint8_t* data, size_t len, bool isRoot) {
|
||||
mDER.clear();
|
||||
if (!mDER.append(data, len)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
mIsRoot = isRoot;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult EnterpriseCert::Init(const EnterpriseCert& orig) {
|
||||
return Init(orig.mDER.begin(), orig.mDER.length(), orig.mIsRoot);
|
||||
}
|
||||
|
||||
nsresult EnterpriseCert::CopyBytes(nsTArray<uint8_t>& dest) const {
|
||||
dest.Clear();
|
||||
if (!dest.AppendElements(mDER.begin(), mDER.length())) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
pkix::Result EnterpriseCert::GetInput(pkix::Input& input) const {
|
||||
return input.Init(mDER.begin(), mDER.length());
|
||||
}
|
||||
|
||||
bool EnterpriseCert::GetIsRoot() const { return mIsRoot; }
|
||||
|
||||
#ifdef XP_WIN
|
||||
const wchar_t* kWindowsDefaultRootStoreName = L"ROOT";
|
||||
const wchar_t* kWindowsDefaultRootStoreNames[] = {L"ROOT", L"CA"};
|
||||
|
||||
// Helper function to determine if the OS considers the given certificate to be
|
||||
// a trust anchor for TLS server auth certificates. This is to be used in the
|
||||
@ -29,10 +58,13 @@ const wchar_t* kWindowsDefaultRootStoreName = L"ROOT";
|
||||
// in some way unsuitable to issue certificates, mozilla::pkix will never build
|
||||
// a valid chain that includes the certificate, so importing it even if it
|
||||
// isn't a valid CA poses no risk.
|
||||
static bool CertIsTrustAnchorForTLSServerAuth(PCCERT_CONTEXT certificate) {
|
||||
static void CertIsTrustAnchorForTLSServerAuth(PCCERT_CONTEXT certificate,
|
||||
bool& isTrusted, bool& isRoot) {
|
||||
isTrusted = false;
|
||||
isRoot = false;
|
||||
MOZ_ASSERT(certificate);
|
||||
if (!certificate) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
PCCERT_CHAIN_CONTEXT pChainContext = nullptr;
|
||||
@ -56,20 +88,19 @@ static bool CertIsTrustAnchorForTLSServerAuth(PCCERT_CONTEXT certificate) {
|
||||
if (!CertGetCertificateChain(nullptr, certificate, nullptr, nullptr,
|
||||
&chainPara, 0, nullptr, &pChainContext)) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("CertGetCertificateChain failed"));
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
isTrusted = pChainContext->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR;
|
||||
if (isTrusted && pChainContext->cChain > 0) {
|
||||
// The so-called "final chain" is what we're after:
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/wincrypt/ns-wincrypt-_cert_chain_context
|
||||
CERT_SIMPLE_CHAIN* finalChain =
|
||||
pChainContext->rgpChain[pChainContext->cChain - 1];
|
||||
// This is a root if the final chain consists of only one certificate (i.e.
|
||||
// this one).
|
||||
isRoot = finalChain->cElement == 1;
|
||||
}
|
||||
bool trusted =
|
||||
pChainContext->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR;
|
||||
bool isRoot = pChainContext->cChain == 1;
|
||||
CertFreeCertificateChain(pChainContext);
|
||||
if (trusted && isRoot) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("certificate is trust anchor for TLS server auth"));
|
||||
return true;
|
||||
}
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("certificate not trust anchor for TLS server auth"));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Because HCERTSTORE is just a typedef void*, we can't use any of the nice
|
||||
@ -100,8 +131,8 @@ class ScopedCertStore final {
|
||||
// HKLM\SOFTWARE\Policies\Microsoft\SystemCertificates\Root\Certificates)
|
||||
// CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE
|
||||
// (for HKLM\SOFTWARE\Microsoft\EnterpriseCertificates\Root\Certificates)
|
||||
static void GatherEnterpriseRootsForLocation(DWORD locationFlag,
|
||||
Vector<Vector<uint8_t>>& roots) {
|
||||
static void GatherEnterpriseCertsForLocation(DWORD locationFlag,
|
||||
Vector<EnterpriseCert>& certs) {
|
||||
MOZ_ASSERT(locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE ||
|
||||
locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY ||
|
||||
locationFlag == CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE,
|
||||
@ -119,52 +150,55 @@ static void GatherEnterpriseRootsForLocation(DWORD locationFlag,
|
||||
// of Microsoft's root store program.
|
||||
// The 3rd parameter to CertOpenStore should be NULL according to
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa376559%28v=vs.85%29.aspx
|
||||
ScopedCertStore enterpriseRootStore(
|
||||
CertOpenStore(CERT_STORE_PROV_SYSTEM_REGISTRY_W, 0, NULL, flags,
|
||||
kWindowsDefaultRootStoreName));
|
||||
if (!enterpriseRootStore.get()) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("failed to open enterprise root store"));
|
||||
return;
|
||||
}
|
||||
PCCERT_CONTEXT certificate = nullptr;
|
||||
uint32_t numImported = 0;
|
||||
while ((certificate = CertFindCertificateInStore(
|
||||
enterpriseRootStore.get(), X509_ASN_ENCODING, 0, CERT_FIND_ANY,
|
||||
nullptr, certificate))) {
|
||||
if (!CertIsTrustAnchorForTLSServerAuth(certificate)) {
|
||||
for (auto name : kWindowsDefaultRootStoreNames) {
|
||||
ScopedCertStore enterpriseRootStore(
|
||||
CertOpenStore(CERT_STORE_PROV_SYSTEM_REGISTRY_W, 0, NULL, flags, name));
|
||||
if (!enterpriseRootStore.get()) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("skipping cert not trust anchor for TLS server auth"));
|
||||
("failed to open enterprise root store"));
|
||||
continue;
|
||||
}
|
||||
Vector<uint8_t> certBytes;
|
||||
if (!certBytes.append(certificate->pbCertEncoded,
|
||||
certificate->cbCertEncoded)) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Verbose,
|
||||
("couldn't copy cert bytes (out of memory?)"));
|
||||
continue;
|
||||
PCCERT_CONTEXT certificate = nullptr;
|
||||
uint32_t numImported = 0;
|
||||
while ((certificate = CertFindCertificateInStore(
|
||||
enterpriseRootStore.get(), X509_ASN_ENCODING, 0, CERT_FIND_ANY,
|
||||
nullptr, certificate))) {
|
||||
bool isTrusted;
|
||||
bool isRoot;
|
||||
CertIsTrustAnchorForTLSServerAuth(certificate, isTrusted, isRoot);
|
||||
if (!isTrusted) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("skipping cert not trusted for TLS server auth"));
|
||||
continue;
|
||||
}
|
||||
EnterpriseCert enterpriseCert;
|
||||
if (NS_FAILED(enterpriseCert.Init(certificate->pbCertEncoded,
|
||||
certificate->cbCertEncoded, isRoot))) {
|
||||
// Best-effort. We probably ran out of memory.
|
||||
continue;
|
||||
}
|
||||
if (!certs.append(std::move(enterpriseCert))) {
|
||||
// Best-effort again.
|
||||
continue;
|
||||
}
|
||||
numImported++;
|
||||
}
|
||||
if (!roots.append(std::move(certBytes))) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Verbose,
|
||||
("couldn't append cert bytes to roots list (out of memory?)"));
|
||||
continue;
|
||||
}
|
||||
numImported++;
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("imported %u certs from %S", numImported, name));
|
||||
}
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("imported %u roots", numImported));
|
||||
}
|
||||
|
||||
static void GatherEnterpriseRootsWindows(Vector<Vector<uint8_t>>& roots) {
|
||||
GatherEnterpriseRootsForLocation(CERT_SYSTEM_STORE_LOCAL_MACHINE, roots);
|
||||
GatherEnterpriseRootsForLocation(CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY,
|
||||
roots);
|
||||
GatherEnterpriseRootsForLocation(CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE,
|
||||
roots);
|
||||
static void GatherEnterpriseCertsWindows(Vector<EnterpriseCert>& certs) {
|
||||
GatherEnterpriseCertsForLocation(CERT_SYSTEM_STORE_LOCAL_MACHINE, certs);
|
||||
GatherEnterpriseCertsForLocation(CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY,
|
||||
certs);
|
||||
GatherEnterpriseCertsForLocation(CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE,
|
||||
certs);
|
||||
}
|
||||
#endif // XP_WIN
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
OSStatus GatherEnterpriseRootsOSX(Vector<Vector<uint8_t>>& roots) {
|
||||
OSStatus GatherEnterpriseCertsMacOS(Vector<EnterpriseCert>& certs) {
|
||||
// The following builds a search dictionary corresponding to:
|
||||
// { class: "certificate",
|
||||
// match limit: "match all",
|
||||
@ -204,33 +238,32 @@ OSStatus GatherEnterpriseRootsOSX(Vector<Vector<uint8_t>>& roots) {
|
||||
// a SecCertificateRef.
|
||||
const SecCertificateRef s = (const SecCertificateRef)c;
|
||||
ScopedCFType<CFDataRef> der(SecCertificateCopyData(s));
|
||||
const unsigned char* ptr = CFDataGetBytePtr(der.get());
|
||||
unsigned int len = CFDataGetLength(der.get());
|
||||
Vector<uint8_t> certBytes;
|
||||
if (!certBytes.append(ptr, len)) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Verbose,
|
||||
("couldn't copy cert bytes (out of memory?)"));
|
||||
EnterpriseCert enterpriseCert;
|
||||
// TODO(BUG 1526004) currently we treat all 3rd party certificates as trust
|
||||
// anchors on MacOS. This will be addressed in a follow-up bug.
|
||||
if (NS_FAILED(enterpriseCert.Init(CFDataGetBytePtr(der.get()),
|
||||
CFDataGetLength(der.get()), true))) {
|
||||
// Best-effort. We probably ran out of memory.
|
||||
continue;
|
||||
}
|
||||
if (!roots.append(std::move(certBytes))) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Verbose,
|
||||
("couldn't append cert bytes to roots list (out of memory?)"));
|
||||
if (!certs.append(std::move(enterpriseCert))) {
|
||||
// Best-effort again.
|
||||
continue;
|
||||
}
|
||||
numImported++;
|
||||
}
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("imported %u roots", numImported));
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("imported %u certs", numImported));
|
||||
return errSecSuccess;
|
||||
}
|
||||
#endif // XP_MACOSX
|
||||
|
||||
nsresult GatherEnterpriseRoots(Vector<Vector<uint8_t>>& roots) {
|
||||
roots.clear();
|
||||
nsresult GatherEnterpriseCerts(Vector<EnterpriseCert>& certs) {
|
||||
certs.clear();
|
||||
#ifdef XP_WIN
|
||||
GatherEnterpriseRootsWindows(roots);
|
||||
GatherEnterpriseCertsWindows(certs);
|
||||
#endif // XP_WIN
|
||||
#ifdef XP_MACOSX
|
||||
OSStatus rv = GatherEnterpriseRootsOSX(roots);
|
||||
OSStatus rv = GatherEnterpriseCertsMacOS(certs);
|
||||
if (rv != errSecSuccess) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -8,8 +8,27 @@
|
||||
#define EnterpriseRoots_h
|
||||
|
||||
#include "mozilla/Vector.h"
|
||||
#include "mozpkix/Input.h"
|
||||
#include "mozpkix/Result.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
nsresult GatherEnterpriseRoots(
|
||||
mozilla::Vector<mozilla::Vector<uint8_t>>& roots);
|
||||
class EnterpriseCert {
|
||||
public:
|
||||
EnterpriseCert() : mIsRoot(false) {}
|
||||
|
||||
nsresult Init(const uint8_t* data, size_t len, bool isRoot);
|
||||
// Like a copy constructor but able to return a result.
|
||||
nsresult Init(const EnterpriseCert& orig);
|
||||
|
||||
nsresult CopyBytes(nsTArray<uint8_t>& dest) const;
|
||||
mozilla::pkix::Result GetInput(mozilla::pkix::Input& input) const;
|
||||
bool GetIsRoot() const;
|
||||
|
||||
private:
|
||||
mozilla::Vector<uint8_t> mDER;
|
||||
bool mIsRoot;
|
||||
};
|
||||
|
||||
nsresult GatherEnterpriseCerts(mozilla::Vector<EnterpriseCert>& certs);
|
||||
|
||||
#endif // EnterpriseRoots_h
|
||||
|
@ -6,6 +6,7 @@
|
||||
#define SharedCertVerifier_h
|
||||
|
||||
#include "CertVerifier.h"
|
||||
#include "EnterpriseRoots.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
|
||||
@ -28,11 +29,11 @@ class SharedCertVerifier : public mozilla::psm::CertVerifier {
|
||||
NetscapeStepUpPolicy netscapeStepUpPolicy,
|
||||
CertificateTransparencyMode ctMode,
|
||||
DistrustedCAPolicy distrustedCAPolicy,
|
||||
const Vector<Vector<uint8_t>>& thirdPartyRoots)
|
||||
const Vector<EnterpriseCert>& thirdPartyCerts)
|
||||
: mozilla::psm::CertVerifier(
|
||||
odc, osc, ocspSoftTimeout, ocspHardTimeout, certShortLifetimeInDays,
|
||||
pinningMode, sha1Mode, nameMatchingMode, netscapeStepUpPolicy,
|
||||
ctMode, distrustedCAPolicy, thirdPartyRoots) {}
|
||||
ctMode, distrustedCAPolicy, thirdPartyCerts) {}
|
||||
};
|
||||
|
||||
} // namespace psm
|
||||
|
@ -68,6 +68,7 @@ EXTRA_JS_MODULES.psm += [
|
||||
|
||||
EXPORTS += [
|
||||
'CryptoTask.h',
|
||||
'EnterpriseRoots.h',
|
||||
'nsClientAuthRemember.h',
|
||||
'nsNSSCallbacks.h',
|
||||
'nsNSSCertificate.h',
|
||||
|
@ -54,6 +54,11 @@ interface nsINSSComponent : nsISupports {
|
||||
*/
|
||||
Array<Array<octet> > getEnterpriseRoots();
|
||||
|
||||
/**
|
||||
* Similarly, but for intermediate certificates.
|
||||
*/
|
||||
Array<Array<octet> > getEnterpriseIntermediates();
|
||||
|
||||
/**
|
||||
* For performance reasons, the builtin roots module is loaded on a background
|
||||
* thread. When any code that depends on the builtin roots module runs, it
|
||||
|
@ -482,7 +482,7 @@ void nsNSSComponent::UnloadEnterpriseRoots() {
|
||||
}
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("UnloadEnterpriseRoots"));
|
||||
MutexAutoLock lock(mMutex);
|
||||
mEnterpriseRoots.clear();
|
||||
mEnterpriseCerts.clear();
|
||||
setValidationOptions(false, lock);
|
||||
}
|
||||
|
||||
@ -510,34 +510,49 @@ void nsNSSComponent::MaybeImportEnterpriseRoots() {
|
||||
|
||||
void nsNSSComponent::ImportEnterpriseRoots() {
|
||||
MutexAutoLock lock(mMutex);
|
||||
nsresult rv = GatherEnterpriseRoots(mEnterpriseRoots);
|
||||
nsresult rv = GatherEnterpriseCerts(mEnterpriseCerts);
|
||||
if (NS_FAILED(rv)) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("failed gathering enterprise roots"));
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNSSComponent::GetEnterpriseRoots(
|
||||
nsTArray<nsTArray<uint8_t>>& enterpriseRoots) {
|
||||
nsresult nsNSSComponent::CommonGetEnterpriseCerts(
|
||||
nsTArray<nsTArray<uint8_t>>& enterpriseCerts, bool getRoots) {
|
||||
MutexAutoLock nsNSSComponentLock(mMutex);
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (!NS_IsMainThread()) {
|
||||
return NS_ERROR_NOT_SAME_THREAD;
|
||||
}
|
||||
|
||||
enterpriseRoots.Clear();
|
||||
for (const auto& root : mEnterpriseRoots) {
|
||||
nsTArray<uint8_t> rootCopy;
|
||||
if (!rootCopy.AppendElements(root.begin(), root.length())) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
if (!enterpriseRoots.AppendElement(std::move(rootCopy))) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
enterpriseCerts.Clear();
|
||||
for (const auto& cert : mEnterpriseCerts) {
|
||||
nsTArray<uint8_t> certCopy;
|
||||
// mEnterpriseCerts includes both roots and intermediates.
|
||||
if (cert.GetIsRoot() == getRoots) {
|
||||
nsresult rv = cert.CopyBytes(certCopy);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (!enterpriseCerts.AppendElement(std::move(certCopy))) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNSSComponent::GetEnterpriseRoots(
|
||||
nsTArray<nsTArray<uint8_t>>& enterpriseRoots) {
|
||||
return CommonGetEnterpriseCerts(enterpriseRoots, true);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsNSSComponent::GetEnterpriseIntermediates(
|
||||
nsTArray<nsTArray<uint8_t>>& enterpriseIntermediates) {
|
||||
return CommonGetEnterpriseCerts(enterpriseIntermediates, false);
|
||||
}
|
||||
|
||||
class LoadLoadableRootsTask final : public Runnable {
|
||||
public:
|
||||
LoadLoadableRootsTask(nsNSSComponent* nssComponent,
|
||||
@ -1199,7 +1214,7 @@ void nsNSSComponent::setValidationOptions(
|
||||
mDefaultCertVerifier = new SharedCertVerifier(
|
||||
odc, osc, softTimeout, hardTimeout, certShortLifetimeInDays, pinningMode,
|
||||
sha1Mode, nameMatchingMode, netscapeStepUpPolicy, ctMode,
|
||||
distrustedCAPolicy, mEnterpriseRoots);
|
||||
distrustedCAPolicy, mEnterpriseCerts);
|
||||
}
|
||||
|
||||
void nsNSSComponent::UpdateCertVerifierWithEnterpriseRoots() {
|
||||
@ -1218,7 +1233,7 @@ void nsNSSComponent::UpdateCertVerifierWithEnterpriseRoots() {
|
||||
oldCertVerifier->mCertShortLifetimeInDays, oldCertVerifier->mPinningMode,
|
||||
oldCertVerifier->mSHA1Mode, oldCertVerifier->mNameMatchingMode,
|
||||
oldCertVerifier->mNetscapeStepUpPolicy, oldCertVerifier->mCTMode,
|
||||
oldCertVerifier->mDistrustedCAPolicy, mEnterpriseRoots);
|
||||
oldCertVerifier->mDistrustedCAPolicy, mEnterpriseCerts);
|
||||
}
|
||||
|
||||
// Enable the TLS versions given in the prefs, defaulting to TLS 1.0 (min) and
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "nsINSSComponent.h"
|
||||
|
||||
#include "EnterpriseRoots.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "SharedCertVerifier.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
@ -86,6 +87,8 @@ class nsNSSComponent final : public nsINSSComponent, public nsIObserver {
|
||||
void MaybeImportEnterpriseRoots();
|
||||
void ImportEnterpriseRoots();
|
||||
void UnloadEnterpriseRoots();
|
||||
nsresult CommonGetEnterpriseCerts(
|
||||
nsTArray<nsTArray<uint8_t>>& enterpriseCerts, bool getRoots);
|
||||
|
||||
bool ShouldEnableEnterpriseRootsForFamilySafety(uint32_t familySafetyMode);
|
||||
|
||||
@ -106,7 +109,7 @@ class nsNSSComponent final : public nsINSSComponent, public nsIObserver {
|
||||
RefPtr<mozilla::psm::SharedCertVerifier> mDefaultCertVerifier;
|
||||
nsString mMitmCanaryIssuer;
|
||||
bool mMitmDetecionEnabled;
|
||||
mozilla::Vector<mozilla::Vector<uint8_t>> mEnterpriseRoots;
|
||||
mozilla::Vector<EnterpriseCert> mEnterpriseCerts;
|
||||
|
||||
// The following members are accessed only on the main thread:
|
||||
static int mInstanceCount;
|
||||
|
Loading…
Reference in New Issue
Block a user