bug 1520347 - fold Family Safety TLS interception feature into enterprise/third-party roots feature r=jcj
authorDana Keeler <dkeeler@mozilla.com>
Tue, 29 Jan 2019 20:10:39 +0000
changeset 455974 d29e9e51d4306f897bdb8fe86c1d1b7c306b3df0
parent 455973 303a009b3ef9c7053c82be92d538a97e7c40f092
child 455975 015cdc788e05a9dafc0abe737c9d16153f607b4c
push id35465
push usershindli@mozilla.com
push dateWed, 30 Jan 2019 04:10:12 +0000
treeherdermozilla-central@9e919be867b3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjcj
bugs1520347
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 1520347 - fold Family Safety TLS interception feature into enterprise/third-party roots feature r=jcj The Family Safety TLS interception feature is seldom used and security-wise is essentially equivalent to the enterprise or third-party roots feature. To simplify future improvements, this patch folds them together by automatically importing third-party roots if Firefox detects that the Family Safety TLS interception feature has been enabled. This affects Windows 8.1 only. When usage of Windows 8.1 is low enough, we will remove the feature altogether. Differential Revision: https://phabricator.services.mozilla.com/D16727
security/manager/ssl/EnterpriseRoots.cpp
security/manager/ssl/EnterpriseRoots.h
security/manager/ssl/nsNSSComponent.cpp
security/manager/ssl/nsNSSComponent.h
security/manager/ssl/security-prefs.js
--- a/security/manager/ssl/EnterpriseRoots.cpp
+++ b/security/manager/ssl/EnterpriseRoots.cpp
@@ -16,17 +16,16 @@
 #endif                         // XP_MACOSX
 
 extern LazyLogModule gPIPNSSLog;
 
 using namespace mozilla;
 
 #ifdef XP_WIN
 const wchar_t* kWindowsDefaultRootStoreName = L"ROOT";
-NS_NAMED_LITERAL_CSTRING(kMicrosoftFamilySafetyCN, "Microsoft Family Safety");
 
 // 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.
@@ -91,16 +90,34 @@ UniqueCERTCertificate PCCERT_CONTEXTToCE
   SECItem derCert = {siBuffer, pccert->pbCertEncoded, pccert->cbCertEncoded};
   return UniqueCERTCertificate(
       CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert,
                               nullptr,  // nickname unnecessary
                               false,    // not permanent
                               true));   // copy DER
 }
 
+// Because HCERTSTORE is just a typedef void*, we can't use any of the nice
+// 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); }
+
+  HCERTSTORE get() { return certstore; }
+
+ private:
+  ScopedCertStore(const ScopedCertStore&) = delete;
+  ScopedCertStore& operator=(const ScopedCertStore&) = delete;
+  HCERTSTORE certstore;
+};
+
 // Loads the enterprise roots at the registry location corresponding to the
 // given location flag.
 // Supported flags are:
 //   CERT_SYSTEM_STORE_LOCAL_MACHINE
 //     (for HKLM\SOFTWARE\Microsoft\SystemCertificates)
 //   CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY
 //     (for
 //     HKLM\SOFTWARE\Policies\Microsoft\SystemCertificates\Root\Certificates)
@@ -148,30 +165,23 @@ static void GatherEnterpriseRootsForLoca
       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 (kMicrosoftFamilySafetyCN.Equals(subjectName.get())) {
-      MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("skipping Family Safety Root"));
-      continue;
-    }
     if (CERT_AddCertToListTail(roots.get(), nssCertificate.get()) !=
         SECSuccess) {
       MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't add cert to list"));
       continue;
     }
