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[]{ | |
|
emaxx
2016/07/26 02:55:31
nit: Please use some consistent style, either " =
Denis Kuznetsov (DE-MUC)
2016/08/29 15:14:40
Gone.
| |
| 29 "kmendfapggjehodndflmmgagdbamhnfd", // Gnubby component extension. | |
|
emaxx
2016/07/26 02:55:31
Is this needed? The code below seems to allow the
Denis Kuznetsov (DE-MUC)
2016/08/29 15:14:40
Gone.
| |
| 30 "beknehfpfkghjoafdifaflglpjkojoco", // Gnubby app | |
|
emaxx
2016/07/26 02:55:31
Is it confirmed that this app is normally installe
Denis Kuznetsov (DE-MUC)
2016/08/29 15:14:40
Gone.
| |
| 31 "ljoammodoonkhnehlncldjelhidljdpi", // Genius app (help) | |
|
emaxx
2016/07/26 02:55:31
This and the next one also seem to be the componen
Denis Kuznetsov (DE-MUC)
2016/08/29 15:14:40
Gone.
But we explicitly block all component extens
emaxx
2016/08/30 13:01:41
Isn't it possible to allow all component extension
| |
| 32 "mppnpdlheglhdfmldimlhpnegondlapf", // Virtual Keyboard | |
| 33 nullptr // End of list | |
| 34 }; | |
| 35 | |
| 36 const char* const kSafePermissionStrings[] = { | |
| 37 "usb", | |
|
emaxx
2016/07/26 02:55:31
nit: Please sort the list lexicographically.
Denis Kuznetsov (DE-MUC)
2016/08/29 15:14:40
Gone.
| |
| 38 "certificateProvider", | |
| 39 "storage", | |
| 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). | |
|
emaxx
2016/07/26 02:55:32
nit: s/ (and for more documentation)//
(there's no
Denis Kuznetsov (DE-MUC)
2016/08/29 15:14:40
Here and below : Gone.
| |
| 45 const char* const kSafePermissionDicts[] = { | |
| 46 // No dictionary permissions are allowed at the moment. | |
|
emaxx
2016/07/26 02:55:31
Actually, we already need "usbDevices" dictionary
Denis Kuznetsov (DE-MUC)
2016/08/29 15:14:40
Acknowledged.
| |
| 47 nullptr // End of list | |
| 48 }; | |
| 49 | |
| 50 // Checks if this provider applies to given extension. | |
| 51 bool IsApplicableForSigninPolicy(const extensions::Extension* extension) { | |
| 52 if (extension->location() == extensions::Manifest::COMPONENT) { | |
|
emaxx
2016/07/26 02:55:31
Shouldn't EXTERNAL_COMPONENT be also specified her
Denis Kuznetsov (DE-MUC)
2016/08/29 15:14:40
Acknowledged.
| |
| 53 // Do not apply to component extensions. | |
| 54 return false; | |
| 55 } | |
| 56 return true; | |
|
emaxx
2016/07/26 02:55:31
Not sure, but it probably also makes sense to alwa
Denis Kuznetsov (DE-MUC)
2016/08/29 15:14:40
Acknowledged.
| |
| 57 } | |
| 58 | |
| 59 // Checks if this provider applies to given extension. | |
|
emaxx
2016/07/26 02:55:31
nit: Please fix the comment.
Denis Kuznetsov (DE-MUC)
2016/08/29 15:14:40
Acknowledged.
| |
| 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() | |
| 65 << " is not an application: " << extension->GetType(); | |
| 66 return true; | |
| 67 } | |
| 68 if (extension->location() == extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD || | |
|
emaxx
2016/07/26 02:55:31
BTW, there's a function Manifest::IsPolicyLocation
emaxx
2016/07/26 02:55:31
IMO, the logic would be simpler if you would negat
| |
| 69 extension->location() == extensions::Manifest::EXTERNAL_POLICY) { | |
| 70 return false; | |
| 71 } | |
| 72 LOG(ERROR) << extension->id() | |
| 73 << " location is prohibited: " << extension->location(); | |
|
emaxx
2016/07/26 02:55:32
I haven't found operator<< for Manifest::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) { | |
|
emaxx
2016/07/26 02:55:31
You may consider passing the array as a reference
| |
| 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, | |
|
emaxx
2016/07/26 02:55:31
nit: s/Public Sessions/the login screen/ (or signi
| |
| 88 // which among other things requires the manifest top-level entries to be | |
| 89 // contained in the |kSafeManifestEntries| whitelist and all permissions to be | |
|
emaxx
2016/07/26 02:55:31
nit: Remove references to kSafeManifestEntries.
| |
| 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 } | |
| 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; | |
|
emaxx
2016/07/26 02:55:32
Shouldn't this be "safe = false", as in other simi
| |
| 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") { | |
|
emaxx
2016/07/26 02:55:32
This check would fail for the current versions of
| |
| 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 if (extension->manifest()->GetManifestVersion() != 2) { | |
| 167 LOG(ERROR) << extension->id() | |
| 168 << " has non-whitelisted manifest version."; | |
| 169 safe = false; | |
| 170 continue; | |
| 171 } | |
| 172 } else { | |
| 173 // Everything else is a warning. | |
|
emaxx
2016/07/26 02:55:31
Looking at our examples of the login screen apps,
| |
| 174 LOG(WARNING) << extension->id() | |
| 175 << " has non-whitelisted manifest entry: " << entry.key(); | |
| 176 } | |
| 177 } | |
| 178 return safe; | |
| 179 } | |
| 180 | |
| 181 } // namespace | |
| 182 | |
| 183 SigninScreenPolicyProvider::SigninScreenPolicyProvider() {} | |
| 184 | |
| 185 SigninScreenPolicyProvider::~SigninScreenPolicyProvider() {} | |
| 186 | |
| 187 std::string SigninScreenPolicyProvider::GetDebugPolicyProviderName() const { | |
| 188 #if defined(NDEBUG) | |
| 189 NOTREACHED(); | |
| 190 return std::string(); | |
| 191 #else | |
| 192 return "permissions for sign-in screen"; | |
| 193 #endif | |
| 194 } | |
| 195 | |
| 196 bool SigninScreenPolicyProvider::UserMayLoad( | |
| 197 const extensions::Extension* extension, | |
| 198 base::string16* error) const { | |
| 199 if (!IsApplicableForSigninPolicy(extension)) { | |
| 200 return true; | |
| 201 } | |
| 202 if (ArrayContains(kWhitelistedApps, extension->id())) { | |
| 203 return true; | |
| 204 } | |
| 205 // Allow force-installed platform app if all manifest contents are | |
| 206 // whitelisted. | |
| 207 if (!IsProhibitedForSignin(extension) && IsSafeForSignin(extension)) { | |
| 208 return true; | |
| 209 } | |
| 210 // Disallow all other extensions. | |
| 211 if (error) { | |
| 212 *error = | |
| 213 l10n_util::GetStringFUTF16(IDS_EXTENSION_CANT_INSTALL_ON_LOGIN_SCREEN, | |
| 214 base::UTF8ToUTF16(extension->name()), | |
| 215 base::UTF8ToUTF16(extension->id())); | |
| 216 } | |
| 217 return false; | |
| 218 } | |
| 219 | |
| 220 } // namespace chromeos | |
| OLD | NEW |