summaryrefslogtreecommitdiffstats
path: root/toolkit/mozapps/defaultagent/Policy.cpp
blob: 56d868866b53ec2917566cdd8625b3f26607a90d (plain)
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
/* -*- 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

// 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);
}