-    MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Imported '%s'", subjectName.get()));
+    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
+            ("Imported '%s'", nssCertificate->subjectName));
     numImported++;
     // now owned by roots
     Unused << nssCertificate.release();
   }
   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("imported %u roots", numImported));
 }
 
 static void GatherEnterpriseRootsWindows(UniqueCERTCertList& roots) {
--- a/security/manager/ssl/EnterpriseRoots.h
+++ b/security/manager/ssl/EnterpriseRoots.h
@@ -5,44 +5,11 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef EnterpriseRoots_h
 #define EnterpriseRoots_h
 
 #include "ErrorList.h"
 #include "ScopedNSSTypes.h"
 
-#ifdef XP_WIN
-#  include "windows.h"  // this needs to be before the following includes
-#  include "wincrypt.h"
-#endif  // XP_WIN
-
 nsresult GatherEnterpriseRoots(mozilla::UniqueCERTCertList& result);
 
-#ifdef XP_WIN
-
-mozilla::UniqueCERTCertificate PCCERT_CONTEXTToCERTCertificate(
-    PCCERT_CONTEXT pccert);
-
-extern const wchar_t* kWindowsDefaultRootStoreName;
-extern const nsLiteralCString kMicrosoftFamilySafetyCN;
-
-// Because HCERTSTORE is just a typedef void*, we can't use any of the nice
-// 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); }
-
-  HCERTSTORE get() { return certstore; }
-
- private:
-  ScopedCertStore(const ScopedCertStore&) = delete;
-  ScopedCertStore& operator=(const ScopedCertStore&) = delete;
-  HCERTSTORE certstore;
-};
-
-#endif  // XP_WIN
-
 #endif  // EnterpriseRoots_h
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -439,150 +439,44 @@ static nsresult AccountHasFamilySafetyEn
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
             ("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;
 }
-
-nsresult nsNSSComponent::MaybeImportFamilySafetyRoot(
-    PCCERT_CONTEXT certificate, bool& wasFamilySafetyRoot) {
-  MutexAutoLock lock(mMutex);
-  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 (kMicrosoftFamilySafetyCN.Equals(subjectName.get())) {
-    wasFamilySafetyRoot = true;
-    MOZ_ASSERT(!mFamilySafetyRoot);
-    mFamilySafetyRoot = std::move(nssCertificate);
-    MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("added Family Safety root"));
-  }
-  return NS_OK;
-}
-
-nsresult nsNSSComponent::LoadFamilySafetyRoot() {
-  ScopedCertStore certstore(
-      CertOpenSystemStore(0, kWindowsDefaultRootStoreName));
-  if (!certstore.get()) {
-    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
-            ("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))) {
-    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;
-}
 #endif  // XP_WIN
 
-void nsNSSComponent::UnloadFamilySafetyRoot() {
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!NS_IsMainThread()) {
-    return;
-  }
-  MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("UnloadFamilySafetyRoot"));
-
-  // We can't call ChangeCertTrustWithPossibleAuthentication while holding
-  // mMutex (because it could potentially call back in to nsNSSComponent and
-  // attempt to acquire mMutex), so we move mFamilySafetyRoot out of
-  // nsNSSComponent into a local handle. This has the side-effect of clearing
-  // mFamilySafetyRoot, which is what we want anyway.
-  UniqueCERTCertificate familySafetyRoot;
-  {
-    MutexAutoLock lock(mMutex);
-    if (!mFamilySafetyRoot) {
-      MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
-              ("Family Safety Root wasn't present"));
-      return;
-    }
-    familySafetyRoot = std::move(mFamilySafetyRoot);
-    MOZ_ASSERT(!mFamilySafetyRoot);
-  }
-  MOZ_ASSERT(familySafetyRoot);
-  // 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 (ChangeCertTrustWithPossibleAuthentication(familySafetyRoot, trust,
-                                                nullptr) != SECSuccess) {
-    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
-            ("couldn't untrust certificate for TLS server auth"));
-  }
-}
-
-// 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
+// On Windows 8.1, if the following preference is 2, we will attempt to detect
+// if the Family Safety TLS interception feature has been enabled. If so, we
+// will behave as if the enterprise roots feature has been enabled (i.e. import
+// and trust third party root certificates from the OS).
+// With any other value of the pref or on any other platform, this does nothing.
+// This preference takes precedence over "security.enterprise_roots.enabled".
 const char* kFamilySafetyModePref = "security.family_safety.mode";
 const uint32_t kFamilySafetyModeDefault = 0;
 
