Chromium Code Reviews| Index: chrome/browser/chromeos/extensions/signin_screen_policy_provider.cc |
| diff --git a/chrome/browser/chromeos/extensions/signin_screen_policy_provider.cc b/chrome/browser/chromeos/extensions/signin_screen_policy_provider.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c3a60ab5c777ac155d7cb8947ec77e52342a1cf9 |
| --- /dev/null |
| +++ b/chrome/browser/chromeos/extensions/signin_screen_policy_provider.cc |
| @@ -0,0 +1,227 @@ |
| +// Copyright 2013 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 "chrome/browser/chromeos/extensions/signin_screen_policy_provider.h" |
| + |
| +#include <stddef.h> |
| + |
| +#include <cstddef> |
| +#include <string> |
| + |
| +#include "base/logging.h" |
| +#include "base/strings/utf_string_conversions.h" |
| +#include "base/values.h" |
| +#include "chrome/grit/generated_resources.h" |
| +#include "extensions/common/extension.h" |
| +#include "extensions/common/manifest.h" |
| +#include "extensions/common/manifest_constants.h" |
| +#include "ui/base/l10n/l10n_util.h" |
| + |
| +namespace chromeos { |
| + |
| +namespace { |
| + |
| +namespace emk = extensions::manifest_keys; |
| + |
| +// Apps/extensions explicitly whitelisted for use on signin screen. |
| +const char* const kWhitelistedApps[]{ |
| + "kmendfapggjehodndflmmgagdbamhnfd", // Gnubby component extension. |
| + "beknehfpfkghjoafdifaflglpjkojoco", // Gnubby app |
| + "ljoammodoonkhnehlncldjelhidljdpi", // Genius app (help) |
| + "mppnpdlheglhdfmldimlhpnegondlapf", // Virtual Keyboard |
| + nullptr // End of list |
| +}; |
| + |
| +const char* const kSafePermissionStrings[] = { |
| + "usb", |
| + "certificateProvider", |
| + "storage", |
|
asargent_no_longer_on_chrome
2016/07/22 00:13:29
You should use APIPermission::ID here instead of s
Denis Kuznetsov (DE-MUC)
2016/07/22 14:37:14
See reasoning below.
I agree that we'd better to h
|
| + nullptr // End of list |
| +}; |
| + |
| +// Some permissions take the form of a dictionary. See |kSafePermissionStrings| |
| +// for permission strings (and for more documentation). |
| +const char* const kSafePermissionDicts[] = { |
|
asargent_no_longer_on_chrome
2016/07/22 00:13:29
Same thing here, although I wonder if you should e
|
| + // No dictionary permissions are allowed at the moment. |
| + nullptr // End of list |
| +}; |
| + |
| +// Checks if given this provider applies to given extension. |
|
asargent_no_longer_on_chrome
2016/07/22 00:13:28
grammar nit: "if given this provider applies to gi
Denis Kuznetsov (DE-MUC)
2016/07/22 14:37:14
Done.
|
| +bool IsApplicableForSigninPolicy(const extensions::Extension* extension) { |
| + if (extension->location() == extensions::Manifest::COMPONENT) { |
| + // Do not apply to component extensions. |
| + return false; |
| + } |
| + return true; |
| +} |
| + |
| +// Checks if given this provider applies to given extension. |
|
asargent_no_longer_on_chrome
2016/07/22 00:13:29
same grammar nit here
Denis Kuznetsov (DE-MUC)
2016/07/22 14:37:14
Done.
|
| +bool IsProhibitedForSignin(const extensions::Extension* extension) { |
| + if (extension->GetType() != extensions::Manifest::TYPE_HOSTED_APP && |
| + extension->GetType() != extensions::Manifest::TYPE_PLATFORM_APP && |
| + extension->GetType() != extensions::Manifest::TYPE_LEGACY_PACKAGED_APP) { |
| + LOG(ERROR) << extension->id() |
|
asargent_no_longer_on_chrome
2016/07/22 00:13:28
https://chromium.googlesource.com/chromium/src/+/m
Denis Kuznetsov (DE-MUC)
2016/07/22 14:37:14
Yes. Because with ChromeOS login screen the extens
asargent_no_longer_on_chrome
2016/07/25 21:27:01
Ok, that makes sense.
|
| + << " is not an application: " << extension->GetType(); |
| + return true; |
| + } |
| + if (extension->location() == extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD || |
| + extension->location() == extensions::Manifest::EXTERNAL_POLICY) { |
| + return false; |
| + } |
| + LOG(ERROR) << extension->id() |
| + << " location is prohibited: " << extension->location(); |
| + return true; |
| +} |
| + |
| +// Return true iff |entry| is contained in |char_array|. |
| +bool ArrayContains(const char* const char_array[], const std::string& entry) { |
| + for (size_t i = 0; char_array[i] != nullptr; ++i) { |
| + if (entry == char_array[i]) { |
| + return true; |
| + } |
| + } |
| + return false; |
| +} |
| + |
| +// Returns true for platform apps that are considered safe for Public Sessions, |
| +// which among other things requires the manifest top-level entries to be |
| +// contained in the |kSafeManifestEntries| whitelist and all permissions to be |
| +// contained in |kSafePermissionStrings| or |kSafePermissionDicts|. Otherwise |
| +// returns false and logs all reasons for failure. |
| +bool IsSafeForSignin(const extensions::Extension* extension) { |
| + bool safe = true; |
| + for (base::DictionaryValue::Iterator entry(*extension->manifest()->value()); |
| + !entry.IsAtEnd(); entry.Advance()) { |
| + // Permissions must be whitelisted in |kSafePermissionStrings|. |
| + if (entry.key() == emk::kPermissions || |
| + entry.key() == emk::kOptionalPermissions) { |
| + const base::ListValue* list_value; |
| + if (!entry.value().GetAsList(&list_value)) { |
| + LOG(ERROR) << extension->id() << ": " << entry.key() |
| + << " is not a list."; |
| + safe = false; |
| + continue; |
| + } |
| + for (auto it = list_value->begin(); it != list_value->end(); ++it) { |
| + // Try to read as dictionary. |
| + const base::DictionaryValue* dict_value; |
| + if ((*it)->GetAsDictionary(&dict_value)) { |
| + if (dict_value->size() != 1) { |
| + LOG(ERROR) << extension->id() |
| + << " has dict in permission list with size " |
| + << dict_value->size() << "."; |
| + safe = false; |
| + continue; |
| + } |
| + for (base::DictionaryValue::Iterator it2(*dict_value); !it2.IsAtEnd(); |
| + it2.Advance()) { |
| + if (!ArrayContains(kSafePermissionDicts, it2.key())) { |
| + LOG(ERROR) << extension->id() |
| + << " has non-whitelisted dict in permission list: " |
| + << it2.key(); |
| + safe = false; |
| + continue; |
| + } |
| + } |
| + continue; |
| + } |
| + // Try to read as string. |
| + std::string permission_string; |
| + if (!(*it)->GetAsString(&permission_string)) { |
| + LOG(ERROR) << extension->id() << ": " << entry.key() |
| + << " contains a token that's neither a string nor a dict."; |
| + safe = false; |
| + continue; |
| + } |
|
asargent_no_longer_on_chrome
2016/07/22 00:13:28
The code in the above couple of blocks seems a lit
Denis Kuznetsov (DE-MUC)
2016/07/22 14:37:14
We do not want to "allow app if it has permission"
asargent_no_longer_on_chrome
2016/07/25 21:27:01
Do you just care about API permissions or other ty
emaxx
2016/07/26 02:55:30
I would agree that the low-level parsing should id
asargent_no_longer_on_chrome
2016/07/27 21:00:14
Yes, one of the reasons I'm arguing for structured
|
| + // Accept whitelisted permissions. |
| + if (ArrayContains(kSafePermissionStrings, permission_string)) { |
| + continue; |
| + } |
| + LOG(ERROR) << extension->id() |
| + << " requested non-whitelisted permission: " |
| + << permission_string; |
| + return false; |
| + } |
| + // "app" may only contain "background". |
| + } else if (entry.key() == emk::kApp) { |
| + const base::DictionaryValue* dict_value; |
| + if (!entry.value().GetAsDictionary(&dict_value)) { |
| + LOG(ERROR) << extension->id() << ": app is not a dictionary."; |
| + safe = false; |
| + continue; |
| + } |
| + for (base::DictionaryValue::Iterator it(*dict_value); !it.IsAtEnd(); |
| + it.Advance()) { |
| + if (it.key() != "background") { |
|
asargent_no_longer_on_chrome
2016/07/22 00:13:28
I have a similar concern here about this being too
Denis Kuznetsov (DE-MUC)
2016/07/22 14:37:14
We want to allow 3rd-party apps on chromeos sign-i
|
| + LOG(ERROR) << extension->id() |
| + << " has non-whitelisted manifest entry: " << entry.key() |
| + << "." << it.key(); |
| + safe = false; |
| + continue; |
| + } |
| + } |
| + // Require v2 because that's the only version we understand. |
| + } else if (entry.key() == emk::kManifestVersion) { |
| + int version; |
|
asargent_no_longer_on_chrome
2016/07/22 00:13:29
Instead of examining the dictionary value from the
Denis Kuznetsov (DE-MUC)
2016/07/22 14:37:14
Done.
|
| + if (!entry.value().GetAsInteger(&version)) { |
| + LOG(ERROR) << extension->id() << ": " << emk::kManifestVersion |
| + << " is not an integer."; |
| + safe = false; |
| + continue; |
| + } |
| + if (version != 2) { |
| + LOG(ERROR) << extension->id() |
| + << " has non-whitelisted manifest version."; |
| + safe = false; |
| + continue; |
| + } |
| + } else { |
| + // Everything else is a warning. |
| + LOG(WARNING) << extension->id() |
| + << " has non-whitelisted manifest entry: " << entry.key(); |
| + } |
| + } |
| + return safe; |
| +} |
| + |
| +} // namespace |
| + |
| +SigninScreenPolicyProvider::SigninScreenPolicyProvider() {} |
| + |
| +SigninScreenPolicyProvider::~SigninScreenPolicyProvider() {} |
| + |
| +std::string SigninScreenPolicyProvider::GetDebugPolicyProviderName() const { |
| +#if defined(NDEBUG) |
| + NOTREACHED(); |
| + return std::string(); |
| +#else |
| + return "permissions for sign-in screen"; |
| +#endif |
| +} |
| + |
| +bool SigninScreenPolicyProvider::UserMayLoad( |
| + const extensions::Extension* extension, |
| + base::string16* error) const { |
| + if (!IsApplicableForSigninPolicy(extension)) { |
| + return true; |
| + } |
| + if (ArrayContains(kWhitelistedApps, extension->id())) { |
| + return true; |
| + } |
| + // Allow force-installed platform app if all manifest contents are |
| + // whitelisted. |
| + if (!IsProhibitedForSignin(extension) && IsSafeForSignin(extension)) { |
| + return true; |
| + } |
| + // Disallow all other extensions. |
| + if (error) { |
| + *error = |
| + l10n_util::GetStringFUTF16(IDS_EXTENSION_CANT_INSTALL_ON_LOGIN_SCREEN, |
| + base::UTF8ToUTF16(extension->name()), |
| + base::UTF8ToUTF16(extension->id())); |
| + } |
| + return false; |
| +} |
| + |
| +} // namespace chromeos |