blob: 5b482c038a86369f6580c8ac1d342f561960a4e2 [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"
Owen Min8dfd0742019-09-06 21:27:0812#include "base/strings/string_split.h"
Nick Petersond952cb772018-03-07 15:46:0313#include "build/build_config.h"
binjin1e1cc33a2014-10-09 18:08:1614#include "chrome/browser/extensions/extension_management_constants.h"
[email protected]01253d272013-10-21 17:07:5015#include "chrome/browser/extensions/external_policy_loader.h"
[email protected]fdd28372014-08-21 02:27:2616#include "components/crx_file/id_util.h"
[email protected]f6c403b2013-12-05 19:01:2517#include "components/policy/core/browser/policy_error_map.h"
[email protected]c4a138a2013-11-21 19:54:5718#include "components/policy/core/common/policy_map.h"
binjin1e1cc33a2014-10-09 18:08:1619#include "components/policy/core/common/schema.h"
brettw39d6ba42016-08-24 16:56:3820#include "components/policy/policy_constants.h"
brettwb1fc1b82016-02-02 00:19:0821#include "components/prefs/pref_value_map.h"
thestig4a2e88e2016-08-27 23:23:5122#include "components/strings/grit/components_strings.h"
[email protected]234fc5ff2014-01-16 23:32:2823#include "extensions/browser/pref_names.h"
[email protected]e4452d32013-11-15 23:07:4124#include "extensions/common/extension.h"
Nick Petersond952cb772018-03-07 15:46:0325#include "extensions/common/extension_urls.h"
binjin1e1cc33a2014-10-09 18:08:1626#include "url/gurl.h"
[email protected]01253d272013-10-21 17:07:5027
Nick Petersond952cb772018-03-07 15:46:0328#if defined(OS_WIN)
Hector Carmonac3565bc42019-02-01 23:31:2129#include "base/enterprise_util.h"
Nick Petersond952cb772018-03-07 15:46:0330#endif
31
[email protected]01253d272013-10-21 17:07:5032namespace extensions {
Owen Min8dfd0742019-09-06 21:27:0833namespace {
34// Returns true if extensions_ids contains a list of valid extension ids,
35// divided by comma.
36bool IsValidIdList(const std::string& extension_ids) {
37 std::vector<base::StringPiece> ids = base::SplitStringPiece(
38 extension_ids, ",", base::WhitespaceHandling::TRIM_WHITESPACE,
39 base::SplitResult::SPLIT_WANT_NONEMPTY);
40 if (ids.size() == 0)
41 return false;
42 for (const auto& id : ids) {
43 if (!crx_file::id_util::IdIsValid(id.as_string()))
44 return false;
45 }
46 return true;
47}
48} // namespace
[email protected]01253d272013-10-21 17:07:5049// ExtensionListPolicyHandler implementation -----------------------------------
50
51ExtensionListPolicyHandler::ExtensionListPolicyHandler(const char* policy_name,
52 const char* pref_path,
53 bool allow_wildcards)
Lutz Justene45e3fe2017-08-18 07:11:3954 : policy::ListPolicyHandler(policy_name, base::Value::Type::STRING),
[email protected]01253d272013-10-21 17:07:5055 pref_path_(pref_path),
56 allow_wildcards_(allow_wildcards) {}
57
58ExtensionListPolicyHandler::~ExtensionListPolicyHandler() {}
59
Lutz Justene45e3fe2017-08-18 07:11:3960bool ExtensionListPolicyHandler::CheckListEntry(const base::Value& value) {
61 const std::string& str = value.GetString();
62 if (allow_wildcards_ && str == "*")
[email protected]01253d272013-10-21 17:07:5063 return true;
64
Lutz Justene45e3fe2017-08-18 07:11:3965 // Make sure str is an extension id.
66 return crx_file::id_util::IdIsValid(str);
67}
[email protected]01253d272013-10-21 17:07:5068
Lutz Justene45e3fe2017-08-18 07:11:3969void ExtensionListPolicyHandler::ApplyList(
70 std::unique_ptr<base::ListValue> filtered_list,
71 PrefValueMap* prefs) {
Sylvain Defresne1787960d2019-01-30 21:02:1072 DCHECK(filtered_list);
73 prefs->SetValue(pref_path_,
74 base::Value::FromUniquePtrValue(std::move(filtered_list)));
[email protected]01253d272013-10-21 17:07:5075}
76
achuith4607f072017-03-08 11:49:1377// ExtensionInstallListPolicyHandler implementation ----------------------------
[email protected]01253d272013-10-21 17:07:5078
achuith4607f072017-03-08 11:49:1379ExtensionInstallListPolicyHandler::ExtensionInstallListPolicyHandler(
80 const char* policy_name,
81 const char* pref_name)
82 : policy::TypeCheckingPolicyHandler(policy_name, base::Value::Type::LIST),
83 pref_name_(pref_name) {}
[email protected]01253d272013-10-21 17:07:5084
achuith4607f072017-03-08 11:49:1385bool ExtensionInstallListPolicyHandler::CheckPolicySettings(
[email protected]01253d272013-10-21 17:07:5086 const policy::PolicyMap& policies,
87 policy::PolicyErrorMap* errors) {
88 const base::Value* value;
89 return CheckAndGetValue(policies, errors, &value) &&
achuith4607f072017-03-08 11:49:1390 ParseList(value, nullptr, errors);
[email protected]01253d272013-10-21 17:07:5091}
92
achuith4607f072017-03-08 11:49:1393void ExtensionInstallListPolicyHandler::ApplyPolicySettings(
[email protected]01253d272013-10-21 17:07:5094 const policy::PolicyMap& policies,
95 PrefValueMap* prefs) {
achuith4607f072017-03-08 11:49:1396 const base::Value* value = nullptr;
Sylvain Defresne1787960d2019-01-30 21:02:1097 base::DictionaryValue dict;
achuith4607f072017-03-08 11:49:1398 if (CheckAndGetValue(policies, nullptr, &value) && value &&
Sylvain Defresne1787960d2019-01-30 21:02:1099 ParseList(value, &dict, nullptr)) {
achuith4607f072017-03-08 11:49:13100 prefs->SetValue(pref_name_, std::move(dict));
[email protected]01253d272013-10-21 17:07:50101 }
102}
103
achuith4607f072017-03-08 11:49:13104bool ExtensionInstallListPolicyHandler::ParseList(
[email protected]01253d272013-10-21 17:07:50105 const base::Value* policy_value,
106 base::DictionaryValue* extension_dict,
107 policy::PolicyErrorMap* errors) {
108 if (!policy_value)
109 return true;
110
achuith4607f072017-03-08 11:49:13111 const base::ListValue* policy_list_value = nullptr;
[email protected]01253d272013-10-21 17:07:50112 if (!policy_value->GetAsList(&policy_list_value)) {
113 // This should have been caught in CheckPolicySettings.
114 NOTREACHED();
115 return false;
116 }
117
jdoerrie13cd648c82018-10-02 21:21:02118 for (auto entry(policy_list_value->begin());
[email protected]01253d272013-10-21 17:07:50119 entry != policy_list_value->end(); ++entry) {
120 std::string entry_string;
jdoerriea5676c62017-04-11 18:09:14121 if (!entry->GetAsString(&entry_string)) {
[email protected]01253d272013-10-21 17:07:50122 if (errors) {
thestige7615d6c2016-07-19 19:43:46123 errors->AddError(policy_name(), entry - policy_list_value->begin(),
[email protected]01253d272013-10-21 17:07:50124 IDS_POLICY_TYPE_ERROR,
jdoerriedc72ee942016-12-07 15:43:28125 base::Value::GetTypeName(base::Value::Type::STRING));
[email protected]01253d272013-10-21 17:07:50126 }
127 continue;
128 }
129
Maksim Ivanoveaac2ff2018-04-16 16:23:24130 // Each string item of the list should be of one of the following forms:
131 // * <extension_id>
132 // * <extension_id>;<update_url>
[email protected]01253d272013-10-21 17:07:50133 // Note: The update URL might also contain semicolons.
Maksim Ivanoveaac2ff2018-04-16 16:23:24134 std::string extension_id;
135 std::string update_url;
[email protected]01253d272013-10-21 17:07:50136 size_t pos = entry_string.find(';');
137 if (pos == std::string::npos) {
Maksim Ivanoveaac2ff2018-04-16 16:23:24138 extension_id = entry_string;
139 update_url = extension_urls::kChromeWebstoreUpdateURL;
140 } else {
141 extension_id = entry_string.substr(0, pos);
142 update_url = entry_string.substr(pos + 1);
[email protected]01253d272013-10-21 17:07:50143 }
144
[email protected]fdd28372014-08-21 02:27:26145 if (!crx_file::id_util::IdIsValid(extension_id) ||
[email protected]01253d272013-10-21 17:07:50146 !GURL(update_url).is_valid()) {
147 if (errors) {
148 errors->AddError(policy_name(),
149 entry - policy_list_value->begin(),
150 IDS_POLICY_VALUE_FORMAT_ERROR);
151 }
152 continue;
153 }
154
155 if (extension_dict) {
achuith4607f072017-03-08 11:49:13156 ExternalPolicyLoader::AddExtension(extension_dict, extension_id,
157 update_url);
[email protected]01253d272013-10-21 17:07:50158 }
159 }
160
161 return true;
162}
163
achuith4607f072017-03-08 11:49:13164// ExtensionInstallForcelistPolicyHandler implementation -----------------------
165
166ExtensionInstallForcelistPolicyHandler::ExtensionInstallForcelistPolicyHandler()
167 : ExtensionInstallListPolicyHandler(policy::key::kExtensionInstallForcelist,
168 pref_names::kInstallForceList) {}
169
Alexander Hendrichb07fd55b2019-04-01 09:24:37170// ExtensionInstallLoginScreenExtensionsPolicyHandler implementation -----------
achuith4607f072017-03-08 11:49:13171
Alexander Hendrichb07fd55b2019-04-01 09:24:37172ExtensionInstallLoginScreenExtensionsPolicyHandler::
173 ExtensionInstallLoginScreenExtensionsPolicyHandler()
achuith4607f072017-03-08 11:49:13174 : ExtensionInstallListPolicyHandler(
Alexander Hendrichb07fd55b2019-04-01 09:24:37175 policy::key::kDeviceLoginScreenExtensions,
176 pref_names::kLoginScreenExtensions) {}
achuith4607f072017-03-08 11:49:13177
[email protected]01253d272013-10-21 17:07:50178// ExtensionURLPatternListPolicyHandler implementation -------------------------
179
180ExtensionURLPatternListPolicyHandler::ExtensionURLPatternListPolicyHandler(
181 const char* policy_name,
182 const char* pref_path)
jdoerriedc72ee942016-12-07 15:43:28183 : policy::TypeCheckingPolicyHandler(policy_name, base::Value::Type::LIST),
[email protected]01253d272013-10-21 17:07:50184 pref_path_(pref_path) {}
185
186ExtensionURLPatternListPolicyHandler::~ExtensionURLPatternListPolicyHandler() {}
187
188bool ExtensionURLPatternListPolicyHandler::CheckPolicySettings(
189 const policy::PolicyMap& policies,
190 policy::PolicyErrorMap* errors) {
191 const base::Value* value = NULL;
192 if (!CheckAndGetValue(policies, errors, &value))
193 return false;
194
195 if (!value)
196 return true;
197
198 const base::ListValue* list_value = NULL;
199 if (!value->GetAsList(&list_value)) {
200 NOTREACHED();
201 return false;
202 }
203
204 // Check that the list contains valid URLPattern strings only.
jdoerrie13cd648c82018-10-02 21:21:02205 for (auto entry(list_value->begin()); entry != list_value->end(); ++entry) {
[email protected]01253d272013-10-21 17:07:50206 std::string url_pattern_string;
jdoerriea5676c62017-04-11 18:09:14207 if (!entry->GetAsString(&url_pattern_string)) {
thestige7615d6c2016-07-19 19:43:46208 errors->AddError(policy_name(), entry - list_value->begin(),
[email protected]01253d272013-10-21 17:07:50209 IDS_POLICY_TYPE_ERROR,
jdoerriedc72ee942016-12-07 15:43:28210 base::Value::GetTypeName(base::Value::Type::STRING));
[email protected]01253d272013-10-21 17:07:50211 return false;
212 }
213
214 URLPattern pattern(URLPattern::SCHEME_ALL);
Devlin Croninbd7f2b5fa2018-09-05 17:41:18215 if (pattern.Parse(url_pattern_string) !=
216 URLPattern::ParseResult::kSuccess) {
[email protected]01253d272013-10-21 17:07:50217 errors->AddError(policy_name(),
218 entry - list_value->begin(),
219 IDS_POLICY_VALUE_FORMAT_ERROR);
220 return false;
221 }
222 }
223
224 return true;
225}
226
227void ExtensionURLPatternListPolicyHandler::ApplyPolicySettings(
228 const policy::PolicyMap& policies,
229 PrefValueMap* prefs) {
230 if (!pref_path_)
231 return;
[email protected]cb1078de2013-12-23 20:04:22232 const base::Value* value = policies.GetValue(policy_name());
[email protected]01253d272013-10-21 17:07:50233 if (value)
Sylvain Defresne1787960d2019-01-30 21:02:10234 prefs->SetValue(pref_path_, value->Clone());
[email protected]01253d272013-10-21 17:07:50235}
236
binjin1e1cc33a2014-10-09 18:08:16237// ExtensionSettingsPolicyHandler implementation ------------------------------
238
239ExtensionSettingsPolicyHandler::ExtensionSettingsPolicyHandler(
240 const policy::Schema& chrome_schema)
241 : policy::SchemaValidatingPolicyHandler(
242 policy::key::kExtensionSettings,
243 chrome_schema.GetKnownProperty(policy::key::kExtensionSettings),
244 policy::SCHEMA_ALLOW_UNKNOWN) {
245}
246
247ExtensionSettingsPolicyHandler::~ExtensionSettingsPolicyHandler() {
248}
249
250bool ExtensionSettingsPolicyHandler::CheckPolicySettings(
251 const policy::PolicyMap& policies,
252 policy::PolicyErrorMap* errors) {
dchengc963c7142016-04-08 03:55:22253 std::unique_ptr<base::Value> policy_value;
binjin1e1cc33a2014-10-09 18:08:16254 if (!CheckAndGetValue(policies, errors, &policy_value))
255 return false;
256 if (!policy_value)
257 return true;
258
259 // |policy_value| is expected to conform to the defined schema. But it's
260 // not strictly valid since there are additional restrictions.
261 const base::DictionaryValue* dict_value = NULL;
jdoerrie1f536b22017-10-23 17:15:11262 DCHECK(policy_value->is_dict());
binjin1e1cc33a2014-10-09 18:08:16263 policy_value->GetAsDictionary(&dict_value);
264
265 for (base::DictionaryValue::Iterator it(*dict_value); !it.IsAtEnd();
266 it.Advance()) {
Owen Min8dfd0742019-09-06 21:27:08267 DCHECK(it.key() == schema_constants::kWildcard || IsValidIdList(it.key()));
jdoerrie1f536b22017-10-23 17:15:11268 DCHECK(it.value().is_dict());
binjin1e1cc33a2014-10-09 18:08:16269
270 // Extracts sub dictionary.
271 const base::DictionaryValue* sub_dict = NULL;
272 it.value().GetAsDictionary(&sub_dict);
273
274 std::string installation_mode;
275 if (sub_dict->GetString(schema_constants::kInstallationMode,
276 &installation_mode)) {
277 if (installation_mode == schema_constants::kForceInstalled ||
278 installation_mode == schema_constants::kNormalInstalled) {
279 DCHECK(it.key() != schema_constants::kWildcard);
280 // Verifies that 'update_url' is specified for 'force_installed' and
281 // 'normal_installed' mode.
282 std::string update_url;
283 if (!sub_dict->GetString(schema_constants::kUpdateUrl, &update_url) ||
284 update_url.empty()) {
285 errors->AddError(policy_name(),
286 it.key() + "." + schema_constants::kUpdateUrl,
287 IDS_POLICY_NOT_SPECIFIED_ERROR);
288 return false;
289 }
Nick Petersond952cb772018-03-07 15:46:03290 if (GURL(update_url).is_valid()) {
291// Unless enterprise managed only extensions from the Chrome Webstore
292// can be force installed.
293#if defined(OS_WIN)
294 // We can't use IsWebstoreUpdateUrl() here since the ExtensionClient
295 // isn't set this early during startup.
Hector Carmonac3565bc42019-02-01 23:31:21296 if (!base::IsMachineExternallyManaged() &&
Nick Petersond952cb772018-03-07 15:46:03297 !base::LowerCaseEqualsASCII(
298 update_url, extension_urls::kChromeWebstoreUpdateURL)) {
299 errors->AddError(policy_name(), it.key(),
300 IDS_POLICY_OFF_CWS_URL_ERROR,
301 extension_urls::kChromeWebstoreUpdateURL);
302 return false;
303 }
304#endif
305 } else {
306 // Warns about an invalid update URL.
binjin1e1cc33a2014-10-09 18:08:16307 errors->AddError(
308 policy_name(), IDS_POLICY_INVALID_UPDATE_URL_ERROR, it.key());
309 return false;
310 }
311 }
312 }
Nick Peterson6bdf5822017-06-01 20:42:45313 // Host keys that don't support user defined paths.
Devlin Cronin7e0f41ff2018-05-16 17:19:36314 const char* host_keys[] = {schema_constants::kPolicyBlockedHosts,
315 schema_constants::kPolicyAllowedHosts};
Nick Peterson6bdf5822017-06-01 20:42:45316 const int extension_scheme_mask =
317 URLPattern::GetValidSchemeMaskForExtensions();
318 for (const char* key : host_keys) {
319 const base::ListValue* unparsed_urls;
320 if (sub_dict->GetList(key, &unparsed_urls)) {
321 for (size_t i = 0; i < unparsed_urls->GetSize(); ++i) {
322 std::string unparsed_url;
323 unparsed_urls->GetString(i, &unparsed_url);
324 URLPattern pattern(extension_scheme_mask);
Devlin Cronin35f8e372019-08-16 19:15:38325 URLPattern::ParseResult parse_result = pattern.Parse(unparsed_url);
Nick Peterson6bdf5822017-06-01 20:42:45326 // These keys don't support paths due to how we track the initiator
327 // of a webRequest and cookie security policy. We expect a valid
328 // pattern to return a PARSE_ERROR_EMPTY_PATH.
Devlin Croninbd7f2b5fa2018-09-05 17:41:18329 if (parse_result == URLPattern::ParseResult::kEmptyPath) {
Nick Peterson6bdf5822017-06-01 20:42:45330 // Add a wildcard path to the URL as it should match any path.
Devlin Cronin35f8e372019-08-16 19:15:38331 parse_result = pattern.Parse(unparsed_url + "/*");
Devlin Croninbd7f2b5fa2018-09-05 17:41:18332 } else if (parse_result == URLPattern::ParseResult::kSuccess) {
Nick Peterson6bdf5822017-06-01 20:42:45333 // The user supplied a path, notify them that this is not supported.
334 if (!pattern.match_all_urls()) {
335 errors->AddError(
336 policy_name(), it.key(),
337 "The URL pattern '" + unparsed_url + "' for attribute " +
338 key + " has a path specified. Paths are not " +
339 "supported, please remove the path and try again. " +
340 "e.g. *://example.com/ => *://example.com");
341 return false;
342 }
343 }
Devlin Croninbd7f2b5fa2018-09-05 17:41:18344 if (parse_result != URLPattern::ParseResult::kSuccess) {
Nick Peterson6bdf5822017-06-01 20:42:45345 errors->AddError(policy_name(), it.key(),
346 "Invalid URL pattern '" + unparsed_url +
347 "' for attribute " + key);
348 return false;
349 }
350 }
351 }
352 }
Yann Dagoee291902019-08-19 15:49:06353
354 const base::ListValue* runtime_blocked_hosts = nullptr;
355 if (sub_dict->GetList(schema_constants::kPolicyBlockedHosts,
356 &runtime_blocked_hosts) &&
357 runtime_blocked_hosts->GetList().size() >
358 schema_constants::kMaxItemsURLPatternSet) {
359 errors->AddError(
360 policy_name(), it.key() + "." + schema_constants::kPolicyBlockedHosts,
361 IDS_POLICY_EXTENSION_SETTINGS_ORIGIN_LIMIT_WARNING,
362 base::NumberToString(schema_constants::kMaxItemsURLPatternSet));
363 }
364
365 const base::ListValue* runtime_allowed_hosts = nullptr;
366 if (sub_dict->GetList(schema_constants::kPolicyAllowedHosts,
367 &runtime_allowed_hosts) &&
368 runtime_allowed_hosts->GetList().size() >
369 schema_constants::kMaxItemsURLPatternSet) {
370 errors->AddError(
371 policy_name(), it.key() + "." + schema_constants::kPolicyAllowedHosts,
372 IDS_POLICY_EXTENSION_SETTINGS_ORIGIN_LIMIT_WARNING,
373 base::NumberToString(schema_constants::kMaxItemsURLPatternSet));
374 }
binjin1e1cc33a2014-10-09 18:08:16375 }
376
377 return true;
378}
379
380void ExtensionSettingsPolicyHandler::ApplyPolicySettings(
381 const policy::PolicyMap& policies,
382 PrefValueMap* prefs) {
dchengc963c7142016-04-08 03:55:22383 std::unique_ptr<base::Value> policy_value;
binjin1e1cc33a2014-10-09 18:08:16384 if (!CheckAndGetValue(policies, NULL, &policy_value) || !policy_value)
385 return;
Sylvain Defresne1787960d2019-01-30 21:02:10386 prefs->SetValue(pref_names::kExtensionManagement,
387 base::Value::FromUniquePtrValue(std::move(policy_value)));
binjin1e1cc33a2014-10-09 18:08:16388}
389
[email protected]01253d272013-10-21 17:07:50390} // namespace extensions