-// 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
-void nsNSSComponent::MaybeEnableFamilySafetyCompatibility(
+bool nsNSSComponent::ShouldEnableEnterpriseRootsForFamilySafety(
     uint32_t familySafetyMode) {
 #ifdef XP_WIN
   if (!(IsWin8Point1OrLater() && !IsWin10OrLater())) {
-    return;
+    return false;
   }
-  if (familySafetyMode > 2) {
-    familySafetyMode = 0;
-  }
-  if (familySafetyMode == 0) {
-    return;
+  if (familySafetyMode != 2) {
+    return false;
   }
   bool familySafetyEnabled;
   nsresult rv = AccountHasFamilySafetyEnabled(familySafetyEnabled);
   if (NS_FAILED(rv)) {
-    return;
-  }
-  if (!familySafetyEnabled) {
-    return;
+    return false;
   }
-  if (familySafetyMode == 2) {
-    rv = LoadFamilySafetyRoot();
-    if (NS_FAILED(rv)) {
-      MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
-              ("failed to load Family Safety root"));
-    }
-  }
+  return familySafetyEnabled;
+#else
+  return false;
 #endif  // XP_WIN
 }
 
 void nsNSSComponent::UnloadEnterpriseRoots() {
   MOZ_ASSERT(NS_IsMainThread());
   if (!NS_IsMainThread()) {
     return;
   }
@@ -636,20 +530,26 @@ static const char* kEnterpriseRootModePr
 
 void nsNSSComponent::MaybeImportEnterpriseRoots() {
   MOZ_ASSERT(NS_IsMainThread());
   if (!NS_IsMainThread()) {
     return;
   }
   bool importEnterpriseRoots =
       Preferences::GetBool(kEnterpriseRootModePref, false);
-  if (!importEnterpriseRoots) {
-    return;
+  uint32_t familySafetyMode =
+      Preferences::GetUint(kFamilySafetyModePref, kFamilySafetyModeDefault);
+  // If we've been configured to detect the Family Safety TLS interception
+  // feature, see if it's enabled. If so, we want to import enterprise roots.
+  if (ShouldEnableEnterpriseRootsForFamilySafety(familySafetyMode)) {
+    importEnterpriseRoots = true;
   }
-  ImportEnterpriseRoots();
+  if (importEnterpriseRoots) {
+    ImportEnterpriseRoots();
+  }
 }
 
 void nsNSSComponent::ImportEnterpriseRoots() {
   UniqueCERTCertList roots;
   nsresult rv = GatherEnterpriseRoots(roots);
   if (NS_FAILED(rv)) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("failed gathering enterprise roots"));
     return;
@@ -690,35 +590,16 @@ nsresult nsNSSComponent::TrustLoaded3rdP
       UniqueCERTCertificate cert(CERT_DupCertificate(n->cert));
       if (ChangeCertTrustWithPossibleAuthentication(cert, trust, nullptr) !=
           SECSuccess) {
         MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
                 ("couldn't trust enterprise certificate for TLS server auth"));
       }
     }
   }
