--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -7,23 +7,25 @@
#include "nsNSSComponent.h"
#include "ExtendedValidation.h"
#include "NSSCertDBTrustDomain.h"
#include "ScopedNSSTypes.h"
#include "SharedSSLState.h"
#include "cert.h"
#include "certdb.h"
+#include "mozilla/ArrayUtils.h"
#include "mozilla/Casting.h"
#include "mozilla/Preferences.h"
#include "mozilla/PublicSSL.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/SyncRunnable.h"
#include "mozilla/Telemetry.h"
+#include "mozilla/unused.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsCRT.h"
#include "nsCertVerificationThread.h"
#include "nsClientAuthRemember.h"
#include "nsComponentManagerUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsICertOverrideService.h"
#include "nsIFile.h"
@@ -437,17 +439,17 @@ GetUserSid(nsAString& sidString)
DWORD lcAccountName = sizeof(lpAccountName) / sizeof(lpAccountName[0]);
BOOL success = GetUserName(lpAccountName, &lcAccountName);
if (!success) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("GetUserName failed"));
return false;
}
char sid_buffer[SECURITY_MAX_SID_SIZE];
SID* sid = BitwiseCast<SID*, char*>(sid_buffer);
- DWORD cbSid = MOZ_ARRAY_LENGTH(sid_buffer);
+ DWORD cbSid = ArrayLength(sid_buffer);
SID_NAME_USE eUse;
// There doesn't appear to be a defined maximum length for the domain name
// here. To deal with this, we start with a reasonable buffer length and
// see if that works. If it fails and the error indicates insufficient length,
// we use the indicated required length and try again.
DWORD cchReferencedDomainName = 128;
auto ReferencedDomainName(MakeUnique<WCHAR[]>(cchReferencedDomainName));
success = LookupAccountName(nullptr, lpAccountName, sid, &cbSid,
@@ -647,77 +649,92 @@ AccountHasFamilySafetyEnabled(bool& enab
("failed to read value of Web\\Filter On"));
return rv;
}
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Web\\Filter On: %u", webFilterOn));
enabled = loggingRequired == 1 || webFilterOn == 1;
return NS_OK;
}
-const char* kImportedFamilySafetyRootPref =
- "security.family_safety.imported_root.db_key";
+// It would be convenient to just use nsIX509CertDB in the following code.
+// However, since nsIX509CertDB depends on nsNSSComponent initialization (and
+// since this code runs during that initialization), we can't use it. Instead,
+// we can use NSS APIs directly (as long as we're called late enough in
+// nsNSSComponent initialization such that those APIs are safe to use).
-static nsresult
-MaybeImportFamilySafetyRoot(PCCERT_CONTEXT certificate,
- bool& wasFamilySafetyRoot)
+// Helper function to convert a PCCERT_CONTEXT (i.e. a certificate obtained via
+// a Windows API) to a temporary CERTCertificate (i.e. a certificate for use
+// with NSS APIs).
+static UniqueCERTCertificate
+PCCERT_CONTEXTToCERTCertificate(PCCERT_CONTEXT pccert)
{
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("MaybeImportFamilySafetyRoot"));
- wasFamilySafetyRoot = false;
-
- // It would be convenient to just use nsIX509CertDB here. However, since
- // nsIX509CertDB depends on nsNSSComponent initialization, we can't use it.
- // Instead, we can use NSS APIs directly (as long as we're called late enough
- // in nsNSSComponent initialization such that those APIs are safe to use).
+ MOZ_ASSERT(pccert);
+ if (!pccert) {
+ return nullptr;
+ }
SECItem derCert = {
siBuffer,
- certificate->pbCertEncoded,
- certificate->cbCertEncoded
+ pccert->pbCertEncoded,
+ pccert->cbCertEncoded
};
- UniqueCERTCertificate nssCertificate(
+ return UniqueCERTCertificate(
CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert,
nullptr, // nickname unnecessary
false, // not permanent
true)); // copy DER
+}
+
+static const char* kMicrosoftFamilySafetyCN = "Microsoft Family Safety";
+
+nsresult
+nsNSSComponent::MaybeImportFamilySafetyRoot(PCCERT_CONTEXT certificate,
+ bool& wasFamilySafetyRoot)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!NS_IsMainThread()) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("MaybeImportFamilySafetyRoot"));
+ wasFamilySafetyRoot = false;
+
+ UniqueCERTCertificate nssCertificate(
+ PCCERT_CONTEXTToCERTCertificate(certificate));
if (!nssCertificate) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't decode certificate"));
return NS_ERROR_FAILURE;
}
// Looking for a certificate with the common name 'Microsoft Family Safety'
UniquePORTString subjectName(CERT_GetCommonName(&nssCertificate->subject));
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("subject name is '%s'", subjectName.get()));
- if (nsCRT::strcmp(subjectName.get(), "Microsoft Family Safety") == 0) {
+ if (nsCRT::strcmp(subjectName.get(), kMicrosoftFamilySafetyCN) == 0) {
wasFamilySafetyRoot = true;
CERTCertTrust trust = {
CERTDB_TRUSTED_CA | CERTDB_VALID_CA | CERTDB_USER,
0,
0
};
- SECStatus srv = __CERT_AddTempCertToPerm(
- nssCertificate.get(), "Microsoft Family Safety", &trust);
- if (srv != SECSuccess) {
+ if (CERT_ChangeCertTrust(nullptr, nssCertificate.get(), &trust)
+ != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("couldn't permanently add certificate"));
+ ("couldn't trust certificate for TLS server auth"));
return NS_ERROR_FAILURE;
}
- nsAutoCString dbKey;
- nsresult rv = nsNSSCertificate::GetDbKey(nssCertificate, dbKey);
- if (NS_FAILED(rv)) {
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("GetDbKey failed"));
- return rv;
- }
- Preferences::SetCString(kImportedFamilySafetyRootPref, dbKey);
+ MOZ_ASSERT(!mFamilySafetyRoot);
+ mFamilySafetyRoot = Move(nssCertificate);
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("added Family Safety root"));
}
return NS_OK;
}
// Because HCERTSTORE is just a typedef void*, we can't use any of the nice
-// scoped pointer templates.
+// scoped or unique pointer templates. To elaborate, any attempt would
+// instantiate those templates with T = void. When T gets used in the context
+// of T&, this results in void&, which isn't legal.
class ScopedCertStore final
{
public:
explicit ScopedCertStore(HCERTSTORE certstore) : certstore(certstore) {}
~ScopedCertStore()
{
CertCloseStore(certstore, 0);
@@ -729,77 +746,71 @@ public:
}
private:
ScopedCertStore(const ScopedCertStore&) = delete;
ScopedCertStore& operator=(const ScopedCertStore&) = delete;
HCERTSTORE certstore;
};
-static const wchar_t* WindowsDefaultRootStoreName = L"ROOT";
+static const wchar_t* kWindowsDefaultRootStoreName = L"ROOT";
-static nsresult
-LoadFamilySafetyRoot()
+nsresult
+nsNSSComponent::LoadFamilySafetyRoot()
{
ScopedCertStore certstore(
- CertOpenSystemStore(0, WindowsDefaultRootStoreName));
+ CertOpenSystemStore(0, kWindowsDefaultRootStoreName));
if (!certstore.get()) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("couldn't get certstore '%S'", WindowsDefaultRootStoreName));
+ ("couldn't get certstore '%S'", kWindowsDefaultRootStoreName));
return NS_ERROR_FAILURE;
}
// Any resources held by the certificate are released by the next call to
// CertFindCertificateInStore.
PCCERT_CONTEXT certificate = nullptr;
- while (certificate = CertFindCertificateInStore(certstore.get(),
- X509_ASN_ENCODING, 0,
- CERT_FIND_ANY, nullptr,
- certificate)) {
+ while ((certificate = CertFindCertificateInStore(certstore.get(),
+ X509_ASN_ENCODING, 0,
+ CERT_FIND_ANY, nullptr,
+ certificate))) {
bool wasFamilySafetyRoot = false;
nsresult rv = MaybeImportFamilySafetyRoot(certificate,
wasFamilySafetyRoot);
if (NS_SUCCEEDED(rv) && wasFamilySafetyRoot) {
return NS_OK; // We're done (we're only expecting one root).
}
}
return NS_ERROR_FAILURE;
}
-static void
-UnloadFamilySafetyRoot()
+void
+nsNSSComponent::UnloadFamilySafetyRoot()
{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!NS_IsMainThread()) {
+ return;
+ }
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("UnloadFamilySafetyRoot"));
- nsAdoptingCString dbKey = Preferences::GetCString(
- kImportedFamilySafetyRootPref);
- if (!dbKey || dbKey.IsEmpty()) {
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("Family Safety root wasn't previously imported"));
+ if (!mFamilySafetyRoot) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Family Safety Root wasn't present"));
return;
}
- UniqueCERTCertificate cert;
- nsresult rv = nsNSSCertificateDB::FindCertByDBKey(dbKey, cert);
- if (NS_FAILED(rv)) {
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("finding previously-imported Family Safety root failed"));
- return;
- }
- if (!cert) {
+ // It would be intuitive to set the trust to { 0, 0, 0 } here. However, this
+ // doesn't work for temporary certificates because CERT_ChangeCertTrust first
+ // looks up the current trust settings in the permanent cert database, finds
+ // that such trust doesn't exist, considers the current trust to be
+ // { 0, 0, 0 }, and decides that it doesn't need to update the trust since
+ // they're the same. To work around this, we set a non-zero flag to ensure
+ // that the trust will get updated.
+ CERTCertTrust trust = { CERTDB_USER, 0, 0 };
+ if (CERT_ChangeCertTrust(nullptr, mFamilySafetyRoot.get(), &trust)
+ != SECSuccess) {
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("previously-imported Family Safety root not found"));
- return;
+ ("couldn't untrust certificate for TLS server auth"));
}
- SECStatus srv = SEC_DeletePermCertificate(cert.get());
- if (srv != SECSuccess) {
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("couldn't delete previously-imported Family Safety root"));
- return;
- }
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("deleted previously-imported Family Safety root"));
- Preferences::ClearUser(kImportedFamilySafetyRootPref);
+ mFamilySafetyRoot = nullptr;
}
#endif // XP_WIN
// The supported values of this pref are:
// 0: disable detecting Family Safety mode and importing the root
// 1: only attempt to detect Family Safety mode (don't import the root)
// 2: detect Family Safety mode and import the root
@@ -807,18 +818,18 @@ const char* kFamilySafetyModePref = "sec
// The telemetry gathered by this function is as follows:
// 0-2: the value of the Family Safety mode pref
// 3: detecting Family Safety mode failed
// 4: Family Safety was not enabled
// 5: Family Safety was enabled
// 6: failed to import the Family Safety root
// 7: successfully imported the root
-static void
-MaybeEnableFamilySafetyCompatibility()
+void
+nsNSSComponent::MaybeEnableFamilySafetyCompatibility()
{
#ifdef XP_WIN
UnloadFamilySafetyRoot();
if (!(IsWin8Point1OrLater() && !IsWin10OrLater())) {
return;
}
// Detect but don't import by default.
uint32_t familySafetyMode = Preferences::GetUint(kFamilySafetyModePref, 1);
@@ -848,16 +859,221 @@ MaybeEnableFamilySafetyCompatibility()
("failed to load Family Safety root"));
} else {
Telemetry::Accumulate(Telemetry::FAMILY_SAFETY, 7);
}
}
#endif // XP_WIN
}
+#ifdef XP_WIN
+// 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
+// context of importing what are presumed to be root certificates from the OS.
+// If this function returns true but it turns out that the given certificate is
+// 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)
+{
+ MOZ_ASSERT(certificate);
+ if (!certificate) {
+ return false;
+ }
+
+ PCCERT_CHAIN_CONTEXT pChainContext = nullptr;
+ CERT_ENHKEY_USAGE enhkeyUsage;
+ memset(&enhkeyUsage, 0, sizeof(CERT_ENHKEY_USAGE));
+ LPSTR identifiers[] = {
+ "1.3.6.1.5.5.7.3.1", // id-kp-serverAuth
+ };
+ enhkeyUsage.cUsageIdentifier = ArrayLength(identifiers);
+ enhkeyUsage.rgpszUsageIdentifier = identifiers;
+ CERT_USAGE_MATCH certUsage;
+ memset(&certUsage, 0, sizeof(CERT_USAGE_MATCH));
+ certUsage.dwType = USAGE_MATCH_TYPE_AND;
+ certUsage.Usage = enhkeyUsage;
+ CERT_CHAIN_PARA chainPara;
+ memset(&chainPara, 0, sizeof(CERT_CHAIN_PARA));
+ chainPara.cbSize = sizeof(CERT_CHAIN_PARA);
+ chainPara.RequestedUsage = certUsage;
+
+ if (!CertGetCertificateChain(nullptr, certificate, nullptr, nullptr,
+ &chainPara, 0, nullptr, &pChainContext)) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("CertGetCertificateChain failed"));
+ return false;
+ }
+ 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;
+}
+
+void
+nsNSSComponent::UnloadEnterpriseRoots()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!NS_IsMainThread()) {
+ return;
+ }
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("UnloadEnterpriseRoots"));
+ if (!mEnterpriseRoots) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("no enterprise roots were present"));
+ return;
+ }
+ // It would be intuitive to set the trust to { 0, 0, 0 } here. However, this
+ // doesn't work for temporary certificates because CERT_ChangeCertTrust first
+ // looks up the current trust settings in the permanent cert database, finds
+ // that such trust doesn't exist, considers the current trust to be
+ // { 0, 0, 0 }, and decides that it doesn't need to update the trust since
+ // they're the same. To work around this, we set a non-zero flag to ensure
+ // that the trust will get updated.
+ CERTCertTrust trust = { CERTDB_USER, 0, 0 };
+ for (CERTCertListNode* n = CERT_LIST_HEAD(mEnterpriseRoots.get());
+ !CERT_LIST_END(n, mEnterpriseRoots.get()); n = CERT_LIST_NEXT(n)) {
+ if (!n || !n->cert) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
+ ("library failure: CERTCertListNode null or lacks cert"));
+ continue;
+ }
+ if (CERT_ChangeCertTrust(nullptr, n->cert, &trust) != SECSuccess) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
+ ("couldn't untrust certificate for TLS server auth"));
+ }
+ }
+ mEnterpriseRoots = nullptr;
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("unloaded enterprise roots"));
+}
+
+NS_IMETHODIMP
+nsNSSComponent::GetEnterpriseRoots(nsIX509CertList** enterpriseRoots)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!NS_IsMainThread()) {
+ return NS_ERROR_NOT_SAME_THREAD;
+ }
+ NS_ENSURE_ARG_POINTER(enterpriseRoots);
+
+ nsNSSShutDownPreventionLock lock;
+ // nsNSSComponent isn't a nsNSSShutDownObject, so we can't check
+ // isAlreadyShutDown(). However, since mEnterpriseRoots is cleared when NSS
+ // shuts down, we can use that as a proxy for checking for NSS shutdown.
+ // (Of course, it may also be the case that no enterprise roots were imported,
+ // so we should just return a null list and NS_OK in this case.)
+ if (!mEnterpriseRoots) {
+ *enterpriseRoots = nullptr;
+ return NS_OK;
+ }
+ UniqueCERTCertList enterpriseRootsCopy(
+ nsNSSCertList::DupCertList(mEnterpriseRoots, lock));
+ if (!enterpriseRootsCopy) {
+ return NS_ERROR_FAILURE;
+ }
+ nsCOMPtr<nsIX509CertList> enterpriseRootsCertList(
+ new nsNSSCertList(Move(enterpriseRootsCopy), lock));
+ if (!enterpriseRootsCertList) {
+ return NS_ERROR_FAILURE;
+ }
+ enterpriseRootsCertList.forget(enterpriseRoots);
+ return NS_OK;
+}
+#endif // XP_WIN
+
+static const char* kEnterpriseRootModePref = "security.enterprise_roots.enabled";
+
+void
+nsNSSComponent::MaybeImportEnterpriseRoots()
+{
+#ifdef XP_WIN
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!NS_IsMainThread()) {
+ return;
+ }
+ UnloadEnterpriseRoots();
+ bool importEnterpriseRoots = Preferences::GetBool(kEnterpriseRootModePref,
+ false);
+ if (!importEnterpriseRoots) {
+ return;
+ }
+ DWORD flags = CERT_SYSTEM_STORE_LOCAL_MACHINE |
+ CERT_STORE_OPEN_EXISTING_FLAG |
+ CERT_STORE_READONLY_FLAG;
+ // The certificate store being opened should consist only of certificates
+ // added by a user or administrator and not any certificates that are part
+ // 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;
+ }
+ MOZ_ASSERT(!mEnterpriseRoots);
+ mEnterpriseRoots.reset(CERT_NewCertList());
+ CERTCertTrust trust = {
+ CERTDB_TRUSTED_CA | CERTDB_VALID_CA | CERTDB_USER,
+ 0,
+ 0
+ };
+ 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)) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
+ ("skipping cert not trust anchor for TLS server auth"));
+ continue;
+ }
+ UniqueCERTCertificate nssCertificate(
+ PCCERT_CONTEXTToCERTCertificate(certificate));
+ if (!nssCertificate) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't decode certificate"));
+ continue;
+ }
+ // Don't import the Microsoft Family Safety root (this prevents the
+ // Enterprise Roots feature from interacting poorly with the Family
+ // Safety support).
+ UniquePORTString subjectName(
+ CERT_GetCommonName(&nssCertificate->subject));
+ if (nsCRT::strcmp(subjectName.get(), kMicrosoftFamilySafetyCN) == 0) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("skipping Family Safety Root"));
+ continue;
+ }
+ if (CERT_AddCertToListTail(mEnterpriseRoots.get(), nssCertificate.get())
+ != SECSuccess) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't add cert to list"));
+ continue;
+ }
+ if (CERT_ChangeCertTrust(nullptr, nssCertificate.get(), &trust)
+ != SECSuccess) {
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
+ ("couldn't trust certificate for TLS server auth"));
+ }
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Imported '%s'", subjectName.get()));
+ numImported++;
+ // now owned by mEnterpriseRoots
+ Unused << nssCertificate.release();
+ }
+ MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("imported %u roots", numImported));
+#endif // XP_WIN
+}
+
void
nsNSSComponent::LoadLoadableRoots()
{
nsNSSShutDownPreventionLock locker;
SECMODModule* RootsModule = nullptr;
// In the past we used SECMOD_AddNewModule to load our module containing
// root CA certificates. This caused problems, refer to bug 176501.
@@ -1554,16 +1770,17 @@ nsNSSComponent::InitializeNSS()
}
DisableMD5();
// Initialize the certverifier log before calling any functions that library.
InitCertVerifierLog();
LoadLoadableRoots();
MaybeEnableFamilySafetyCompatibility();
+ MaybeImportEnterpriseRoots();
ConfigureTLSSessionIdentifiers();
bool requireSafeNegotiation =
Preferences::GetBool("security.ssl.require_safe_negotiation",
REQUIRE_SAFE_NEGOTIATION_DEFAULT);
SSL_OptionSetDefault(SSL_REQUIRE_SAFE_NEGOTIATION, requireSafeNegotiation);
@@ -1641,16 +1858,21 @@ nsNSSComponent::ShutdownNSS()
if (mNSSInitialized) {
mNSSInitialized = false;
PK11_SetPasswordFunc((PK11PasswordFunc)nullptr);
Preferences::RemoveObserver(this, "security.");
+#ifdef XP_WIN
+ mFamilySafetyRoot = nullptr;
+ mEnterpriseRoots = nullptr;
+#endif
+
#ifndef MOZ_NO_SMART_CARDS
ShutdownSmartCardThreads();
#endif
SSL_ClearSessionCache();
UnloadLoadableRoots();
#ifndef MOZ_NO_EV_CERTS
CleanupIdentityInfo();
#endif
@@ -1781,16 +2003,18 @@ nsNSSComponent::Observe(nsISupports* aSu
mTestBuiltInRootHash = Preferences::GetString("security.test.built_in_root_hash");
#endif // DEBUG
} else if (prefName.Equals(kFamilySafetyModePref)) {
MaybeEnableFamilySafetyCompatibility();
} else if (prefName.EqualsLiteral("security.content.signature.root_hash")) {
MutexAutoLock lock(mutex);
mContentSigningRootHash =
Preferences::GetString("security.content.signature.root_hash");
+ } else if (prefName.Equals(kEnterpriseRootModePref)) {
+ MaybeImportEnterpriseRoots();
} else {
clearSessionCache = false;
}
if (clearSessionCache)
SSL_ClearSessionCache();
}
return NS_OK;