Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // 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/chromeos/extensions/signin_screen_policy_provider.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 | |
| 9 #include <cstddef> | |
| 10 #include <string> | |
| 11 | |
| 12 #include "base/logging.h" | |
| 13 #include "base/strings/utf_string_conversions.h" | |
| 14 #include "base/values.h" | |
| 15 #include "chrome/grit/generated_resources.h" | |
| 16 #include "extensions/common/extension.h" | |
| 17 #include "extensions/common/manifest.h" | |
| 18 #include "extensions/common/manifest_constants.h" | |
| 19 #include "ui/base/l10n/l10n_util.h" | |
| 20 | |
| 21 namespace chromeos { | |
| 22 | |
| 23 namespace { | |
| 24 | |
| 25 namespace emk = extensions::manifest_keys; | |
| 26 | |
| 27 // Apps/extensions explicitly whitelisted for use on signin screen. | |
| 28 const char* const kWhitelistedApps[]{ | |
| 29 "kmendfapggjehodndflmmgagdbamhnfd", // Gnubby component extension. | |
| 30 "beknehfpfkghjoafdifaflglpjkojoco", // Gnubby app | |
| 31 "ljoammodoonkhnehlncldjelhidljdpi", // Genius app (help) | |
| 32 "mppnpdlheglhdfmldimlhpnegondlapf", // Virtual Keyboard | |
| 33 nullptr // End of list | |
| 34 }; | |
| 35 | |
| 36 const char* const kSafePermissionStrings[] = { | |
| 37 "usb", | |
| 38 "certificateProvider", | |
| 39 "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
| |
| 40 nullptr // End of list | |
| 41 }; | |
| 42 | |
| 43 // Some permissions take the form of a dictionary. See |kSafePermissionStrings| | |
| 44 // for permission strings (and for more documentation). | |
| 45 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
| |
| 46 // No dictionary permissions are allowed at the moment. | |
| 47 nullptr // End of list | |
| 48 }; | |
| 49 | |
| 50 // 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.
| |
| 51 bool IsApplicableForSigninPolicy(const extensions::Extension* extension) { | |
| 52 if (extension->location() == extensions::Manifest::COMPONENT) { | |
| 53 // Do not apply to component extensions. | |
| 54 return false; | |
| 55 } | |
| 56 return true; | |
| 57 } | |
| 58 | |
| 59 // 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.
| |
| 60 bool IsProhibitedForSignin(const extensions::Extension* extension) { | |
| 61 if (extension->GetType() != extensions::Manifest::TYPE_HOSTED_APP && | |
| 62 extension->GetType() != extensions::Manifest::TYPE_PLATFORM_APP && | |
| 63 extension->GetType() != extensions::Manifest::TYPE_LEGACY_PACKAGED_APP) { | |
| 64 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.
| |
| 65 << " is not an application: " << extension->GetType(); | |
| 66 return true; | |
| 67 } | |
| 68 if (extension->location() == extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD || | |
| 69 extension->location() == extensions::Manifest::EXTERNAL_POLICY) { | |
| 70 return false; | |
| 71 } | |
| 72 LOG(ERROR) << extension->id() | |
| 73 << " location is prohibited: " << extension->location(); | |
| 74 return true; | |
| 75 } | |
| 76 | |
| 77 // Return true iff |entry| is contained in |char_array|. | |
| 78 bool ArrayContains(const char* const char_array[], const std::string& entry) { | |
| 79 for (size_t i = 0; char_array[i] != nullptr; ++i) { | |
| 80 if (entry == char_array[i]) { | |
| 81 return true; | |
| 82 } | |
| 83 } | |
| 84 return false; | |
| 85 } | |
| 86 | |
| 87 // Returns true for platform apps that are considered safe for Public Sessions, | |
| 88 // which among other things requires the manifest top-level entries to be | |
| 89 // contained in the |kSafeManifestEntries| whitelist and all permissions to be | |
| 90 // contained in |kSafePermissionStrings| or |kSafePermissionDicts|. Otherwise | |
| 91 // returns false and logs all reasons for failure. | |
| 92 bool IsSafeForSignin(const extensions::Extension* extension) { | |
| 93 bool safe = true; | |
| 94 for (base::DictionaryValue::Iterator entry(*extension->manifest()->value()); | |
| 95 !entry.IsAtEnd(); entry.Advance()) { | |
| 96 // Permissions must be whitelisted in |kSafePermissionStrings|. | |
| 97 if (entry.key() == emk::kPermissions || | |
| 98 entry.key() == emk::kOptionalPermissions) { | |
| 99 const base::ListValue* list_value; | |
| 100 if (!entry.value().GetAsList(&list_value)) { | |
| 101 LOG(ERROR) << extension->id() << ": " << entry.key() | |
| 102 << " is not a list."; | |
| 103 safe = false; | |
| 104 continue; | |
| 105 } | |
| 106 for (auto it = list_value->begin(); it != list_value->end(); ++it) { | |
| 107 // Try to read as dictionary. | |
| 108 const base::DictionaryValue* dict_value; | |
| 109 if ((*it)->GetAsDictionary(&dict_value)) { | |
| 110 if (dict_value->size() != 1) { | |
| 111 LOG(ERROR) << extension->id() | |
| 112 << " has dict in permission list with size " | |
| 113 << dict_value->size() << "."; | |
| 114 safe = false; | |
| 115 continue; | |
| 116 } | |
| 117 for (base::DictionaryValue::Iterator it2(*dict_value); !it2.IsAtEnd(); | |
| 118 it2.Advance()) { | |
| 119 if (!ArrayContains(kSafePermissionDicts, it2.key())) { | |
| 120 LOG(ERROR) << extension->id() | |
| 121 << " has non-whitelisted dict in permission list: " | |
| 122 << it2.key(); | |
| 123 safe = false; | |
| 124 continue; | |
| 125 } | |
| 126 } | |
| 127 continue; | |
| 128 } | |
| 129 // Try to read as string. | |
| 130 std::string permission_string; | |
| 131 if (!(*it)->GetAsString(&permission_string)) { | |
| 132 LOG(ERROR) << extension->id() << ": " << entry.key() | |
| 133 << " contains a token that's neither a string nor a dict."; | |
| 134 safe = false; | |
| 135 continue; | |
| 136 } | |
|
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
| |
| 137 // Accept whitelisted permissions. | |
| 138 if (ArrayContains(kSafePermissionStrings, permission_string)) { | |
| 139 continue; | |
| 140 } | |
| 141 LOG(ERROR) << extension->id() | |
| 142 << " requested non-whitelisted permission: " | |
| 143 << permission_string; | |
| 144 return false; | |
| 145 } | |
| 146 // "app" may only contain "background". | |
| 147 } else if (entry.key() == emk::kApp) { | |
| 148 const base::DictionaryValue* dict_value; | |
| 149 if (!entry.value().GetAsDictionary(&dict_value)) { | |
| 150 LOG(ERROR) << extension->id() << ": app is not a dictionary."; | |
| 151 safe = false; | |
| 152 continue; | |
| 153 } | |
| 154 for (base::DictionaryValue::Iterator it(*dict_value); !it.IsAtEnd(); | |
| 155 it.Advance()) { | |
| 156 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
| |
| 157 LOG(ERROR) << extension->id() | |
| 158 << " has non-whitelisted manifest entry: " << entry.key() | |
| 159 << "." << it.key(); | |
| 160 safe = false; | |
| 161 continue; | |
| 162 } | |
| 163 } | |
| 164 // Require v2 because that's the only version we understand. | |
| 165 } else if (entry.key() == emk::kManifestVersion) { | |
| 166 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.
| |
| 167 if (!entry.value().GetAsInteger(&version)) { | |
| 168 LOG(ERROR) << extension->id() << ": " << emk::kManifestVersion | |
| 169 << " is not an integer."; | |
| 170 safe = false; | |
| 171 continue; | |
| 172 } | |
| 173 if (version != 2) { | |
| 174 LOG(ERROR) << extension->id() | |
| 175 << " has non-whitelisted manifest version."; | |
| 176 safe = false; | |
| 177 continue; | |
| 178 } | |
| 179 } else { | |
| 180 // Everything else is a warning. | |
| 181 LOG(WARNING) << extension->id() | |
| 182 << " has non-whitelisted manifest entry: " << entry.key(); | |
| 183 } | |
| 184 } | |
| 185 return safe; | |
| 186 } | |
| 187 | |
| 188 } // namespace | |
| 189 | |
| 190 SigninScreenPolicyProvider::SigninScreenPolicyProvider() {} | |
| 191 | |
| 192 SigninScreenPolicyProvider::~SigninScreenPolicyProvider() {} | |
| 193 | |
| 194 std::string SigninScreenPolicyProvider::GetDebugPolicyProviderName() const { | |
| 195 #if defined(NDEBUG) | |
| 196 NOTREACHED(); | |
| 197 return std::string(); | |
| 198 #else | |
| 199 return "permissions for sign-in screen"; | |
| 200 #endif | |
| 201 } | |
| 202 | |
| 203 bool SigninScreenPolicyProvider::UserMayLoad( | |
| 204 const extensions::Extension* extension, | |
| 205 base::string16* error) const { | |
| 206 if (!IsApplicableForSigninPolicy(extension)) { | |
| 207 return true; | |
| 208 } | |
| 209 if (ArrayContains(kWhitelistedApps, extension->id())) { | |
| 210 return true; | |
| 211 } | |
| 212 // Allow force-installed platform app if all manifest contents are | |
| 213 // whitelisted. | |
| 214 if (!IsProhibitedForSignin(extension) && IsSafeForSignin(extension)) { | |
| 215 return true; | |
| 216 } | |
| 217 // Disallow all other extensions. | |
| 218 if (error) { | |
| 219 *error = | |
| 220 l10n_util::GetStringFUTF16(IDS_EXTENSION_CANT_INSTALL_ON_LOGIN_SCREEN, | |
| 221 base::UTF8ToUTF16(extension->name()), | |
| 222 base::UTF8ToUTF16(extension->id())); | |
| 223 } | |
| 224 return false; | |
| 225 } | |
| 226 | |
| 227 } // namespace chromeos | |
| OLD | NEW |