blob: 33edb396836720d31a8d4a908474d58a4e3a4fc5 [file] [log] [blame]
[email protected]01253d272013-10-21 17:07:501// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/extensions/policy_handlers.h"
6
avia2f4804a2015-12-24 23:11:137#include <stddef.h>
dcheng1fc00f12015-12-26 22:18:038#include <utility>
avia2f4804a2015-12-24 23:11:139
[email protected]01253d272013-10-21 17:07:5010#include "base/logging.h"
Yann Dagoee291902019-08-19 15:49:0611#include "base/strings/string_number_conversions.h"
Nick Petersond952cb772018-03-07 15:46:0312#include "build/build_config.h"
binjin1e1cc33a2014-10-09 18:08:1613#include "chrome/browser/extensions/extension_management_constants.h"
[email protected]01253d272013-10-21 17:07:5014#include "chrome/browser/extensions/external_policy_loader.h"
[email protected]fdd28372014-08-21 02:27:2615#include "components/crx_file/id_util.h"
[email protected]f6c403b2013-12-05 19:01:2516#include "components/policy/core/browser/policy_error_map.h"
[email protected]c4a138a2013-11-21 19:54:5717#include "components/policy/core/common/policy_map.h"
binjin1e1cc33a2014-10-09 18:08:1618#include "components/policy/core/common/schema.h"
brettw39d6ba42016-08-24 16:56:3819#include "components/policy/policy_constants.h"
brettwb1fc1b82016-02-02 00:19:0820#include "components/prefs/pref_value_map.h"
thestig4a2e88e2016-08-27 23:23:5121#include "components/strings/grit/components_strings.h"
[email protected]234fc5ff2014-01-16 23:32:2822#include "extensions/browser/pref_names.h"
[email protected]e4452d32013-11-15 23:07:4123#include "extensions/common/extension.h"
Nick Petersond952cb772018-03-07 15:46:0324#include "extensions/common/extension_urls.h"
binjin1e1cc33a2014-10-09 18:08:1625#include "url/gurl.h"
[email protected]01253d272013-10-21 17:07:5026
Nick Petersond952cb772018-03-07 15:46:0327#if defined(OS_WIN)
Hector Carmonac3565bc42019-02-01 23:31:2128#include "base/enterprise_util.h"
Nick Petersond952cb772018-03-07 15:46:0329#endif
30
[email protected]01253d272013-10-21 17:07:5031namespace extensions {
32
33// ExtensionListPolicyHandler implementation -----------------------------------
34
35ExtensionListPolicyHandler::ExtensionListPolicyHandler(const char* policy_name,
36 const char* pref_path,
37 bool allow_wildcards)
Lutz Justene45e3fe2017-08-18 07:11:3938 : policy::ListPolicyHandler(policy_name, base::Value::Type::STRING),
[email protected]01253d272013-10-21 17:07:5039 pref_path_(pref_path),
40 allow_wildcards_(allow_wildcards) {}
41
42ExtensionListPolicyHandler::~ExtensionListPolicyHandler() {}
43
Lutz Justene45e3fe2017-08-18 07:11:3944bool ExtensionListPolicyHandler::CheckListEntry(const base::Value& value) {
45 const std::string& str = value.GetString();
46 if (allow_wildcards_ && str == "*")
[email protected]01253d272013-10-21 17:07:5047 return true;
48
Lutz Justene45e3fe2017-08-18 07:11:3949 // Make sure str is an extension id.
50 return crx_file::id_util::IdIsValid(str);
51}
[email protected]01253d272013-10-21 17:07:5052
Lutz Justene45e3fe2017-08-18 07:11:3953void ExtensionListPolicyHandler::ApplyList(
54 std::unique_ptr<base::ListValue> filtered_list,
55 PrefValueMap* prefs) {
Sylvain Defresne1787960d2019-01-30 21:02:1056 DCHECK(filtered_list);
57 prefs->SetValue(pref_path_,
58 base::Value::FromUniquePtrValue(std::move(filtered_list)));
[email protected]01253d272013-10-21 17:07:5059}
60
achuith4607f072017-03-08 11:49:1361// ExtensionInstallListPolicyHandler implementation ----------------------------
[email protected]01253d272013-10-21 17:07:5062
achuith4607f072017-03-08 11:49:1363ExtensionInstallListPolicyHandler::ExtensionInstallListPolicyHandler(
64 const char* policy_name,
65 const char* pref_name)
66 : policy::TypeCheckingPolicyHandler(policy_name, base::Value::Type::LIST),
67 pref_name_(pref_name) {}
[email protected]01253d272013-10-21 17:07:5068
achuith4607f072017-03-08 11:49:1369bool ExtensionInstallListPolicyHandler::CheckPolicySettings(
[email protected]01253d272013-10-21 17:07:5070 const policy::PolicyMap& policies,
71 policy::PolicyErrorMap* errors) {
72 const base::Value* value;
73 return CheckAndGetValue(policies, errors, &value) &&
achuith4607f072017-03-08 11:49:1374 ParseList(value, nullptr, errors);
[email protected]01253d272013-10-21 17:07:5075}
76
achuith4607f072017-03-08 11:49:1377void ExtensionInstallListPolicyHandler::ApplyPolicySettings(
[email protected]01253d272013-10-21 17:07:5078 const policy::PolicyMap& policies,
79 PrefValueMap* prefs) {
achuith4607f072017-03-08 11:49:1380 const base::Value* value = nullptr;
Sylvain Defresne1787960d2019-01-30 21:02:1081 base::DictionaryValue dict;
achuith4607f072017-03-08 11:49:1382 if (CheckAndGetValue(policies, nullptr, &value) && value &&
Sylvain Defresne1787960d2019-01-30 21:02:1083 ParseList(value, &dict, nullptr)) {
achuith4607f072017-03-08 11:49:1384 prefs->SetValue(pref_name_, std::move(dict));
[email protected]01253d272013-10-21 17:07:5085 }
86}
87
achuith4607f072017-03-08 11:49:1388bool ExtensionInstallListPolicyHandler::ParseList(
[email protected]01253d272013-10-21 17:07:5089 const base::Value* policy_value,
90 base::DictionaryValue* extension_dict,
91 policy::PolicyErrorMap* errors) {
92 if (!policy_value)
93 return true;
94
achuith4607f072017-03-08 11:49:1395 const base::ListValue* policy_list_value = nullptr;
[email protected]01253d272013-10-21 17:07:5096 if (!policy_value->GetAsList(&policy_list_value)) {
97 // This should have been caught in CheckPolicySettings.
98 NOTREACHED();
99 return false;
100 }
101
jdoerrie13cd648c82018-10-02 21:21:02102 for (auto entry(policy_list_value->begin());
[email protected]01253d272013-10-21 17:07:50103 entry != policy_list_value->end(); ++entry) {
104 std::string entry_string;
jdoerriea5676c62017-04-11 18:09:14105 if (!entry->GetAsString(&entry_string)) {
[email protected]01253d272013-10-21 17:07:50106 if (errors) {
thestige7615d6c2016-07-19 19:43:46107 errors->AddError(policy_name(), entry - policy_list_value->begin(),
[email protected]01253d272013-10-21 17:07:50108 IDS_POLICY_TYPE_ERROR,
jdoerriedc72ee942016-12-07 15:43:28109 base::Value::GetTypeName(base::Value::Type::STRING));
[email protected]01253d272013-10-21 17:07:50110 }
111 continue;
112 }
113
Maksim Ivanoveaac2ff2018-04-16 16:23:24114 // Each string item of the list should be of one of the following forms:
115 // * <extension_id>
116 // * <extension_id>;<update_url>
[email protected]01253d272013-10-21 17:07:50117 // Note: The update URL might also contain semicolons.
Maksim Ivanoveaac2ff2018-04-16 16:23:24118 std::string extension_id;
119 std::string update_url;
[email protected]01253d272013-10-21 17:07:50120 size_t pos = entry_string.find(';');
121 if (pos == std::string::npos) {
Maksim Ivanoveaac2ff2018-04-16 16:23:24122 extension_id = entry_string;
123 update_url = extension_urls::kChromeWebstoreUpdateURL;
124 } else {
125 extension_id = entry_string.substr(0, pos);
126 update_url = entry_string.substr(pos + 1);
[email protected]01253d272013-10-21 17:07:50127 }
128
[email protected]fdd28372014-08-21 02:27:26129 if (!crx_file::id_util::IdIsValid(extension_id) ||
[email protected]01253d272013-10-21 17:07:50130 !GURL(update_url).is_valid()) {
131 if (errors) {
132 errors->AddError(policy_name(),
133 entry - policy_list_value->begin(),
134 IDS_POLICY_VALUE_FORMAT_ERROR);
135 }
136 continue;
137 }
138
139 if (extension_dict) {
achuith4607f072017-03-08 11:49:13140 ExternalPolicyLoader::AddExtension(extension_dict, extension_id,
141 update_url);
[email protected]01253d272013-10-21 17:07:50142 }
143 }
144
145 return true;
146}
147
achuith4607f072017-03-08 11:49:13148// ExtensionInstallForcelistPolicyHandler implementation -----------------------
149
150ExtensionInstallForcelistPolicyHandler::ExtensionInstallForcelistPolicyHandler()
151 : ExtensionInstallListPolicyHandler(policy::key::kExtensionInstallForcelist,
152 pref_names::kInstallForceList) {}
153
Alexander Hendrichb07fd55b2019-04-01 09:24:37154// ExtensionInstallLoginScreenExtensionsPolicyHandler implementation -----------
achuith4607f072017-03-08 11:49:13155
Alexander Hendrichb07fd55b2019-04-01 09:24:37156ExtensionInstallLoginScreenExtensionsPolicyHandler::
157 ExtensionInstallLoginScreenExtensionsPolicyHandler()
achuith4607f072017-03-08 11:49:13158 : ExtensionInstallListPolicyHandler(
Alexander Hendrichb07fd55b2019-04-01 09:24:37159 policy::key::kDeviceLoginScreenExtensions,
160 pref_names::kLoginScreenExtensions) {}
achuith4607f072017-03-08 11:49:13161
[email protected]01253d272013-10-21 17:07:50162// ExtensionURLPatternListPolicyHandler implementation -------------------------
163
164ExtensionURLPatternListPolicyHandler::ExtensionURLPatternListPolicyHandler(
165 const char* policy_name,
166 const char* pref_path)
jdoerriedc72ee942016-12-07 15:43:28167 : policy::TypeCheckingPolicyHandler(policy_name, base::Value::Type::LIST),
[email protected]01253d272013-10-21 17:07:50168 pref_path_(pref_path) {}
169
170ExtensionURLPatternListPolicyHandler::~ExtensionURLPatternListPolicyHandler() {}
171
172bool ExtensionURLPatternListPolicyHandler::CheckPolicySettings(
173 const policy::PolicyMap& policies,
174 policy::PolicyErrorMap* errors) {
175 const base::Value* value = NULL;
176 if (!CheckAndGetValue(policies, errors, &value))
177 return false;
178
179 if (!value)
180 return true;
181
182 const base::ListValue* list_value = NULL;
183 if (!value->GetAsList(&list_value)) {
184 NOTREACHED();
185 return false;
186 }
187
188 // Check that the list contains valid URLPattern strings only.
jdoerrie13cd648c82018-10-02 21:21:02189 for (auto entry(list_value->begin()); entry != list_value->end(); ++entry) {
[email protected]01253d272013-10-21 17:07:50190 std::string url_pattern_string;
jdoerriea5676c62017-04-11 18:09:14191 if (!entry->GetAsString(&url_pattern_string)) {
thestige7615d6c2016-07-19 19:43:46192 errors->AddError(policy_name(), entry - list_value->begin(),
[email protected]01253d272013-10-21 17:07:50193 IDS_POLICY_TYPE_ERROR,
jdoerriedc72ee942016-12-07 15:43:28194 base::Value::GetTypeName(base::Value::Type::STRING));
[email protected]01253d272013-10-21 17:07:50195 return false;
196 }
197
198 URLPattern pattern(URLPattern::SCHEME_ALL);
Devlin Croninbd7f2b5fa2018-09-05 17:41:18199 if (pattern.Parse(url_pattern_string) !=
200 URLPattern::ParseResult::kSuccess) {
[email protected]01253d272013-10-21 17:07:50201 errors->AddError(policy_name(),
202 entry - list_value->begin(),
203 IDS_POLICY_VALUE_FORMAT_ERROR);
204 return false;
205 }
206 }
207
208 return true;
209}
210
211void ExtensionURLPatternListPolicyHandler::ApplyPolicySettings(
212 const policy::PolicyMap& policies,
213 PrefValueMap* prefs) {
214 if (!pref_path_)
215 return;
[email protected]cb1078de2013-12-23 20:04:22216 const base::Value* value = policies.GetValue(policy_name());
[email protected]01253d272013-10-21 17:07:50217 if (value)
Sylvain Defresne1787960d2019-01-30 21:02:10218 prefs->SetValue(pref_path_, value->Clone());
[email protected]01253d272013-10-21 17:07:50219}
220
binjin1e1cc33a2014-10-09 18:08:16221// ExtensionSettingsPolicyHandler implementation ------------------------------
222
223ExtensionSettingsPolicyHandler::ExtensionSettingsPolicyHandler(
224 const policy::Schema& chrome_schema)
225 : policy::SchemaValidatingPolicyHandler(
226 policy::key::kExtensionSettings,
227 chrome_schema.GetKnownProperty(policy::key::kExtensionSettings),
228 policy::SCHEMA_ALLOW_UNKNOWN) {
229}
230
231ExtensionSettingsPolicyHandler::~ExtensionSettingsPolicyHandler() {
232}
233
234bool ExtensionSettingsPolicyHandler::CheckPolicySettings(
235 const policy::PolicyMap& policies,
236 policy::PolicyErrorMap* errors) {
dchengc963c7142016-04-08 03:55:22237 std::unique_ptr<base::Value> policy_value;
binjin1e1cc33a2014-10-09 18:08:16238 if (!CheckAndGetValue(policies, errors, &policy_value))
239 return false;
240 if (!policy_value)
241 return true;
242
243 // |policy_value| is expected to conform to the defined schema. But it's
244 // not strictly valid since there are additional restrictions.
245 const base::DictionaryValue* dict_value = NULL;
jdoerrie1f536b22017-10-23 17:15:11246 DCHECK(policy_value->is_dict());
binjin1e1cc33a2014-10-09 18:08:16247 policy_value->GetAsDictionary(&dict_value);
248
249 for (base::DictionaryValue::Iterator it(*dict_value); !it.IsAtEnd();
250 it.Advance()) {
251 DCHECK(it.key() == schema_constants::kWildcard ||
252 crx_file::id_util::IdIsValid(it.key()));
jdoerrie1f536b22017-10-23 17:15:11253 DCHECK(it.value().is_dict());
binjin1e1cc33a2014-10-09 18:08:16254
255 // Extracts sub dictionary.
256 const base::DictionaryValue* sub_dict = NULL;
257 it.value().GetAsDictionary(&sub_dict);
258
259 std::string installation_mode;
260 if (sub_dict->GetString(schema_constants::kInstallationMode,
261 &installation_mode)) {
262 if (installation_mode == schema_constants::kForceInstalled ||
263 installation_mode == schema_constants::kNormalInstalled) {
264 DCHECK(it.key() != schema_constants::kWildcard);
265 // Verifies that 'update_url' is specified for 'force_installed' and
266 // 'normal_installed' mode.
267 std::string update_url;
268 if (!sub_dict->GetString(schema_constants::kUpdateUrl, &update_url) ||
269 update_url.empty()) {
270 errors->AddError(policy_name(),
271 it.key() + "." + schema_constants::kUpdateUrl,
272 IDS_POLICY_NOT_SPECIFIED_ERROR);
273 return false;
274 }
Nick Petersond952cb772018-03-07 15:46:03275 if (GURL(update_url).is_valid()) {
276// Unless enterprise managed only extensions from the Chrome Webstore
277// can be force installed.
278#if defined(OS_WIN)
279 // We can't use IsWebstoreUpdateUrl() here since the ExtensionClient
280 // isn't set this early during startup.
Hector Carmonac3565bc42019-02-01 23:31:21281 if (!base::IsMachineExternallyManaged() &&
Nick Petersond952cb772018-03-07 15:46:03282 !base::LowerCaseEqualsASCII(
283 update_url, extension_urls::kChromeWebstoreUpdateURL)) {
284 errors->AddError(policy_name(), it.key(),
285 IDS_POLICY_OFF_CWS_URL_ERROR,
286 extension_urls::kChromeWebstoreUpdateURL);
287 return false;
288 }
289#endif
290 } else {
291 // Warns about an invalid update URL.
binjin1e1cc33a2014-10-09 18:08:16292 errors->AddError(
293 policy_name(), IDS_POLICY_INVALID_UPDATE_URL_ERROR, it.key());
294 return false;
295 }
296 }
297 }
Nick Peterson6bdf5822017-06-01 20:42:45298 // Host keys that don't support user defined paths.
Devlin Cronin7e0f41ff2018-05-16 17:19:36299 const char* host_keys[] = {schema_constants::kPolicyBlockedHosts,
300 schema_constants::kPolicyAllowedHosts};
Nick Peterson6bdf5822017-06-01 20:42:45301 const int extension_scheme_mask =
302 URLPattern::GetValidSchemeMaskForExtensions();
303 for (const char* key : host_keys) {
304 const base::ListValue* unparsed_urls;
305 if (sub_dict->GetList(key, &unparsed_urls)) {
306 for (size_t i = 0; i < unparsed_urls->GetSize(); ++i) {
307 std::string unparsed_url;
308 unparsed_urls->GetString(i, &unparsed_url);
309 URLPattern pattern(extension_scheme_mask);
Devlin Cronin35f8e372019-08-16 19:15:38310 URLPattern::ParseResult parse_result = pattern.Parse(unparsed_url);
Nick Peterson6bdf5822017-06-01 20:42:45311 // These keys don't support paths due to how we track the initiator
312 // of a webRequest and cookie security policy. We expect a valid
313 // pattern to return a PARSE_ERROR_EMPTY_PATH.
Devlin Croninbd7f2b5fa2018-09-05 17:41:18314 if (parse_result == URLPattern::ParseResult::kEmptyPath) {
Nick Peterson6bdf5822017-06-01 20:42:45315 // Add a wildcard path to the URL as it should match any path.
Devlin Cronin35f8e372019-08-16 19:15:38316 parse_result = pattern.Parse(unparsed_url + "/*");
Devlin Croninbd7f2b5fa2018-09-05 17:41:18317 } else if (parse_result == URLPattern::ParseResult::kSuccess) {
Nick Peterson6bdf5822017-06-01 20:42:45318 // The user supplied a path, notify them that this is not supported.
319 if (!pattern.match_all_urls()) {
320 errors->AddError(
321 policy_name(), it.key(),
322 "The URL pattern '" + unparsed_url + "' for attribute " +
323 key + " has a path specified. Paths are not " +
324 "supported, please remove the path and try again. " +
325 "e.g. *://example.com/ => *://example.com");
326 return false;
327 }
328 }
Devlin Croninbd7f2b5fa2018-09-05 17:41:18329 if (parse_result != URLPattern::ParseResult::kSuccess) {
Nick Peterson6bdf5822017-06-01 20:42:45330 errors->AddError(policy_name(), it.key(),
331 "Invalid URL pattern '" + unparsed_url +
332 "' for attribute " + key);
333 return false;
334 }
335 }
336 }
337 }
Yann Dagoee291902019-08-19 15:49:06338
339 const base::ListValue* runtime_blocked_hosts = nullptr;
340 if (sub_dict->GetList(schema_constants::kPolicyBlockedHosts,
341 &runtime_blocked_hosts) &&
342 runtime_blocked_hosts->GetList().size() >
343 schema_constants::kMaxItemsURLPatternSet) {
344 errors->AddError(
345 policy_name(), it.key() + "." + schema_constants::kPolicyBlockedHosts,
346 IDS_POLICY_EXTENSION_SETTINGS_ORIGIN_LIMIT_WARNING,
347 base::NumberToString(schema_constants::kMaxItemsURLPatternSet));
348 }
349
350 const base::ListValue* runtime_allowed_hosts = nullptr;
351 if (sub_dict->GetList(schema_constants::kPolicyAllowedHosts,
352 &runtime_allowed_hosts) &&
353 runtime_allowed_hosts->GetList().size() >
354 schema_constants::kMaxItemsURLPatternSet) {
355 errors->AddError(
356 policy_name(), it.key() + "." + schema_constants::kPolicyAllowedHosts,
357 IDS_POLICY_EXTENSION_SETTINGS_ORIGIN_LIMIT_WARNING,
358 base::NumberToString(schema_constants::kMaxItemsURLPatternSet));
359 }
binjin1e1cc33a2014-10-09 18:08:16360 }
361
362 return true;
363}
364
365void ExtensionSettingsPolicyHandler::ApplyPolicySettings(
366 const policy::PolicyMap& policies,
367 PrefValueMap* prefs) {
dchengc963c7142016-04-08 03:55:22368 std::unique_ptr<base::Value> policy_value;
binjin1e1cc33a2014-10-09 18:08:16369 if (!CheckAndGetValue(policies, NULL, &policy_value) || !policy_value)
370 return;
Sylvain Defresne1787960d2019-01-30 21:02:10371 prefs->SetValue(pref_names::kExtensionManagement,
372 base::Value::FromUniquePtrValue(std::move(policy_value)));
binjin1e1cc33a2014-10-09 18:08:16373}
374
[email protected]01253d272013-10-21 17:07:50375} // namespace extensions