|
|
|
@ -4,16 +4,21 @@
|
|
|
|
|
* 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/. */
|
|
|
|
|
|
|
|
|
|
#define CERT_AddTempCertToPerm __CERT_AddTempCertToPerm
|
|
|
|
|
|
|
|
|
|
#include "nsNSSComponent.h"
|
|
|
|
|
|
|
|
|
|
#include "ExtendedValidation.h"
|
|
|
|
|
#include "NSSCertDBTrustDomain.h"
|
|
|
|
|
#include "SharedSSLState.h"
|
|
|
|
|
#include "cert.h"
|
|
|
|
|
#include "certdb.h"
|
|
|
|
|
#include "mozilla/Preferences.h"
|
|
|
|
|
#include "mozilla/PublicSSL.h"
|
|
|
|
|
#include "mozilla/Services.h"
|
|
|
|
|
#include "mozilla/StaticPtr.h"
|
|
|
|
|
#include "mozilla/Telemetry.h"
|
|
|
|
|
#include "mozilla/UniquePtr.h"
|
|
|
|
|
#include "nsAppDirectoryServiceDefs.h"
|
|
|
|
|
#include "nsCRT.h"
|
|
|
|
|
#include "nsCertVerificationThread.h"
|
|
|
|
@ -30,6 +35,7 @@
|
|
|
|
|
#include "nsITokenPasswordDialogs.h"
|
|
|
|
|
#include "nsIWindowWatcher.h"
|
|
|
|
|
#include "nsIXULRuntime.h"
|
|
|
|
|
#include "nsNSSCertificateDB.h"
|
|
|
|
|
#include "nsNSSHelper.h"
|
|
|
|
|
#include "nsNSSShutDown.h"
|
|
|
|
|
#include "nsServiceManagerUtils.h"
|
|
|
|
@ -49,7 +55,14 @@
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef XP_WIN
|
|
|
|
|
#include "mozilla/WindowsVersion.h"
|
|
|
|
|
#include "nsILocalFileWin.h"
|
|
|
|
|
|
|
|
|
|
#include "windows.h" // this needs to be before the following includes
|
|
|
|
|
#include "Lmcons.h"
|
|
|
|
|
#include "Sddl.h"
|
|
|
|
|
#include "Wincrypt.h"
|
|
|
|
|
#include "nsIWindowsRegKey.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
using namespace mozilla;
|
|
|
|
@ -394,6 +407,433 @@ nsNSSComponent::ShutdownSmartCardThreads()
|
|
|
|
|
}
|
|
|
|
|
#endif // MOZ_NO_SMART_CARDS
|
|
|
|
|
|
|
|
|
|
#ifdef XP_WIN
|
|
|
|
|
static bool
|
|
|
|
|
GetUserSid(nsAString& sidString)
|
|
|
|
|
{
|
|
|
|
|
// UNLEN is the maximum user name length (see Lmcons.h). +1 for the null
|
|
|
|
|
// terminator.
|
|
|
|
|
WCHAR lpAccountName[UNLEN + 1];
|
|
|
|
|
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 = reinterpret_cast<SID*>(sid_buffer);
|
|
|
|
|
DWORD cbSid = MOZ_ARRAY_LENGTH(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,
|
|
|
|
|
ReferencedDomainName.get(),
|
|
|
|
|
&cchReferencedDomainName, &eUse);
|
|
|
|
|
if (!success && GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("LookupAccountName failed"));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (!success) {
|
|
|
|
|
ReferencedDomainName = MakeUnique<WCHAR[]>(cchReferencedDomainName);
|
|
|
|
|
success = LookupAccountName(nullptr, lpAccountName, sid, &cbSid,
|
|
|
|
|
ReferencedDomainName.get(),
|
|
|
|
|
&cchReferencedDomainName, &eUse);
|
|
|
|
|
}
|
|
|
|
|
if (!success) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("LookupAccountName failed"));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
LPTSTR StringSid;
|
|
|
|
|
success = ConvertSidToStringSid(sid, &StringSid);
|
|
|
|
|
if (!success) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("ConvertSidToStringSid failed"));
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
sidString.Assign(StringSid);
|
|
|
|
|
LocalFree(StringSid);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This is a specialized helper function to read the value of a registry key
|
|
|
|
|
// that might not be present. If it is present, returns (via the output
|
|
|
|
|
// parameter) its value. Otherwise, returns the given default value.
|
|
|
|
|
// This function handles one level of nesting. That is, if the desired value
|
|
|
|
|
// is actually in a direct child of the given registry key (where the child
|
|
|
|
|
// and/or the value being sought may not actually be present), this function
|
|
|
|
|
// will handle that. In the normal case, though, optionalChildName will be
|
|
|
|
|
// null.
|
|
|
|
|
static nsresult
|
|
|
|
|
ReadRegKeyValueWithDefault(nsCOMPtr<nsIWindowsRegKey> regKey,
|
|
|
|
|
uint32_t flags,
|
|
|
|
|
wchar_t* optionalChildName,
|
|
|
|
|
wchar_t* valueName,
|
|
|
|
|
uint32_t defaultValue,
|
|
|
|
|
uint32_t& valueOut)
|
|
|
|
|
{
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("ReadRegKeyValueWithDefault"));
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
|
|
|
("attempting to read '%S%s%S' with default '%u'",
|
|
|
|
|
optionalChildName ? optionalChildName : L"",
|
|
|
|
|
optionalChildName ? "\\" : "", valueName, defaultValue));
|
|
|
|
|
if (optionalChildName) {
|
|
|
|
|
nsDependentString childNameString(optionalChildName);
|
|
|
|
|
bool hasChild;
|
|
|
|
|
nsresult rv = regKey->HasChild(childNameString, &hasChild);
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
|
|
|
("failed to determine if child key is present"));
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
if (!hasChild) {
|
|
|
|
|
valueOut = defaultValue;
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
nsCOMPtr<nsIWindowsRegKey> childRegKey;
|
|
|
|
|
rv = regKey->OpenChild(childNameString, flags,
|
|
|
|
|
getter_AddRefs(childRegKey));
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't open child key"));
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
return ReadRegKeyValueWithDefault(childRegKey, flags, nullptr, valueName,
|
|
|
|
|
defaultValue, valueOut);
|
|
|
|
|
}
|
|
|
|
|
nsDependentString valueNameString(valueName);
|
|
|
|
|
bool hasValue;
|
|
|
|
|
nsresult rv = regKey->HasValue(valueNameString, &hasValue);
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
|
|
|
("failed to determine if value is present"));
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
if (!hasValue) {
|
|
|
|
|
valueOut = defaultValue;
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
rv = regKey->ReadIntValue(valueNameString, &valueOut);
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("failed to read value"));
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static nsresult
|
|
|
|
|
AccountHasFamilySafetyEnabled(bool& enabled)
|
|
|
|
|
{
|
|
|
|
|
enabled = false;
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("AccountHasFamilySafetyEnabled?"));
|
|
|
|
|
nsCOMPtr<nsIWindowsRegKey> parentalControlsKey(
|
|
|
|
|
do_CreateInstance("@mozilla.org/windows-registry-key;1"));
|
|
|
|
|
if (!parentalControlsKey) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't create nsIWindowsRegKey"));
|
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
uint32_t flags = nsIWindowsRegKey::ACCESS_READ | nsIWindowsRegKey::WOW64_64;
|
|
|
|
|
NS_NAMED_LITERAL_STRING(familySafetyPath,
|
|
|
|
|
"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Parental Controls");
|
|
|
|
|
nsresult rv = parentalControlsKey->Open(
|
|
|
|
|
nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE, familySafetyPath, flags);
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't open parentalControlsKey"));
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
NS_NAMED_LITERAL_STRING(usersString, "Users");
|
|
|
|
|
bool hasUsers;
|
|
|
|
|
rv = parentalControlsKey->HasChild(usersString, &hasUsers);
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("HasChild(Users) failed"));
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
if (!hasUsers) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
|
|
|
("Users subkey not present - Parental Controls not enabled"));
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
nsCOMPtr<nsIWindowsRegKey> usersKey;
|
|
|
|
|
rv = parentalControlsKey->OpenChild(usersString, flags,
|
|
|
|
|
getter_AddRefs(usersKey));
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("failed to open Users subkey"));
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
nsAutoString sid;
|
|
|
|
|
if (!GetUserSid(sid)) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't get sid"));
|
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("our sid is '%S'", sid.get()));
|
|
|
|
|
bool hasSid;
|
|
|
|
|
rv = usersKey->HasChild(sid, &hasSid);
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("HasChild(sid) failed"));
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
if (!hasSid) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
|
|
|
("sid not present in Family Safety Users"));
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
nsCOMPtr<nsIWindowsRegKey> sidKey;
|
|
|
|
|
rv = usersKey->OpenChild(sid, flags, getter_AddRefs(sidKey));
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't open sid key"));
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
// There are three keys we're interested in: "Parental Controls On",
|
|
|
|
|
// "Logging Required", and "Web\\Filter On". These keys will have value 0
|
|
|
|
|
// or 1, indicating a particular feature is disabled or enabled,
|
|
|
|
|
// respectively. So, if "Parental Controls On" is not 1, Family Safety is
|
|
|
|
|
// disabled and we don't care about anything else. If both "Logging
|
|
|
|
|
// Required" and "Web\\Filter On" are 0, the proxy will not be running,
|
|
|
|
|
// so for our purposes we can consider Family Safety disabled in that
|
|
|
|
|
// case.
|
|
|
|
|
// By default, "Logging Required" is 1 and "Web\\Filter On" is 0,
|
|
|
|
|
// reflecting the initial settings when Family Safety is enabled for an
|
|
|
|
|
// account for the first time, However, these sub-keys are not created
|
|
|
|
|
// unless they are switched away from the default value.
|
|
|
|
|
uint32_t parentalControlsOn;
|
|
|
|
|
rv = sidKey->ReadIntValue(NS_LITERAL_STRING("Parental Controls On"),
|
|
|
|
|
&parentalControlsOn);
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
|
|
|
("couldn't read Parental Controls On"));
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
|
|
|
("Parental Controls On: %u", parentalControlsOn));
|
|
|
|
|
if (parentalControlsOn != 1) {
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
|
uint32_t loggingRequired;
|
|
|
|
|
rv = ReadRegKeyValueWithDefault(sidKey, flags, nullptr, L"Logging Required",
|
|
|
|
|
1, loggingRequired);
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
|
|
|
("failed to read value of Logging Required"));
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
|
|
|
("Logging Required: %u", loggingRequired));
|
|
|
|
|
uint32_t webFilterOn;
|
|
|
|
|
rv = ReadRegKeyValueWithDefault(sidKey, flags, L"Web", L"Filter On", 0,
|
|
|
|
|
webFilterOn);
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const char* kImportedFamilySafetyRootPref =
|
|
|
|
|
"security.family_safety.imported_root.db_key";
|
|
|
|
|
|
|
|
|
|
static nsresult
|
|
|
|
|
MaybeImportFamilySafetyRoot(PCCERT_CONTEXT certificate,
|
|
|
|
|
bool& wasFamilySafetyRoot)
|
|
|
|
|
{
|
|
|
|
|
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).
|
|
|
|
|
|
|
|
|
|
SECItem derCert = {
|
|
|
|
|
siBuffer,
|
|
|
|
|
certificate->pbCertEncoded,
|
|
|
|
|
certificate->cbCertEncoded
|
|
|
|
|
};
|
|
|
|
|
UniqueCERTCertificate nssCertificate(
|
|
|
|
|
CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert,
|
|
|
|
|
nullptr, // nickname unnecessary
|
|
|
|
|
false, // not permanent
|
|
|
|
|
true)); // copy DER
|
|
|
|
|
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'
|
|
|
|
|
UniquePtr<char, void(&)(void*)> subjectName(
|
|
|
|
|
CERT_GetCommonName(&nssCertificate->subject), PORT_Free);
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
|
|
|
("subject name is '%s'", subjectName.get()));
|
|
|
|
|
if (nsCRT::strcmp(subjectName.get(), "Microsoft Family Safety") == 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) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
|
|
|
("couldn't permanently add certificate"));
|
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
}
|
|
|
|
|
nsAutoCString dbKey;
|
|
|
|
|
nsresult rv = nsNSSCertificate::GetDbKey(nssCertificate.get(), dbKey);
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("GetDbKey failed"));
|
|
|
|
|
return rv;
|
|
|
|
|
}
|
|
|
|
|
Preferences::SetCString(kImportedFamilySafetyRootPref, dbKey);
|
|
|
|
|
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.
|
|
|
|
|
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;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const wchar_t* WindowsDefaultRootStoreName = L"ROOT";
|
|
|
|
|
|
|
|
|
|
static nsresult
|
|
|
|
|
LoadFamilySafetyRoot()
|
|
|
|
|
{
|
|
|
|
|
ScopedCertStore certstore(
|
|
|
|
|
CertOpenSystemStore(0, WindowsDefaultRootStoreName));
|
|
|
|
|
if (!certstore.get()) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
|
|
|
("couldn't get certstore '%S'", WindowsDefaultRootStoreName));
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
UnloadFamilySafetyRoot()
|
|
|
|
|
{
|
|
|
|
|
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"));
|
|
|
|
|
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) {
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
|
|
|
("previously-imported Family Safety root not found"));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#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
|
|
|
|
|
const char* kFamilySafetyModePref = "security.family_safety.mode";
|
|
|
|
|
|
|
|
|
|
// 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()
|
|
|
|
|
{
|
|
|
|
|
#ifdef XP_WIN
|
|
|
|
|
UnloadFamilySafetyRoot();
|
|
|
|
|
if (!(IsWin8Point1OrLater() && !IsWin10OrLater())) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Detect but don't import by default.
|
|
|
|
|
uint32_t familySafetyMode = Preferences::GetUint(kFamilySafetyModePref, 1);
|
|
|
|
|
if (familySafetyMode > 2) {
|
|
|
|
|
familySafetyMode = 0;
|
|
|
|
|
}
|
|
|
|
|
Telemetry::Accumulate(Telemetry::FAMILY_SAFETY, familySafetyMode);
|
|
|
|
|
if (familySafetyMode == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
bool familySafetyEnabled;
|
|
|
|
|
nsresult rv = AccountHasFamilySafetyEnabled(familySafetyEnabled);
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
|
Telemetry::Accumulate(Telemetry::FAMILY_SAFETY, 3);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (!familySafetyEnabled) {
|
|
|
|
|
Telemetry::Accumulate(Telemetry::FAMILY_SAFETY, 4);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
Telemetry::Accumulate(Telemetry::FAMILY_SAFETY, 5);
|
|
|
|
|
if (familySafetyMode == 2) {
|
|
|
|
|
rv = LoadFamilySafetyRoot();
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
|
Telemetry::Accumulate(Telemetry::FAMILY_SAFETY, 6);
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
|
|
|
|
("failed to load Family Safety root"));
|
|
|
|
|
} else {
|
|
|
|
|
Telemetry::Accumulate(Telemetry::FAMILY_SAFETY, 7);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif // XP_WIN
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
nsNSSComponent::LoadLoadableRoots()
|
|
|
|
|
{
|
|
|
|
@ -1079,6 +1519,8 @@ nsNSSComponent::InitializeNSS()
|
|
|
|
|
InitCertVerifierLog();
|
|
|
|
|
LoadLoadableRoots();
|
|
|
|
|
|
|
|
|
|
MaybeEnableFamilySafetyCompatibility();
|
|
|
|
|
|
|
|
|
|
ConfigureTLSSessionIdentifiers();
|
|
|
|
|
|
|
|
|
|
bool requireSafeNegotiation =
|
|
|
|
@ -1144,7 +1586,6 @@ nsNSSComponent::InitializeNSS()
|
|
|
|
|
if (PK11_IsFIPS()) {
|
|
|
|
|
Telemetry::Accumulate(Telemetry::FIPS_ENABLED, true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("NSS Initialization done\n"));
|
|
|
|
|
return NS_OK;
|
|
|
|
|
}
|
|
|
|
@ -1346,6 +1787,8 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic,
|
|
|
|
|
MutexAutoLock lock(mutex);
|
|
|
|
|
mTestBuiltInRootHash = Preferences::GetString("security.test.built_in_root_hash");
|
|
|
|
|
#endif // DEBUG
|
|
|
|
|
} else if (prefName.Equals(kFamilySafetyModePref)) {
|
|
|
|
|
MaybeEnableFamilySafetyCompatibility();
|
|
|
|
|
} else {
|
|
|
|
|
clearSessionCache = false;
|
|
|
|
|
}
|
|
|
|
|