/* 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 "EnterprisePolicies.h"
#include <filesystem>
#include <format>
#include <fstream>
#include <string_view>
#include <string>
#include <windows.h>
#include <json/json.h>
namespace fs = std::filesystem;
namespace {
static fs::path GetDistributionPoliciesFilePath(
const fs::path& aDir) noexcept {
const fs::path kRelativeFilePath{
"distribution/policies.json"};
return aDir / kRelativeFilePath;
}
static bool EnterprisePoliciesInRegistry(HKEY aHive, std::wstring_view aBrand) {
std::wstring keyPath{std::format(LR
"(SOFTWARE\Policies\Mozilla\{})", aBrand)};
HKEY key{};
if (RegOpenKeyExW(aHive, keyPath.c_str(),
0, KEY_READ, &key) !=
ERROR_SUCCESS) {
return false;
}
DWORD numSubkeys{
0};
DWORD numValues{
0};
LONG rv{RegQueryInfoKeyW(key, nullptr, nullptr, nullptr, &numSubkeys, nullptr,
nullptr, &numValues, nullptr, nullptr, nullptr,
nullptr)};
if (rv != ERROR_SUCCESS) {
RegCloseKey(key);
return false;
}
// If there is any value, it's enterprise managed
if (numValues >
0) {
RegCloseKey(key);
return true;
}
// If there are no subkeys, it's not enterprise managed
if (numSubkeys ==
0) {
RegCloseKey(key);
return false;
}
// If there's more than one subkey, it's enterprise managed
if (numSubkeys >
1) {
RegCloseKey(key);
return true;
}
// If the only subkey isn't Certificates, it's enterprise managed
wchar_t subkeyName[MAX_PATH +
1]{};
DWORD subkeyNameLen{MAX_PATH +
1};
rv = RegEnumKeyExW(key,
0, subkeyName, &subkeyNameLen, nullptr, nullptr,
nullptr, nullptr);
RegCloseKey(key);
if (rv != ERROR_SUCCESS || _wcsicmp(subkeyName, L
"Certificates") !=
0) {
return true;
}
std::wstring certsPath{std::format(L
"{}\\Certificates", keyPath)};
HKEY certsKey{};
if (RegOpenKeyExW(aHive, certsPath.c_str(),
0, KEY_READ, &certsKey) !=
ERROR_SUCCESS) {
return true;
}
DWORD certsNumSubkeys{
0};
DWORD certsNumValues{
0};
rv = RegQueryInfoKeyW(certsKey, nullptr, nullptr, nullptr, &certsNumSubkeys,
nullptr, nullptr, &certsNumValues, nullptr, nullptr,
nullptr, nullptr);
if (rv != ERROR_SUCCESS) {
RegCloseKey(certsKey);
return true;
}
// If Certificates has additional values, it's enterprise managed
if (certsNumValues >
1) {
RegCloseKey(certsKey);
return true;
}
// If Certificates has subkeys, it's enterprise managed
if (certsNumSubkeys >
0) {
RegCloseKey(certsKey);
return true;
}
// Read ImportEnterpriseRoots, try as DWORD first, then as string
bool importEnterpriseRootsEnabled{
false};
DWORD type{};
DWORD dwordValue{};
DWORD dataSize{
sizeof(dwordValue)};
rv = RegQueryValueExW(certsKey, L
"ImportEnterpriseRoots", nullptr, &type,
reinterpret_cast<BYTE*>(&dwordValue), &dataSize);
if (rv == ERROR_SUCCESS && type == REG_DWORD) {
importEnterpriseRootsEnabled = (dwordValue ==
1);
}
else {
wchar_t strValue[
64]{};
dataSize =
sizeof(strValue);
rv = RegQueryValueExW(certsKey, L
"ImportEnterpriseRoots", nullptr, &type,
reinterpret_cast<BYTE*>(strValue), &dataSize);
if (rv == ERROR_SUCCESS && (type == REG_SZ || type == REG_EXPAND_SZ)) {
importEnterpriseRootsEnabled =
_wcsicmp(strValue, L
"1") ==
0 || _wcsicmp(strValue, L
"true") ==
0;
}
}
RegCloseKey(certsKey);
return !importEnterpriseRootsEnabled;
}
}
// namespace
namespace EnterprisePolicies {
bool InDistribution(
const std::filesystem::path& aDir) {
Json::CharReaderBuilder builder;
std::ifstream file(GetDistributionPoliciesFilePath(aDir));
Json::Value root;
std::string errors;
if (!Json::parseFromStream(builder, file, &root, &errors)) {
return false;
}
if (!root.isObject() || !root.isMember(
"policies")) {
return false;
}
const Json::Value& policies = root[
"policies"];
return !(!policies.isObject() || policies.empty());
}
bool InRegistry(std::wstring_view aBrand) {
return EnterprisePoliciesInRegistry(HKEY_LOCAL_MACHINE, aBrand) ||
EnterprisePoliciesInRegistry(HKEY_CURRENT_USER, aBrand);
}
}
// namespace EnterprisePolicies