-#ifdef XP_WIN
-  // Again copy mFamilySafetyRoot so we don't hold mMutex while calling
-  // ChangeCertTrustWithPossibleAuthentication.
-  UniqueCERTCertificate familySafetyRoot;
-  {
-    MutexAutoLock lock(mMutex);
-    if (mFamilySafetyRoot) {
-      familySafetyRoot.reset(CERT_DupCertificate(mFamilySafetyRoot.get()));
-      if (!familySafetyRoot) {
-        return NS_ERROR_OUT_OF_MEMORY;
-      }
-    }
-  }
-  if (familySafetyRoot && ChangeCertTrustWithPossibleAuthentication(
-                              familySafetyRoot, trust, nullptr) != SECSuccess) {
-    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
-            ("couldn't trust family safety certificate for TLS server auth"));
-  }
-#endif
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNSSComponent::GetEnterpriseRoots(nsIX509CertList** enterpriseRoots) {
   MutexAutoLock nsNSSComponentLock(mMutex);
   MOZ_ASSERT(NS_IsMainThread());
   if (!NS_IsMainThread()) {
@@ -817,20 +698,25 @@ LoadLoadableRootsTask::Run() {
   if (NS_SUCCEEDED(loadLoadableRootsResult)) {
     if (NS_FAILED(LoadExtendedValidationInfo())) {
       // This isn't a show-stopper in the same way that failing to load the
       // roots module is.
       MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("failed to load EV info"));
     }
   }
 
+  // If we've been configured to detect the Family Safety TLS interception
+  // feature, see if it's enabled. If so, we want to import enterprise roots.
+  if (mNSSComponent->ShouldEnableEnterpriseRootsForFamilySafety(
+          mFamilySafetyMode)) {
+    mImportEnterpriseRoots = true;
+  }
   if (mImportEnterpriseRoots) {
     mNSSComponent->ImportEnterpriseRoots();
   }
-  mNSSComponent->MaybeEnableFamilySafetyCompatibility(mFamilySafetyMode);
   nsresult rv = mNSSComponent->TrustLoaded3rdPartyRoots();
   if (NS_FAILED(rv)) {
     MOZ_LOG(gPIPNSSLog, LogLevel::Error,
             ("failed to trust loaded 3rd party roots"));
   }
 
   {
     MonitorAutoLock rootsLoadedLock(mNSSComponent->mLoadableRootsLoadedMonitor);
@@ -1981,20 +1867,17 @@ void nsNSSComponent::ShutdownNSS() {
   // In that case, we don't want to block on an event that will never happen.
   if (loadLoadableRootsTaskDispatched) {
     Unused << BlockUntilLoadableRootsLoaded();
   }
 
   ::mozilla::psm::UnloadLoadableRoots();
 
   MutexAutoLock lock(mMutex);
-#ifdef XP_WIN
-  mFamilySafetyRoot = nullptr;
   mEnterpriseRoots = nullptr;
-#endif
 
   PK11_SetPasswordFunc((PK11PasswordFunc) nullptr);
 
   Preferences::RemoveObserver(this, "security.");
 
   // Release the default CertVerifier. This will cause any held NSS resources
   // to be released.
   mDefaultCertVerifier = nullptr;
@@ -2102,30 +1985,23 @@ nsNSSComponent::Observe(nsISupports* aSu
       setValidationOptions(false, lock);
 #ifdef DEBUG
     } else if (prefName.EqualsLiteral("security.test.built_in_root_hash")) {
       MutexAutoLock lock(mMutex);
       mTestBuiltInRootHash.Truncate();
       Preferences::GetString("security.test.built_in_root_hash",
                              mTestBuiltInRootHash);
 #endif  // DEBUG
-    } else if (prefName.Equals(kFamilySafetyModePref)) {
-      // When the pref changes, it is safe to change the trust of 3rd party
-      // roots in the same event tick that they're loaded.
-      UnloadFamilySafetyRoot();
-      uint32_t familySafetyMode =
-          Preferences::GetUint(kFamilySafetyModePref, kFamilySafetyModeDefault);
-      MaybeEnableFamilySafetyCompatibility(familySafetyMode);
-      TrustLoaded3rdPartyRoots();
     } else if (prefName.EqualsLiteral("security.content.signature.root_hash")) {
       MutexAutoLock lock(mMutex);
       mContentSigningRootHash.Truncate();
       Preferences::GetString("security.content.signature.root_hash",
                              mContentSigningRootHash);
-    } else if (prefName.Equals(kEnterpriseRootModePref)) {
+    } else if (prefName.Equals(kEnterpriseRootModePref) ||
+               prefName.Equals(kFamilySafetyModePref)) {
       // When the pref changes, it is safe to change the trust of 3rd party
       // roots in the same event tick that they're loaded.
       UnloadEnterpriseRoots();
       MaybeImportEnterpriseRoots();
       TrustLoaded3rdPartyRoots();
     } else if (prefName.EqualsLiteral("security.pki.mitm_canary_issuer")) {
       MutexAutoLock lock(mMutex);
       mMitmCanaryIssuer.Truncate();
--- a/security/manager/ssl/nsNSSComponent.h
+++ b/security/manager/ssl/nsNSSComponent.h
@@ -81,27 +81,20 @@ class nsNSSComponent final : public nsIN
                             const mozilla::MutexAutoLock& proofOfLock);
   nsresult setEnabledTLSVersions();
   nsresult RegisterObservers();
 
   void MaybeImportEnterpriseRoots();
   void ImportEnterpriseRoots();
   void UnloadEnterpriseRoots();
 
-  void MaybeEnableFamilySafetyCompatibility(uint32_t familySafetyMode);
-  void UnloadFamilySafetyRoot();
+  bool ShouldEnableEnterpriseRootsForFamilySafety(uint32_t familySafetyMode);
 
   nsresult TrustLoaded3rdPartyRoots();
 
-#ifdef XP_WIN
-  nsresult MaybeImportFamilySafetyRoot(PCCERT_CONTEXT certificate,
-                                       bool& wasFamilySafetyRoot);
-  nsresult LoadFamilySafetyRoot();
-#endif  // XP_WIN
-
   // mLoadableRootsLoadedMonitor protects mLoadableRootsLoaded.
   mozilla::Monitor mLoadableRootsLoadedMonitor;
   bool mLoadableRootsLoaded;
   nsresult mLoadableRootsLoadedResult;
 
   // mMutex protects all members that are accessed from more than one thread.
   mozilla::Mutex mMutex;
 
@@ -110,17 +103,16 @@ class nsNSSComponent final : public nsIN
 #ifdef DEBUG
   nsString mTestBuiltInRootHash;
 #endif
   nsString mContentSigningRootHash;
   RefPtr<mozilla::psm::SharedCertVerifier> mDefaultCertVerifier;
   nsString mMitmCanaryIssuer;
   bool mMitmDetecionEnabled;
   mozilla::UniqueCERTCertList mEnterpriseRoots;
-  mozilla::UniqueCERTCertificate mFamilySafetyRoot;
 
   // The following members are accessed only on the main thread:
   static int mInstanceCount;
   // If InitializeNSS succeeds, then we have dispatched an event to load the
   // loadable roots module on a background thread. We must wait for it to
   // complete before attempting to unload the module again in ShutdownNSS. If we
   // never dispatched the event, then we can't wait for it to complete (because
   // it will never complete) so we use this boolean to keep track of if we
--- a/security/manager/ssl/security-prefs.js
+++ b/security/manager/ssl/security-prefs.js
@@ -38,21 +38,22 @@ pref("security.ssl3.rsa_des_ede3_sha", t
 pref("security.content.signature.root_hash",
      "97:E8:BA:9C:F1:2F:B3:DE:53:CC:42:A4:E6:57:7E:D6:4D:F4:93:C2:47:B4:14:FE:A0:36:81:8D:38:23:56:0E");
 
 pref("security.default_personal_cert",   "Ask Every Time");
 pref("security.remember_cert_checkbox_default_setting", true);
 pref("security.ask_for_password",        0);
 pref("security.password_lifetime",       30);
 
-// 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
-// (This is only relevant to Windows 8.1)
+// On Windows 8.1, if the following preference is 2, we will attempt to detect
+// if the Family Safety TLS interception feature has been enabled. If so, we
+// will behave as if the enterprise roots feature has been enabled (i.e. import
+// and trust third party root certificates from the OS).
+// With any other value of the pref or on any other platform, this does nothing.
+// This preference takes precedence over "security.enterprise_roots.enabled".
 pref("security.family_safety.mode", 2);
 
 pref("security.enterprise_roots.enabled", false);
 
 // The supported values of this pref are:
 // 0: do not fetch OCSP
 // 1: fetch OCSP for DV and EV certificates
 // 2: fetch OCSP only for EV certificates