summaryrefslogtreecommitdiffstats
path: root/chromium/components/policy/core/common/policy_loader_common.cc
blob: 5d64293bfd6debd51f1395dc3da0b371406931e4 (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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/policy/core/common/policy_loader_common.h"

#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_util.h"
#include "components/policy/core/common/policy_map.h"
#include "components/policy/policy_constants.h"
#include "components/strings/grit/components_strings.h"

namespace policy {

namespace {

// Duplicate the extension constants in order to avoid extension dependency.
// However, those values below must be synced with files in extension folders.
// In long term, we can refactor the code and create an interface for sensitive
// policy filtering so that each policy component users can have their own
// implementation. And the Chrome one can be moved to c/b/policy.
// From extensions/common/extension_urls.cc
const char kChromeWebstoreUpdateURL[] =
    "https://clients2.google.com/service/update2/crx";
const char16_t kChromeWebstoreUpdateURL16[] =
    u"https://clients2.google.com/service/update2/crx";

// From chrome/browser/extensions/extension_management_constants.cc
const char kWildcard[] = "*";
const char kInstallationMode[] = "installation_mode";
const char kForceInstalled[] = "force_installed";
const char kNormalInstalled[] = "normal_installed";
const char kUpdateUrl[] = "update_url";

// String to be prepended to each blocked entry.
const char kBlockedExtensionPrefix[] = "[BLOCKED]";

// List of policies that are considered only if the user is part of a AD domain
// on Windows or managed on the Mac. Please document any new additions in
// policy_templates.json!
// Please keep the list in alphabetical order!
const char* kSensitivePolicies[] = {
    key::kAutoOpenFileTypes,
    key::kChromeCleanupEnabled,
    key::kChromeCleanupReportingEnabled,
    key::kCommandLineFlagSecurityWarningsEnabled,
    key::kDefaultSearchProviderEnabled,
    key::kHomepageIsNewTabPage,
    key::kHomepageLocation,
    key::kMetricsReportingEnabled,
    key::kNewTabPageLocation,
    key::kPasswordProtectionChangePasswordURL,
    key::kPasswordProtectionLoginURLs,
    key::kRestoreOnStartup,
    key::kRestoreOnStartupURLs,
    key::kSafeBrowsingForTrustedSourcesEnabled,
    key::kSafeBrowsingEnabled,
    key::kSafeBrowsingAllowlistDomains,
};

void RecordInvalidPolicies(const std::string& policy_name) {
  const PolicyDetails* details = GetChromePolicyDetails(policy_name);
  base::UmaHistogramSparse("EnterpriseCheck.InvalidPolicies", details->id);
}

// Marks the sensitive ExtensionInstallForceList policy entries, returns true if
// there is any sensitive entries in the policy.
bool FilterSensitiveExtensionsInstallForcelist(PolicyMap::Entry* map_entry) {
  bool has_invalid_policies = false;
  if (!map_entry)
    return false;

  base::Value* policy_list_value = map_entry->value(base::Value::Type::LIST);
  if (!policy_list_value)
    return false;

  // Using index for loop to update the list in place.
  for (size_t i = 0; i < policy_list_value->GetListDeprecated().size(); i++) {
    const auto& list_entry = policy_list_value->GetListDeprecated()[i];
    if (!list_entry.is_string())
      continue;

    const std::string& entry = list_entry.GetString();
    size_t pos = entry.find(';');
    if (pos == std::string::npos)
      continue;

    // Only allow custom update urls in enterprise environments.
    if (!base::LowerCaseEqualsASCII(entry.substr(pos + 1),
                                    kChromeWebstoreUpdateURL)) {
      policy_list_value->GetListDeprecated()[i] =
          base::Value(kBlockedExtensionPrefix + entry);
      has_invalid_policies = true;
    }
  }

  if (has_invalid_policies) {
    map_entry->AddMessage(PolicyMap::MessageType::kWarning,
                          IDS_POLICY_OFF_CWS_URL_ERROR,
                          {kChromeWebstoreUpdateURL16});
    RecordInvalidPolicies(key::kExtensionInstallForcelist);
  }

  return has_invalid_policies;
}

// Marks the sensitive ExtensionSettings policy entries, returns the number of
// sensitive entries in the policy.
bool FilterSensitiveExtensionSettings(PolicyMap::Entry* map_entry) {
  if (!map_entry)
    return false;
  base::Value* policy_dict_value = map_entry->value(base::Value::Type::DICT);
  if (!policy_dict_value) {
    return false;
  }

  // Note that we only search for sensitive entries, all other validations will
  // be handled by ExtensionSettingsPolicyHandler.
  std::vector<std::string> filtered_extensions;
  for (auto entry : policy_dict_value->DictItems()) {
    if (entry.first == kWildcard)
      continue;
    if (!entry.second.is_dict())
      continue;
    std::string* installation_mode =
        entry.second.FindStringKey(kInstallationMode);
    if (!installation_mode || (*installation_mode != kForceInstalled &&
                               *installation_mode != kNormalInstalled)) {
      continue;
    }
    std::string* update_url = entry.second.FindStringKey(kUpdateUrl);
    if (!update_url ||
        base::LowerCaseEqualsASCII(*update_url, kChromeWebstoreUpdateURL)) {
      continue;
    }

    filtered_extensions.push_back(entry.first);
  }

  // Marking the blocked extension by adding the "[BLOCKED]" prefix. This is an
  // invalid extension id and will be removed by PolicyHandler later.
  if (!filtered_extensions.empty()) {
    for (const auto& extension : filtered_extensions) {
      auto setting = policy_dict_value->ExtractKey(extension);
      if (!setting)
        continue;
      policy_dict_value->SetKey(kBlockedExtensionPrefix + extension,
                                std::move(setting.value()));
    }
    map_entry->AddMessage(PolicyMap::MessageType::kWarning,
                          IDS_POLICY_OFF_CWS_URL_ERROR,
                          {kChromeWebstoreUpdateURL16});
    RecordInvalidPolicies(key::kExtensionSettings);
  }

  return !filtered_extensions.empty();
}

}  // namespace

void FilterSensitivePolicies(PolicyMap* policy) {
  int invalid_policies = 0;
  if (FilterSensitiveExtensionsInstallForcelist(
          policy->GetMutable(key::kExtensionInstallForcelist))) {
    invalid_policies++;
  }
  if (FilterSensitiveExtensionSettings(
          policy->GetMutable(key::kExtensionSettings))) {
    invalid_policies++;
  }
  for (const char* sensitive_policy : kSensitivePolicies) {
    if (policy->Get(sensitive_policy)) {
      policy->GetMutable(sensitive_policy)->SetBlocked();
      invalid_policies++;
      RecordInvalidPolicies(sensitive_policy);
    }
  }

  UMA_HISTOGRAM_COUNTS_1M("EnterpriseCheck.InvalidPoliciesDetected",
                          invalid_policies);
}  // namespace policy

}  // namespace policy