1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* 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/. */
#include "Policy.h"
#include <windows.h>
#include <shlwapi.h>
#include <fstream>
#include "common.h"
#include "Registry.h"
#include "UtfConvert.h"
#include "json/json.h"
#include "mozilla/HelperMacros.h"
#include "mozilla/Maybe.h"
#include "mozilla/WinHeaderOnlyUtils.h"
// There is little logging or error handling in this file, because the file and
// registry values we are reading here are normally absent, so never finding
// anything that we look for at all would not be an error worth generating an
// event log for.
#define AGENT_POLICY_NAME "DisableDefaultBrowserAgent"
#define TELEMETRY_POLICY_NAME "DisableTelemetry"
// The Firefox policy engine hardcodes the string "Mozilla" in its registry
// key accesses rather than using the configured vendor name, so we should do
// the same here to be sure we're compatible with it.
#define POLICY_REGKEY_NAME L"SOFTWARE\\Policies\\Mozilla\\" MOZ_APP_BASENAME
namespace mozilla::default_agent {
// This enum is the return type for the functions that check policy values.
enum class PolicyState {
Enabled, // There is a policy explicitly set to enabled
Disabled, // There is a policy explicitly set to disabled
NoPolicy, // This policy isn't configured
};
static PolicyState FindPolicyInRegistry(HKEY rootKey,
const wchar_t* policyName) {
HKEY rawRegKey = nullptr;
RegOpenKeyExW(rootKey, POLICY_REGKEY_NAME, 0, KEY_READ, &rawRegKey);
nsAutoRegKey regKey(rawRegKey);
if (!regKey) {
return PolicyState::NoPolicy;
}
// If this key is empty and doesn't have any actual policies in it,
// treat that the same as the key not existing and return no result.
DWORD numSubKeys = 0, numValues = 0;
LSTATUS ls = RegQueryInfoKeyW(regKey.get(), nullptr, nullptr, nullptr,
&numSubKeys, nullptr, nullptr, &numValues,
nullptr, nullptr, nullptr, nullptr);
if (ls != ERROR_SUCCESS) {
return PolicyState::NoPolicy;
}
DWORD policyValue = UINT32_MAX;
DWORD policyValueSize = sizeof(policyValue);
ls = RegGetValueW(regKey.get(), nullptr, policyName, RRF_RT_REG_DWORD,
nullptr, &policyValue, &policyValueSize);
if (ls != ERROR_SUCCESS) {
return PolicyState::NoPolicy;
}
return policyValue == 0 ? PolicyState::Disabled : PolicyState::Enabled;
}
static PolicyState FindPolicyInFile(const char* policyName) {
mozilla::UniquePtr<wchar_t[]> thisBinaryPath = mozilla::GetFullBinaryPath();
if (!PathRemoveFileSpecW(thisBinaryPath.get())) {
return PolicyState::NoPolicy;
}
wchar_t policiesFilePath[MAX_PATH] = L"";
if (!PathCombineW(policiesFilePath, thisBinaryPath.get(), L"distribution")) {
return PolicyState::NoPolicy;
}
if (!PathAppendW(policiesFilePath, L"policies.json")) {
return PolicyState::NoPolicy;
}
// We need a narrow string-based std::ifstream because that's all jsoncpp can
// use; that means we need to supply it the file path as a narrow string.
Utf16ToUtf8Result policiesFilePathToUtf8 = Utf16ToUtf8(policiesFilePath);
if (policiesFilePathToUtf8.isErr()) {
return PolicyState::NoPolicy;
}
std::string policiesFilePathA = policiesFilePathToUtf8.unwrap();
Json::Value jsonRoot;
std::ifstream stream(policiesFilePathA);
Json::Reader().parse(stream, jsonRoot);
if (jsonRoot.isObject() && jsonRoot.isMember("Policies") &&
jsonRoot["Policies"].isObject()) {
if (jsonRoot["Policies"].isMember(policyName) &&
jsonRoot["Policies"][policyName].isBool()) {
return jsonRoot["Policies"][policyName].asBool() ? PolicyState::Enabled
: PolicyState::Disabled;
} else {
return PolicyState::NoPolicy;
}
}
return PolicyState::NoPolicy;
}
static PolicyState IsDisabledByPref(const wchar_t* prefRegValue) {
auto prefValueResult =
RegistryGetValueBool(IsPrefixed::Prefixed, prefRegValue);
if (prefValueResult.isErr()) {
return PolicyState::NoPolicy;
}
auto prefValue = prefValueResult.unwrap();
if (prefValue.isNothing()) {
return PolicyState::NoPolicy;
}
return prefValue.value() ? PolicyState::Enabled : PolicyState::Disabled;
}
// Everything we call from this function wants wide strings, except for jsoncpp,
// which cannot work with them at all, so at some point we need both formats.
// It's awkward to take both formats as individual arguments, but it would be
// more awkward to take one and runtime convert it to the other, or to turn
// this function into a macro so that the preprocessor can trigger the
// conversion for us, so this is what we've got.
static bool IsThingDisabled(const char* thing, const wchar_t* wideThing) {
// The logic here is intended to be the same as that used by Firefox's policy
// engine implementation; they should be kept in sync. We have added the pref
// check at the end though, since that's our own custom mechanism.
PolicyState state = FindPolicyInRegistry(HKEY_LOCAL_MACHINE, wideThing);
if (state == PolicyState::NoPolicy) {
state = FindPolicyInRegistry(HKEY_CURRENT_USER, wideThing);
}
if (state == PolicyState::NoPolicy) {
state = FindPolicyInFile(thing);
}
if (state == PolicyState::NoPolicy) {
state = IsDisabledByPref(wideThing);
}
return state == PolicyState::Enabled ? true : false;
}
bool IsAgentDisabled() {
return IsThingDisabled(AGENT_POLICY_NAME, L"" AGENT_POLICY_NAME);
}
bool IsTelemetryDisabled() {
return IsThingDisabled(TELEMETRY_POLICY_NAME, L"" TELEMETRY_POLICY_NAME);
}
} // namespace mozilla::default_agent
|