blob: 4be1677ae00f705361d938af30137bbb90221852 [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"
Nick Petersond952cb772018-03-07 15:46:0311#include "build/build_config.h"
binjin1e1cc33a2014-10-09 18:08:1612#include "chrome/browser/extensions/extension_management_constants.h"
[email protected]01253d272013-10-21 17:07:5013#include "chrome/browser/extensions/external_policy_loader.h"
[email protected]fdd28372014-08-21 02:27:2614#include "components/crx_file/id_util.h"
[email protected]f6c403b2013-12-05 19:01:2515#include "components/policy/core/browser/policy_error_map.h"
[email protected]c4a138a2013-11-21 19:54:5716#include "components/policy/core/common/policy_map.h"
binjin1e1cc33a2014-10-09 18:08:1617#include "components/policy/core/common/schema.h"
brettw39d6ba42016-08-24 16:56:3818#include "components/policy/policy_constants.h"
brettwb1fc1b82016-02-02 00:19:0819#include "components/prefs/pref_value_map.h"
thestig4a2e88e2016-08-27 23:23:5120#include "components/strings/grit/components_strings.h"
[email protected]234fc5ff2014-01-16 23:32:2821#include "extensions/browser/pref_names.h"
[email protected]e4452d32013-11-15 23:07:4122#include "extensions/common/extension.h"
Nick Petersond952cb772018-03-07 15:46:0323#include "extensions/common/extension_urls.h"
binjin1e1cc33a2014-10-09 18:08:1624#include "url/gurl.h"
[email protected]01253d272013-10-21 17:07:5025
Nick Petersond952cb772018-03-07 15:46:0326#if defined(OS_WIN)
27#include "base/win/win_util.h"
28#endif
29
[email protected]01253d272013-10-21 17:07:5030namespace extensions {
31
32// ExtensionListPolicyHandler implementation -----------------------------------
33
34ExtensionListPolicyHandler::ExtensionListPolicyHandler(const char* policy_name,
35 const char* pref_path,
36 bool allow_wildcards)
Lutz Justene45e3fe2017-08-18 07:11:3937 : policy::ListPolicyHandler(policy_name, base::Value::Type::STRING),
[email protected]01253d272013-10-21 17:07:5038 pref_path_(pref_path),
39 allow_wildcards_(allow_wildcards) {}
40
41ExtensionListPolicyHandler::~ExtensionListPolicyHandler() {}
42
Lutz Justene45e3fe2017-08-18 07:11:3943bool ExtensionListPolicyHandler::CheckListEntry(const base::Value& value) {
44 const std::string& str = value.GetString();
45 if (allow_wildcards_ && str == "*")
[email protected]01253d272013-10-21 17:07:5046 return true;
47
Lutz Justene45e3fe2017-08-18 07:11:3948 // Make sure str is an extension id.
49 return crx_file::id_util::IdIsValid(str);
50}
[email protected]01253d272013-10-21 17:07:5051
Lutz Justene45e3fe2017-08-18 07:11:3952void ExtensionListPolicyHandler::ApplyList(
53 std::unique_ptr<base::ListValue> filtered_list,
54 PrefValueMap* prefs) {
55 prefs->SetValue(pref_path_, std::move(filtered_list));
[email protected]01253d272013-10-21 17:07:5056}
57
achuith4607f072017-03-08 11:49:1358// ExtensionInstallListPolicyHandler implementation ----------------------------
[email protected]01253d272013-10-21 17:07:5059
achuith4607f072017-03-08 11:49:1360ExtensionInstallListPolicyHandler::ExtensionInstallListPolicyHandler(
61 const char* policy_name,
62 const char* pref_name)
63 : policy::TypeCheckingPolicyHandler(policy_name, base::Value::Type::LIST),
64 pref_name_(pref_name) {}
[email protected]01253d272013-10-21 17:07:5065
achuith4607f072017-03-08 11:49:1366bool ExtensionInstallListPolicyHandler::CheckPolicySettings(
[email protected]01253d272013-10-21 17:07:5067 const policy::PolicyMap& policies,
68 policy::PolicyErrorMap* errors) {
69 const base::Value* value;
70 return CheckAndGetValue(policies, errors, &value) &&
achuith4607f072017-03-08 11:49:1371 ParseList(value, nullptr, errors);
[email protected]01253d272013-10-21 17:07:5072}
73
achuith4607f072017-03-08 11:49:1374void ExtensionInstallListPolicyHandler::ApplyPolicySettings(
[email protected]01253d272013-10-21 17:07:5075 const policy::PolicyMap& policies,
76 PrefValueMap* prefs) {
achuith4607f072017-03-08 11:49:1377 const base::Value* value = nullptr;
dchengc963c7142016-04-08 03:55:2278 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
achuith4607f072017-03-08 11:49:1379 if (CheckAndGetValue(policies, nullptr, &value) && value &&
80 ParseList(value, dict.get(), nullptr)) {
81 prefs->SetValue(pref_name_, std::move(dict));
[email protected]01253d272013-10-21 17:07:5082 }
83}
84
achuith4607f072017-03-08 11:49:1385bool ExtensionInstallListPolicyHandler::ParseList(
[email protected]01253d272013-10-21 17:07:5086 const base::Value* policy_value,
87 base::DictionaryValue* extension_dict,
88 policy::PolicyErrorMap* errors) {
89 if (!policy_value)
90 return true;
91
achuith4607f072017-03-08 11:49:1392 const base::ListValue* policy_list_value = nullptr;
[email protected]01253d272013-10-21 17:07:5093 if (!policy_value->GetAsList(&policy_list_value)) {
94 // This should have been caught in CheckPolicySettings.
95 NOTREACHED();
96 return false;
97 }
98
jdoerrie13cd648c82018-10-02 21:21:0299 for (auto entry(policy_list_value->begin());
[email protected]01253d272013-10-21 17:07:50100 entry != policy_list_value->end(); ++entry) {
101 std::string entry_string;
jdoerriea5676c62017-04-11 18:09:14102 if (!entry->GetAsString(&entry_string)) {
[email protected]01253d272013-10-21 17:07:50103 if (errors) {
thestige7615d6c2016-07-19 19:43:46104 errors->AddError(policy_name(), entry - policy_list_value->begin(),
[email protected]01253d272013-10-21 17:07:50105 IDS_POLICY_TYPE_ERROR,
jdoerriedc72ee942016-12-07 15:43:28106 base::Value::GetTypeName(base::Value::Type::STRING));
[email protected]01253d272013-10-21 17:07:50107 }
108 continue;
109 }
110
Maksim Ivanoveaac2ff2018-04-16 16:23:24111 // Each string item of the list should be of one of the following forms:
112 // * <extension_id>
113 // * <extension_id>;<update_url>
[email protected]01253d272013-10-21 17:07:50114 // Note: The update URL might also contain semicolons.
Maksim Ivanoveaac2ff2018-04-16 16:23:24115 std::string extension_id;
116 std::string update_url;
[email protected]01253d272013-10-21 17:07:50117 size_t pos = entry_string.find(';');
118 if (pos == std::string::npos) {
Maksim Ivanoveaac2ff2018-04-16 16:23:24119 extension_id = entry_string;
120 update_url = extension_urls::kChromeWebstoreUpdateURL;
121 } else {
122 extension_id = entry_string.substr(0, pos);
123 update_url = entry_string.substr(pos + 1);
[email protected]01253d272013-10-21 17:07:50124 }
125
[email protected]fdd28372014-08-21 02:27:26126 if (!crx_file::id_util::IdIsValid(extension_id) ||
[email protected]01253d272013-10-21 17:07:50127 !GURL(update_url).is_valid()) {
128 if (errors) {
129 errors->AddError(policy_name(),
130 entry - policy_list_value->begin(),
131 IDS_POLICY_VALUE_FORMAT_ERROR);
132 }
133 continue;
134 }
135
136 if (extension_dict) {
achuith4607f072017-03-08 11:49:13137 ExternalPolicyLoader::AddExtension(extension_dict, extension_id,
138 update_url);
[email protected]01253d272013-10-21 17:07:50139 }
140 }
141
142 return true;
143}
144
achuith4607f072017-03-08 11:49:13145// ExtensionInstallForcelistPolicyHandler implementation -----------------------
146
147ExtensionInstallForcelistPolicyHandler::ExtensionInstallForcelistPolicyHandler()
148 : ExtensionInstallListPolicyHandler(policy::key::kExtensionInstallForcelist,
149 pref_names::kInstallForceList) {}
150
151// ExtensionInstallLoginScreenAppListPolicyHandler implementation --------------
152
153ExtensionInstallLoginScreenAppListPolicyHandler::
154 ExtensionInstallLoginScreenAppListPolicyHandler()
155 : ExtensionInstallListPolicyHandler(
156 policy::key::kDeviceLoginScreenAppInstallList,
157 pref_names::kInstallLoginScreenAppList) {}
158
[email protected]01253d272013-10-21 17:07:50159// ExtensionURLPatternListPolicyHandler implementation -------------------------
160
161ExtensionURLPatternListPolicyHandler::ExtensionURLPatternListPolicyHandler(
162 const char* policy_name,
163 const char* pref_path)
jdoerriedc72ee942016-12-07 15:43:28164 : policy::TypeCheckingPolicyHandler(policy_name, base::Value::Type::LIST),
[email protected]01253d272013-10-21 17:07:50165 pref_path_(pref_path) {}
166
167ExtensionURLPatternListPolicyHandler::~ExtensionURLPatternListPolicyHandler() {}
168
169bool ExtensionURLPatternListPolicyHandler::CheckPolicySettings(
170 const policy::PolicyMap& policies,
171 policy::PolicyErrorMap* errors) {
172 const base::Value* value = NULL;
173 if (!CheckAndGetValue(policies, errors, &value))
174 return false;
175
176 if (!value)
177 return true;
178
179 const base::ListValue* list_value = NULL;
180 if (!value->GetAsList(&list_value)) {
181 NOTREACHED();
182 return false;
183 }
184
185 // Check that the list contains valid URLPattern strings only.
jdoerrie13cd648c82018-10-02 21:21:02186 for (auto entry(list_value->begin()); entry != list_value->end(); ++entry) {
[email protected]01253d272013-10-21 17:07:50187 std::string url_pattern_string;
jdoerriea5676c62017-04-11 18:09:14188 if (!entry->GetAsString(&url_pattern_string)) {
thestige7615d6c2016-07-19 19:43:46189 errors->AddError(policy_name(), entry - list_value->begin(),
[email protected]01253d272013-10-21 17:07:50190 IDS_POLICY_TYPE_ERROR,
jdoerriedc72ee942016-12-07 15:43:28191 base::Value::GetTypeName(base::Value::Type::STRING));
[email protected]01253d272013-10-21 17:07:50192 return false;
193 }
194
195 URLPattern pattern(URLPattern::SCHEME_ALL);
Devlin Croninbd7f2b5fa2018-09-05 17:41:18196 if (pattern.Parse(url_pattern_string) !=
197 URLPattern::ParseResult::kSuccess) {
[email protected]01253d272013-10-21 17:07:50198 errors->AddError(policy_name(),
199 entry - list_value->begin(),
200 IDS_POLICY_VALUE_FORMAT_ERROR);
201 return false;
202 }
203 }
204
205 return true;
206}
207
208void ExtensionURLPatternListPolicyHandler::ApplyPolicySettings(
209 const policy::PolicyMap& policies,
210 PrefValueMap* prefs) {
211 if (!pref_path_)
212 return;
[email protected]cb1078de2013-12-23 20:04:22213 const base::Value* value = policies.GetValue(policy_name());
[email protected]01253d272013-10-21 17:07:50214 if (value)
estade0bd407f2015-06-26 18:16:18215 prefs->SetValue(pref_path_, value->CreateDeepCopy());
[email protected]01253d272013-10-21 17:07:50216}
217
binjin1e1cc33a2014-10-09 18:08:16218// ExtensionSettingsPolicyHandler implementation ------------------------------
219
220ExtensionSettingsPolicyHandler::ExtensionSettingsPolicyHandler(
221 const policy::Schema& chrome_schema)
222 : policy::SchemaValidatingPolicyHandler(
223 policy::key::kExtensionSettings,
224 chrome_schema.GetKnownProperty(policy::key::kExtensionSettings),
225 policy::SCHEMA_ALLOW_UNKNOWN) {
226}
227
228ExtensionSettingsPolicyHandler::~ExtensionSettingsPolicyHandler() {
229}
230
231bool ExtensionSettingsPolicyHandler::CheckPolicySettings(
232 const policy::PolicyMap& policies,
233 policy::PolicyErrorMap* errors) {
dchengc963c7142016-04-08 03:55:22234 std::unique_ptr<base::Value> policy_value;
binjin1e1cc33a2014-10-09 18:08:16235 if (!CheckAndGetValue(policies, errors, &policy_value))
236 return false;
237 if (!policy_value)
238 return true;
239
240 // |policy_value| is expected to conform to the defined schema. But it's
241 // not strictly valid since there are additional restrictions.
242 const base::DictionaryValue* dict_value = NULL;
jdoerrie1f536b22017-10-23 17:15:11243 DCHECK(policy_value->is_dict());
binjin1e1cc33a2014-10-09 18:08:16244 policy_value->GetAsDictionary(&dict_value);
245
246 for (base::DictionaryValue::Iterator it(*dict_value); !it.IsAtEnd();
247 it.Advance()) {
248 DCHECK(it.key() == schema_constants::kWildcard ||
249 crx_file::id_util::IdIsValid(it.key()));
jdoerrie1f536b22017-10-23 17:15:11250 DCHECK(it.value().is_dict());
binjin1e1cc33a2014-10-09 18:08:16251
252 // Extracts sub dictionary.
253 const base::DictionaryValue* sub_dict = NULL;
254 it.value().GetAsDictionary(&sub_dict);
255
256 std::string installation_mode;
257 if (sub_dict->GetString(schema_constants::kInstallationMode,
258 &installation_mode)) {
259 if (installation_mode == schema_constants::kForceInstalled ||
260 installation_mode == schema_constants::kNormalInstalled) {
261 DCHECK(it.key() != schema_constants::kWildcard);
262 // Verifies that 'update_url' is specified for 'force_installed' and
263 // 'normal_installed' mode.
264 std::string update_url;
265 if (!sub_dict->GetString(schema_constants::kUpdateUrl, &update_url) ||
266 update_url.empty()) {
267 errors->AddError(policy_name(),
268 it.key() + "." + schema_constants::kUpdateUrl,
269 IDS_POLICY_NOT_SPECIFIED_ERROR);
270 return false;
271 }
Nick Petersond952cb772018-03-07 15:46:03272 if (GURL(update_url).is_valid()) {
273// Unless enterprise managed only extensions from the Chrome Webstore
274// can be force installed.
275#if defined(OS_WIN)
276 // We can't use IsWebstoreUpdateUrl() here since the ExtensionClient
277 // isn't set this early during startup.
278 if (!base::win::IsEnterpriseManaged() &&
279 !base::LowerCaseEqualsASCII(
280 update_url, extension_urls::kChromeWebstoreUpdateURL)) {
281 errors->AddError(policy_name(), it.key(),
282 IDS_POLICY_OFF_CWS_URL_ERROR,
283 extension_urls::kChromeWebstoreUpdateURL);
284 return false;
285 }
286#endif
287 } else {
288 // Warns about an invalid update URL.
binjin1e1cc33a2014-10-09 18:08:16289 errors->AddError(
290 policy_name(), IDS_POLICY_INVALID_UPDATE_URL_ERROR, it.key());
291 return false;
292 }
293 }
294 }
Nick Peterson6bdf5822017-06-01 20:42:45295 // Host keys that don't support user defined paths.
Devlin Cronin7e0f41ff2018-05-16 17:19:36296 const char* host_keys[] = {schema_constants::kPolicyBlockedHosts,
297 schema_constants::kPolicyAllowedHosts};
Nick Peterson6bdf5822017-06-01 20:42:45298 const int extension_scheme_mask =
299 URLPattern::GetValidSchemeMaskForExtensions();
300 for (const char* key : host_keys) {
301 const base::ListValue* unparsed_urls;
302 if (sub_dict->GetList(key, &unparsed_urls)) {
303 for (size_t i = 0; i < unparsed_urls->GetSize(); ++i) {
304 std::string unparsed_url;
305 unparsed_urls->GetString(i, &unparsed_url);
306 URLPattern pattern(extension_scheme_mask);
307 URLPattern::ParseResult parse_result = pattern.Parse(
Nick Peterson87ecb102018-10-16 04:55:01308 unparsed_url, URLPattern::DENY_WILDCARD_FOR_EFFECTIVE_TLD);
Nick Peterson6bdf5822017-06-01 20:42:45309 // These keys don't support paths due to how we track the initiator
310 // of a webRequest and cookie security policy. We expect a valid
311 // pattern to return a PARSE_ERROR_EMPTY_PATH.
Devlin Croninbd7f2b5fa2018-09-05 17:41:18312 if (parse_result == URLPattern::ParseResult::kEmptyPath) {
Nick Peterson6bdf5822017-06-01 20:42:45313 // Add a wildcard path to the URL as it should match any path.
314 parse_result =
315 pattern.Parse(unparsed_url + "/*",
Nick Peterson87ecb102018-10-16 04:55:01316 URLPattern::DENY_WILDCARD_FOR_EFFECTIVE_TLD);
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 }
binjin1e1cc33a2014-10-09 18:08:16338 }
339
340 return true;
341}
342
343void ExtensionSettingsPolicyHandler::ApplyPolicySettings(
344 const policy::PolicyMap& policies,
345 PrefValueMap* prefs) {
dchengc963c7142016-04-08 03:55:22346 std::unique_ptr<base::Value> policy_value;
binjin1e1cc33a2014-10-09 18:08:16347 if (!CheckAndGetValue(policies, NULL, &policy_value) || !policy_value)
348 return;
dcheng1fc00f12015-12-26 22:18:03349 prefs->SetValue(pref_names::kExtensionManagement, std::move(policy_value));
binjin1e1cc33a2014-10-09 18:08:16350}
351
[email protected]01253d272013-10-21 17:07:50352} // namespace extensions