blob: 0e2eda119ebc6ab0b6df02379b9540b71e43cf8a [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) {
Sylvain Defresne1787960d2019-01-30 21:02:1055 DCHECK(filtered_list);
56 prefs->SetValue(pref_path_,
57 base::Value::FromUniquePtrValue(std::move(filtered_list)));
[email protected]01253d272013-10-21 17:07:5058}
59
achuith4607f072017-03-08 11:49:1360// ExtensionInstallListPolicyHandler implementation ----------------------------
[email protected]01253d272013-10-21 17:07:5061
achuith4607f072017-03-08 11:49:1362ExtensionInstallListPolicyHandler::ExtensionInstallListPolicyHandler(
63 const char* policy_name,
64 const char* pref_name)
65 : policy::TypeCheckingPolicyHandler(policy_name, base::Value::Type::LIST),
66 pref_name_(pref_name) {}
[email protected]01253d272013-10-21 17:07:5067
achuith4607f072017-03-08 11:49:1368bool ExtensionInstallListPolicyHandler::CheckPolicySettings(
[email protected]01253d272013-10-21 17:07:5069 const policy::PolicyMap& policies,
70 policy::PolicyErrorMap* errors) {
71 const base::Value* value;
72 return CheckAndGetValue(policies, errors, &value) &&
achuith4607f072017-03-08 11:49:1373 ParseList(value, nullptr, errors);
[email protected]01253d272013-10-21 17:07:5074}
75
achuith4607f072017-03-08 11:49:1376void ExtensionInstallListPolicyHandler::ApplyPolicySettings(
[email protected]01253d272013-10-21 17:07:5077 const policy::PolicyMap& policies,
78 PrefValueMap* prefs) {
achuith4607f072017-03-08 11:49:1379 const base::Value* value = nullptr;
Sylvain Defresne1787960d2019-01-30 21:02:1080 base::DictionaryValue dict;
achuith4607f072017-03-08 11:49:1381 if (CheckAndGetValue(policies, nullptr, &value) && value &&
Sylvain Defresne1787960d2019-01-30 21:02:1082 ParseList(value, &dict, nullptr)) {
achuith4607f072017-03-08 11:49:1383 prefs->SetValue(pref_name_, std::move(dict));
[email protected]01253d272013-10-21 17:07:5084 }
85}
86
achuith4607f072017-03-08 11:49:1387bool ExtensionInstallListPolicyHandler::ParseList(
[email protected]01253d272013-10-21 17:07:5088 const base::Value* policy_value,
89 base::DictionaryValue* extension_dict,
90 policy::PolicyErrorMap* errors) {
91 if (!policy_value)
92 return true;
93
achuith4607f072017-03-08 11:49:1394 const base::ListValue* policy_list_value = nullptr;
[email protected]01253d272013-10-21 17:07:5095 if (!policy_value->GetAsList(&policy_list_value)) {
96 // This should have been caught in CheckPolicySettings.
97 NOTREACHED();
98 return false;
99 }
100
jdoerrie13cd648c82018-10-02 21:21:02101 for (auto entry(policy_list_value->begin());
[email protected]01253d272013-10-21 17:07:50102 entry != policy_list_value->end(); ++entry) {
103 std::string entry_string;
jdoerriea5676c62017-04-11 18:09:14104 if (!entry->GetAsString(&entry_string)) {
[email protected]01253d272013-10-21 17:07:50105 if (errors) {
thestige7615d6c2016-07-19 19:43:46106 errors->AddError(policy_name(), entry - policy_list_value->begin(),
[email protected]01253d272013-10-21 17:07:50107 IDS_POLICY_TYPE_ERROR,
jdoerriedc72ee942016-12-07 15:43:28108 base::Value::GetTypeName(base::Value::Type::STRING));
[email protected]01253d272013-10-21 17:07:50109 }
110 continue;
111 }
112
Maksim Ivanoveaac2ff2018-04-16 16:23:24113 // Each string item of the list should be of one of the following forms:
114 // * <extension_id>
115 // * <extension_id>;<update_url>
[email protected]01253d272013-10-21 17:07:50116 // Note: The update URL might also contain semicolons.
Maksim Ivanoveaac2ff2018-04-16 16:23:24117 std::string extension_id;
118 std::string update_url;
[email protected]01253d272013-10-21 17:07:50119 size_t pos = entry_string.find(';');
120 if (pos == std::string::npos) {
Maksim Ivanoveaac2ff2018-04-16 16:23:24121 extension_id = entry_string;
122 update_url = extension_urls::kChromeWebstoreUpdateURL;
123 } else {
124 extension_id = entry_string.substr(0, pos);
125 update_url = entry_string.substr(pos + 1);
[email protected]01253d272013-10-21 17:07:50126 }
127
[email protected]fdd28372014-08-21 02:27:26128 if (!crx_file::id_util::IdIsValid(extension_id) ||
[email protected]01253d272013-10-21 17:07:50129 !GURL(update_url).is_valid()) {
130 if (errors) {
131 errors->AddError(policy_name(),
132 entry - policy_list_value->begin(),
133 IDS_POLICY_VALUE_FORMAT_ERROR);
134 }
135 continue;
136 }
137
138 if (extension_dict) {
achuith4607f072017-03-08 11:49:13139 ExternalPolicyLoader::AddExtension(extension_dict, extension_id,
140 update_url);
[email protected]01253d272013-10-21 17:07:50141 }
142 }
143
144 return true;
145}
146
achuith4607f072017-03-08 11:49:13147// ExtensionInstallForcelistPolicyHandler implementation -----------------------
148
149ExtensionInstallForcelistPolicyHandler::ExtensionInstallForcelistPolicyHandler()
150 : ExtensionInstallListPolicyHandler(policy::key::kExtensionInstallForcelist,
151 pref_names::kInstallForceList) {}
152
153// ExtensionInstallLoginScreenAppListPolicyHandler implementation --------------
154
155ExtensionInstallLoginScreenAppListPolicyHandler::
156 ExtensionInstallLoginScreenAppListPolicyHandler()
157 : ExtensionInstallListPolicyHandler(
158 policy::key::kDeviceLoginScreenAppInstallList,
159 pref_names::kInstallLoginScreenAppList) {}
160
[email protected]01253d272013-10-21 17:07:50161// ExtensionURLPatternListPolicyHandler implementation -------------------------
162
163ExtensionURLPatternListPolicyHandler::ExtensionURLPatternListPolicyHandler(
164 const char* policy_name,
165 const char* pref_path)
jdoerriedc72ee942016-12-07 15:43:28166 : policy::TypeCheckingPolicyHandler(policy_name, base::Value::Type::LIST),
[email protected]01253d272013-10-21 17:07:50167 pref_path_(pref_path) {}
168
169ExtensionURLPatternListPolicyHandler::~ExtensionURLPatternListPolicyHandler() {}
170
171bool ExtensionURLPatternListPolicyHandler::CheckPolicySettings(
172 const policy::PolicyMap& policies,
173 policy::PolicyErrorMap* errors) {
174 const base::Value* value = NULL;
175 if (!CheckAndGetValue(policies, errors, &value))
176 return false;
177
178 if (!value)
179 return true;
180
181 const base::ListValue* list_value = NULL;
182 if (!value->GetAsList(&list_value)) {
183 NOTREACHED();
184 return false;
185 }
186
187 // Check that the list contains valid URLPattern strings only.
jdoerrie13cd648c82018-10-02 21:21:02188 for (auto entry(list_value->begin()); entry != list_value->end(); ++entry) {
[email protected]01253d272013-10-21 17:07:50189 std::string url_pattern_string;
jdoerriea5676c62017-04-11 18:09:14190 if (!entry->GetAsString(&url_pattern_string)) {
thestige7615d6c2016-07-19 19:43:46191 errors->AddError(policy_name(), entry - list_value->begin(),
[email protected]01253d272013-10-21 17:07:50192 IDS_POLICY_TYPE_ERROR,
jdoerriedc72ee942016-12-07 15:43:28193 base::Value::GetTypeName(base::Value::Type::STRING));
[email protected]01253d272013-10-21 17:07:50194 return false;
195 }
196
197 URLPattern pattern(URLPattern::SCHEME_ALL);
Devlin Croninbd7f2b5fa2018-09-05 17:41:18198 if (pattern.Parse(url_pattern_string) !=
199 URLPattern::ParseResult::kSuccess) {
[email protected]01253d272013-10-21 17:07:50200 errors->AddError(policy_name(),
201 entry - list_value->begin(),
202 IDS_POLICY_VALUE_FORMAT_ERROR);
203 return false;
204 }
205 }
206
207 return true;
208}
209
210void ExtensionURLPatternListPolicyHandler::ApplyPolicySettings(
211 const policy::PolicyMap& policies,
212 PrefValueMap* prefs) {
213 if (!pref_path_)
214 return;
[email protected]cb1078de2013-12-23 20:04:22215 const base::Value* value = policies.GetValue(policy_name());
[email protected]01253d272013-10-21 17:07:50216 if (value)
Sylvain Defresne1787960d2019-01-30 21:02:10217 prefs->SetValue(pref_path_, value->Clone());
[email protected]01253d272013-10-21 17:07:50218}
219
binjin1e1cc33a2014-10-09 18:08:16220// ExtensionSettingsPolicyHandler implementation ------------------------------
221
222ExtensionSettingsPolicyHandler::ExtensionSettingsPolicyHandler(
223 const policy::Schema& chrome_schema)
224 : policy::SchemaValidatingPolicyHandler(
225 policy::key::kExtensionSettings,
226 chrome_schema.GetKnownProperty(policy::key::kExtensionSettings),
227 policy::SCHEMA_ALLOW_UNKNOWN) {
228}
229
230ExtensionSettingsPolicyHandler::~ExtensionSettingsPolicyHandler() {
231}
232
233bool ExtensionSettingsPolicyHandler::CheckPolicySettings(
234 const policy::PolicyMap& policies,
235 policy::PolicyErrorMap* errors) {
dchengc963c7142016-04-08 03:55:22236 std::unique_ptr<base::Value> policy_value;
binjin1e1cc33a2014-10-09 18:08:16237 if (!CheckAndGetValue(policies, errors, &policy_value))
238 return false;
239 if (!policy_value)
240 return true;
241
242 // |policy_value| is expected to conform to the defined schema. But it's
243 // not strictly valid since there are additional restrictions.
244 const base::DictionaryValue* dict_value = NULL;
jdoerrie1f536b22017-10-23 17:15:11245 DCHECK(policy_value->is_dict());
binjin1e1cc33a2014-10-09 18:08:16246 policy_value->GetAsDictionary(&dict_value);
247
248 for (base::DictionaryValue::Iterator it(*dict_value); !it.IsAtEnd();
249 it.Advance()) {
250 DCHECK(it.key() == schema_constants::kWildcard ||
251 crx_file::id_util::IdIsValid(it.key()));
jdoerrie1f536b22017-10-23 17:15:11252 DCHECK(it.value().is_dict());
binjin1e1cc33a2014-10-09 18:08:16253
254 // Extracts sub dictionary.
255 const base::DictionaryValue* sub_dict = NULL;
256 it.value().GetAsDictionary(&sub_dict);
257
258 std::string installation_mode;
259 if (sub_dict->GetString(schema_constants::kInstallationMode,
260 &installation_mode)) {
261 if (installation_mode == schema_constants::kForceInstalled ||
262 installation_mode == schema_constants::kNormalInstalled) {
263 DCHECK(it.key() != schema_constants::kWildcard);
264 // Verifies that 'update_url' is specified for 'force_installed' and
265 // 'normal_installed' mode.
266 std::string update_url;
267 if (!sub_dict->GetString(schema_constants::kUpdateUrl, &update_url) ||
268 update_url.empty()) {
269 errors->AddError(policy_name(),
270 it.key() + "." + schema_constants::kUpdateUrl,
271 IDS_POLICY_NOT_SPECIFIED_ERROR);
272 return false;
273 }
Nick Petersond952cb772018-03-07 15:46:03274 if (GURL(update_url).is_valid()) {
275// Unless enterprise managed only extensions from the Chrome Webstore
276// can be force installed.
277#if defined(OS_WIN)
278 // We can't use IsWebstoreUpdateUrl() here since the ExtensionClient
279 // isn't set this early during startup.
280 if (!base::win::IsEnterpriseManaged() &&
281 !base::LowerCaseEqualsASCII(
282 update_url, extension_urls::kChromeWebstoreUpdateURL)) {
283 errors->AddError(policy_name(), it.key(),
284 IDS_POLICY_OFF_CWS_URL_ERROR,
285 extension_urls::kChromeWebstoreUpdateURL);
286 return false;
287 }
288#endif
289 } else {
290 // Warns about an invalid update URL.
binjin1e1cc33a2014-10-09 18:08:16291 errors->AddError(
292 policy_name(), IDS_POLICY_INVALID_UPDATE_URL_ERROR, it.key());
293 return false;
294 }
295 }
296 }
Nick Peterson6bdf5822017-06-01 20:42:45297 // Host keys that don't support user defined paths.
Devlin Cronin7e0f41ff2018-05-16 17:19:36298 const char* host_keys[] = {schema_constants::kPolicyBlockedHosts,
299 schema_constants::kPolicyAllowedHosts};
Nick Peterson6bdf5822017-06-01 20:42:45300 const int extension_scheme_mask =
301 URLPattern::GetValidSchemeMaskForExtensions();
302 for (const char* key : host_keys) {
303 const base::ListValue* unparsed_urls;
304 if (sub_dict->GetList(key, &unparsed_urls)) {
305 for (size_t i = 0; i < unparsed_urls->GetSize(); ++i) {
306 std::string unparsed_url;
307 unparsed_urls->GetString(i, &unparsed_url);
308 URLPattern pattern(extension_scheme_mask);
309 URLPattern::ParseResult parse_result = pattern.Parse(
Nick Peterson87ecb102018-10-16 04:55:01310 unparsed_url, URLPattern::DENY_WILDCARD_FOR_EFFECTIVE_TLD);
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.
316 parse_result =
317 pattern.Parse(unparsed_url + "/*",
Nick Peterson87ecb102018-10-16 04:55:01318 URLPattern::DENY_WILDCARD_FOR_EFFECTIVE_TLD);
Devlin Croninbd7f2b5fa2018-09-05 17:41:18319 } else if (parse_result == URLPattern::ParseResult::kSuccess) {
Nick Peterson6bdf5822017-06-01 20:42:45320 // The user supplied a path, notify them that this is not supported.
321 if (!pattern.match_all_urls()) {
322 errors->AddError(
323 policy_name(), it.key(),
324 "The URL pattern '" + unparsed_url + "' for attribute " +
325 key + " has a path specified. Paths are not " +
326 "supported, please remove the path and try again. " +
327 "e.g. *://example.com/ => *://example.com");
328 return false;
329 }
330 }
Devlin Croninbd7f2b5fa2018-09-05 17:41:18331 if (parse_result != URLPattern::ParseResult::kSuccess) {
Nick Peterson6bdf5822017-06-01 20:42:45332 errors->AddError(policy_name(), it.key(),
333 "Invalid URL pattern '" + unparsed_url +
334 "' for attribute " + key);
335 return false;
336 }
337 }
338 }
339 }
binjin1e1cc33a2014-10-09 18:08:16340 }
341
342 return true;
343}
344
345void ExtensionSettingsPolicyHandler::ApplyPolicySettings(
346 const policy::PolicyMap& policies,
347 PrefValueMap* prefs) {
dchengc963c7142016-04-08 03:55:22348 std::unique_ptr<base::Value> policy_value;
binjin1e1cc33a2014-10-09 18:08:16349 if (!CheckAndGetValue(policies, NULL, &policy_value) || !policy_value)
350 return;
Sylvain Defresne1787960d2019-01-30 21:02:10351 prefs->SetValue(pref_names::kExtensionManagement,
352 base::Value::FromUniquePtrValue(std::move(policy_value)));
binjin1e1cc33a2014-10-09 18:08:16353}
354
[email protected]01253d272013-10-21 17:07:50355} // namespace extensions