OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/chromeos/extensions/device_local_account_management_pol
icy_provider.h" | 5 #include "chrome/browser/chromeos/extensions/device_local_account_management_pol
icy_provider.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include <cstddef> | 9 #include <cstddef> |
10 #include <string> | 10 #include <string> |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
117 // Testing extensions: | 117 // Testing extensions: |
118 "ongnjlefhnoajpbodoldndkbkdgfomlp", // Show Managed Storage | 118 "ongnjlefhnoajpbodoldndkbkdgfomlp", // Show Managed Storage |
119 "ilnpadgckeacioehlommkaafedibdeob", // Enterprise DeviceAttributes | 119 "ilnpadgckeacioehlommkaafedibdeob", // Enterprise DeviceAttributes |
120 "oflckobdemeldmjddmlbaiaookhhcngo", // Citrix Receiver QA version | 120 "oflckobdemeldmjddmlbaiaookhhcngo", // Citrix Receiver QA version |
121 "ljacajndfccfgnfohlgkdphmbnpkjflk", // Chrome Remote Desktop (Dev Build) | 121 "ljacajndfccfgnfohlgkdphmbnpkjflk", // Chrome Remote Desktop (Dev Build) |
122 }; | 122 }; |
123 | 123 |
124 // List of manifest entries from https://developer.chrome.com/apps/manifest. | 124 // List of manifest entries from https://developer.chrome.com/apps/manifest. |
125 // Unsafe entries are commented out and special cases too. | 125 // Unsafe entries are commented out and special cases too. |
126 const char* const kSafeManifestEntries[] = { | 126 const char* const kSafeManifestEntries[] = { |
127 // Special-cased in IsPlatformAppSafeForPublicSession(). | 127 emk::kAboutPage, |
| 128 |
| 129 // Special-cased in IsSafeForPublicSession(). |
128 // emk::kApp, | 130 // emk::kApp, |
129 | 131 |
130 // Documented in https://developer.chrome.com/extensions/manifest but not | 132 // Documented in https://developer.chrome.com/extensions/manifest but not |
131 // implemented anywhere. Still, a lot of apps use it. | 133 // implemented anywhere. Still, a lot of apps use it. |
132 "author", | 134 "author", |
133 | 135 |
134 // TBD | 136 // Allows inspection of page contents, not enabled on stable anyways except |
135 // emk::kBluetooth, | 137 // for whitelist. |
| 138 // emk::kAutomation, |
136 | 139 |
137 // TBD | 140 // TODO(isandrk): Ask Mattias for comments on entries without a comment. |
138 // emk::kCommands, | 141 // See crbug.com/650672. |
| 142 "background", |
| 143 |
| 144 emk::kBackgroundPageLegacy, |
| 145 |
| 146 emk::kBackgroundPersistent, |
| 147 |
| 148 emk::kBluetooth, |
| 149 |
| 150 emk::kBrowserAction, |
| 151 |
| 152 // Allows to replace the search provider which is somewhat risky - need to |
| 153 // double check how the search provider policy behaves in PS. |
| 154 // emk::kSettingsOverride, |
| 155 |
| 156 // Custom bookmark managers - I think this is fair game, bookmarks should be |
| 157 // URLs only, and it's restricted to whitelist on stable. |
| 158 emk::kUIOverride, |
| 159 |
| 160 // Bookmark manager, history, new tab - should be safe. |
| 161 emk::kChromeURLOverrides, |
| 162 |
| 163 // General risk of capturing user input, but key combos must include Ctrl or |
| 164 // Alt, so I think this is safe. |
| 165 emk::kCommands, |
| 166 |
| 167 // General risk of capturing user input, but key combos must include Ctrl or |
| 168 // Alt, so I think this is safe. |
| 169 emk::kContentCapabilities, |
| 170 |
| 171 // Access to web content. |
| 172 // emk::kContentScripts, |
| 173 |
| 174 emk::kContentSecurityPolicy, |
| 175 |
| 176 // Access to web content. |
| 177 // emk::kConvertedFromUserScript, |
139 | 178 |
140 // An implementation detail (actually written by Chrome, not the app | 179 // An implementation detail (actually written by Chrome, not the app |
141 // author). | 180 // author). |
142 emk::kCurrentLocale, | 181 emk::kCurrentLocale, |
143 | 182 |
144 // Name of directory containg default strings. | 183 // Name of directory containg default strings. |
145 emk::kDefaultLocale, | 184 emk::kDefaultLocale, |
146 | 185 |
147 // Just a display string. | 186 // Just a display string. |
148 emk::kDescription, | 187 emk::kDescription, |
149 | 188 |
150 // TBD, looks unsafe | 189 // Access to web content. |
| 190 // emk::kDevToolsPage, |
| 191 |
| 192 // Restricted to whitelist already. |
| 193 emk::kDisplayInLauncher, |
| 194 |
| 195 emk::kDisplayInNewTabPage, |
| 196 |
| 197 // This allows to declaratively filter web requests and content, matching on |
| 198 // content data. Doesn't allow direct access to request/content data. Can be |
| 199 // used to brute-force e.g. cookies (reload with filter rules adjusted to |
| 200 // match all possible cookie values) - but that's equivalent to an |
| 201 // off-device brute-force attack. |
| 202 // Looks safe in general with one exception: There's an action that allows |
| 203 // to insert content scripts on matching content. We can't allow this, need |
| 204 // to check whether there's also a host permission required for this case. |
151 // emk::kEventRules, | 205 // emk::kEventRules, |
152 | 206 |
153 // Shared Modules configuration: Allow other extensions to access resources. | 207 // Shared Modules configuration: Allow other extensions to access resources. |
154 emk::kExport, | 208 emk::kExport, |
155 | 209 |
156 // TBD | 210 emk::kExternallyConnectable, |
157 // emk::kExternallyConnectable, | |
158 | 211 |
159 // TBD | 212 emk::kFileBrowserHandlers, |
160 // emk::kFileHandlers, | |
161 | 213 |
162 // TBD | 214 // Extension file handlers are restricted to whitelist which only contains |
163 // emk::kFileSystemProviderCapabilities, | 215 // quickoffice. |
| 216 emk::kFileHandlers, |
| 217 |
| 218 emk::kFileSystemProviderCapabilities, |
| 219 |
| 220 emk::kHomepageURL, |
164 | 221 |
165 // Just UX. | 222 // Just UX. |
166 emk::kIcons, | 223 emk::kIcons, |
167 | 224 |
168 // Shared Modules configuration: Import resources from another extension. | 225 // Shared Modules configuration: Import resources from another extension. |
169 emk::kImport, | 226 emk::kImport, |
170 | 227 |
| 228 emk::kIncognito, |
| 229 |
| 230 // Keylogging. |
| 231 // emk::kInputComponents, |
| 232 |
171 // Shared Modules configuration: Specify extension id for development. | 233 // Shared Modules configuration: Specify extension id for development. |
172 emk::kKey, | 234 emk::kKey, |
173 | 235 |
174 // Descriptive statement about the app. | 236 emk::kKiosk, |
| 237 |
175 emk::kKioskEnabled, | 238 emk::kKioskEnabled, |
176 | 239 |
177 // Contradicts the purpose of running inside a Public Session. | 240 // Not useful since it will prevent app from running, but we don't care. |
178 // emk::kKioskOnly, | 241 emk::kKioskOnly, |
179 | 242 |
180 // Special-cased in IsPlatformAppSafeForPublicSession(). | 243 emk::kKioskRequiredPlatformVersion, |
| 244 |
| 245 // Not useful since it will prevent app from running, but we don't care. |
| 246 emk::kKioskSecondaryApps, |
| 247 |
| 248 // Whitelisted to only allow Google Now. |
| 249 emk::kLauncherPage, |
| 250 |
| 251 // Special-cased in IsSafeForPublicSession(). |
181 // emk::kManifestVersion, | 252 // emk::kManifestVersion, |
182 | 253 |
183 // Descriptive statement about the app. | 254 emk::kMIMETypes, |
| 255 |
| 256 // Whitelisted to only allow browser tests and PDF viewer. |
| 257 emk::kMimeTypesHandler, |
| 258 |
184 emk::kMinimumChromeVersion, | 259 emk::kMinimumChromeVersion, |
185 | 260 |
186 // NaCl modules are bound to app permissions just like the rest of the app | 261 // NaCl modules are bound to app permissions just like the rest of the app |
187 // and thus should not pose a risk. | 262 // and thus should not pose a risk. |
188 emk::kNaClModules, | 263 emk::kNaClModules, |
189 | 264 |
190 // Just a display string. | 265 // Just a display string. |
191 emk::kName, | 266 emk::kName, |
192 | 267 |
193 // TBD, doc missing | 268 // Used in conjunction with the identity API - not really used when there's |
194 // emk::kOAuth2, | 269 // no GAIA user signed in. |
| 270 emk::kOAuth2, |
195 | 271 |
196 // Descriptive statement about the app. | 272 // Generally safe (i.e. only whitelist apps), except for the policy to |
| 273 // whitelist apps for auto-approved token minting (we should just ignore |
| 274 // this in public sessions). Risk is that admin mints OAuth tokens to access |
| 275 // services on behalf of the user silently. |
| 276 // emk::kOAuth2AutoApprove, |
| 277 |
197 emk::kOfflineEnabled, | 278 emk::kOfflineEnabled, |
198 | 279 |
199 // Special-cased in IsPlatformAppSafeForPublicSession(). | 280 // A bit risky as the extensions sees all keystrokes entered into the |
| 281 // omnibox after the search key matches, but generally we deem URLs fair |
| 282 // game. |
| 283 emk::kOmnibox, |
| 284 |
| 285 // Special-cased in IsSafeForPublicSession(). Subject to permission |
| 286 // restrictions. |
200 // emk::kOptionalPermissions, | 287 // emk::kOptionalPermissions, |
201 | 288 |
202 // Special-cased in IsPlatformAppSafeForPublicSession(). | 289 emk::kOptionsPage, |
| 290 |
| 291 emk::kOptionsUI, |
| 292 |
| 293 emk::kPageAction, |
| 294 |
| 295 // Special-cased in IsSafeForPublicSession(). Subject to permission |
| 296 // restrictions. |
203 // emk::kPermissions, | 297 // emk::kPermissions, |
204 | 298 |
205 // No constant in manifest_constants.cc. | 299 // No constant in manifest_constants.cc. Declared as a feature, but unused. |
206 // "platforms", | 300 // "platforms", |
207 | 301 |
208 // Descriptive statement about the app. | 302 // N/A on Chrome OS, so we don't care. |
| 303 emk::kPlugins, |
| 304 |
| 305 // Stated 3D/WebGL/plugin requirements of an app. |
209 emk::kRequirements, | 306 emk::kRequirements, |
210 | 307 |
211 // Execute some pages in a separate sandbox. (Note: Using string literal | 308 // Execute some pages in a separate sandbox. (Note: Using string literal |
212 // since extensions::manifest_keys only has constants for sub-keys.) | 309 // since extensions::manifest_keys only has constants for sub-keys.) |
213 "sandbox", | 310 "sandbox", |
214 | 311 |
215 // Just a display string. | 312 // Just a display string. |
216 emk::kShortName, | 313 emk::kShortName, |
217 | 314 |
218 // TBD, doc missing | 315 // Doc missing. Declared as a feature, but unused. |
219 // emk::kSignature, | 316 // emk::kSignature, |
220 | 317 |
221 // Network access. | 318 // Network access. |
222 emk::kSockets, | 319 emk::kSockets, |
223 | 320 |
224 // TBD. (Note: Using string literal since extensions::manifest_keys only | 321 // Just provides dictionaries, no access to content. |
225 // has constants for sub-keys.) | 322 emk::kSpellcheck, |
226 // "storage", | |
227 | 323 |
228 // TBD, doc missing | 324 // (Note: Using string literal since extensions::manifest_keys only has |
229 // emk::kSystemIndicator, | 325 // constants for sub-keys.) |
| 326 "storage", |
| 327 |
| 328 // Only Hangouts is whitelisted. |
| 329 emk::kSystemIndicator, |
| 330 |
| 331 emk::kTheme, |
| 332 |
| 333 // Might need this for accessibilty, but has content access. Manual |
| 334 // whitelisting might be reasonable here? |
| 335 // emk::kTtsEngine, |
230 | 336 |
231 // TODO(tnagel): Ensure that extension updates query UserMayLoad(). | 337 // TODO(tnagel): Ensure that extension updates query UserMayLoad(). |
232 // https://crbug.com/549720 | 338 // https://crbug.com/549720 |
233 emk::kUpdateURL, | 339 emk::kUpdateURL, |
234 | 340 |
235 // Apps may intercept navigations to URL patterns for domains for which the | 341 // Apps may intercept navigations to URL patterns for domains for which the |
236 // app author has proven ownership of to the Web Store. (Chrome starts the | 342 // app author has proven ownership of to the Web Store. (Chrome starts the |
237 // app instead of fulfilling the navigation.) This is only safe for apps | 343 // app instead of fulfilling the navigation.) This is only safe for apps |
238 // that have been loaded from the Web Store and thus is special-cased in | 344 // that have been loaded from the Web Store and thus is special-cased in |
239 // IsPlatformAppSafeForPublicSession(). | 345 // IsSafeForPublicSession(). |
240 // emk::kUrlHandlers, | 346 // emk::kUrlHandlers, |
241 | 347 |
242 // TBD | 348 emk::kUsbPrinters, |
243 // emk::kUsbPrinters, | |
244 | 349 |
245 // Version string (for app updates). | 350 // Version string (for app updates). |
246 emk::kVersion, | 351 emk::kVersion, |
247 | 352 |
248 // Just a display string. | 353 // Just a display string. |
249 emk::kVersionName, | 354 emk::kVersionName, |
250 | 355 |
| 356 emk::kWebAccessibleResources, |
| 357 |
251 // Webview has no special privileges or capabilities. | 358 // Webview has no special privileges or capabilities. |
252 emk::kWebview, | 359 emk::kWebview, |
253 }; | 360 }; |
254 | 361 |
255 // List of permission strings based on [1] and [2]. See |kSafePermissionDicts| | 362 // List of permission strings based on [1] and [2]. See |kSafePermissionDicts| |
256 // for permission dicts. Since Public Session users may be fully unaware of any | 363 // for permission dicts. Since Public Session users may be fully unaware of any |
257 // apps being installed, their consent to access any kind of sensitive | 364 // apps being installed, their consent to access any kind of sensitive |
258 // information cannot be assumed. Therefore only APIs are whitelisted which | 365 // information cannot be assumed. Therefore only APIs are whitelisted which |
259 // should not leak sensitive data to the caller. Since the privacy boundary is | 366 // should not leak sensitive data to the caller. Since the privacy boundary is |
260 // drawn at the API level, no safeguards are required to prevent exfiltration | 367 // drawn at the API level, no safeguards are required to prevent exfiltration |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
398 // Some permissions take the form of a dictionary. See |kSafePermissionStrings| | 505 // Some permissions take the form of a dictionary. See |kSafePermissionStrings| |
399 // for permission strings (and for more documentation). | 506 // for permission strings (and for more documentation). |
400 const char* const kSafePermissionDicts[] = { | 507 const char* const kSafePermissionDicts[] = { |
401 // TBD | 508 // TBD |
402 // "fileSystem", | 509 // "fileSystem", |
403 | 510 |
404 // Just another type of connectivity. | 511 // Just another type of connectivity. |
405 "socket", | 512 "socket", |
406 }; | 513 }; |
407 | 514 |
| 515 // List of safe entries for the "app" dict in manifest. |
| 516 const char* const kSafeAppStrings[] = { |
| 517 "background", |
| 518 "content_security_policy", |
| 519 "icon_color", |
| 520 "isolation", |
| 521 "launch", |
| 522 "linked_icons", |
| 523 }; |
| 524 |
408 // Return true iff |entry| is contained in |char_array|. | 525 // Return true iff |entry| is contained in |char_array|. |
409 bool ArrayContainsImpl(const char* const char_array[], | 526 bool ArrayContainsImpl(const char* const char_array[], |
410 size_t entry_count, | 527 size_t entry_count, |
411 const std::string& entry) { | 528 const std::string& entry) { |
412 for (size_t i = 0; i < entry_count; ++i) { | 529 for (size_t i = 0; i < entry_count; ++i) { |
413 if (entry == char_array[i]) { | 530 if (entry == char_array[i]) { |
414 return true; | 531 return true; |
415 } | 532 } |
416 } | 533 } |
417 return false; | 534 return false; |
418 } | 535 } |
419 | 536 |
420 // See http://blogs.msdn.com/b/the1/archive/2004/05/07/128242.aspx for an | 537 // See http://blogs.msdn.com/b/the1/archive/2004/05/07/128242.aspx for an |
421 // explanation of array size determination. | 538 // explanation of array size determination. |
422 template <size_t N> | 539 template <size_t N> |
423 bool ArrayContains(const char* const (&char_array)[N], | 540 bool ArrayContains(const char* const (&char_array)[N], |
424 const std::string& entry) { | 541 const std::string& entry) { |
425 return ArrayContainsImpl(char_array, N, entry); | 542 return ArrayContainsImpl(char_array, N, entry); |
426 } | 543 } |
427 | 544 |
428 // Returns true for platform apps that are considered safe for Public Sessions, | 545 // Returns true for extensions that are considered safe for Public Sessions, |
429 // which among other things requires the manifest top-level entries to be | 546 // which among other things requires the manifest top-level entries to be |
430 // contained in the |kSafeManifestEntries| whitelist and all permissions to be | 547 // contained in the |kSafeManifestEntries| whitelist and all permissions to be |
431 // contained in |kSafePermissionStrings| or |kSafePermissionDicts|. Otherwise | 548 // contained in |kSafePermissionStrings| or |kSafePermissionDicts|. Otherwise |
432 // returns false and logs all reasons for failure. | 549 // returns false and logs all reasons for failure. |
433 bool IsPlatformAppSafeForPublicSession(const extensions::Extension* extension) { | 550 bool IsSafeForPublicSession(const extensions::Extension* extension) { |
434 bool safe = true; | 551 bool safe = true; |
435 if (extension->GetType() != extensions::Manifest::TYPE_PLATFORM_APP) { | 552 if (!extension->is_extension() && |
436 LOG(ERROR) << extension->id() << " is not a platform app."; | 553 !extension->is_hosted_app() && |
| 554 !extension->is_platform_app() && |
| 555 !extension->is_shared_module() && |
| 556 !extension->is_theme()) { |
| 557 LOG(ERROR) << extension->id() |
| 558 << " is not of a supported type. Extension type: " |
| 559 << extension->GetType(); |
437 safe = false; | 560 safe = false; |
438 } | 561 } |
439 | 562 |
440 for (base::DictionaryValue::Iterator it(*extension->manifest()->value()); | 563 for (base::DictionaryValue::Iterator it(*extension->manifest()->value()); |
441 !it.IsAtEnd(); it.Advance()) { | 564 !it.IsAtEnd(); it.Advance()) { |
442 if (ArrayContains(kSafeManifestEntries, it.key())) { | 565 if (ArrayContains(kSafeManifestEntries, it.key())) { |
443 continue; | 566 continue; |
444 } | 567 } |
445 | 568 |
446 // Permissions must be whitelisted in |kSafePermissionStrings| or | 569 // Permissions must be whitelisted in |kSafePermissionStrings| or |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
496 base::CompareCase::SENSITIVE) || | 619 base::CompareCase::SENSITIVE) || |
497 base::StartsWith(permission_string, "ftp://", | 620 base::StartsWith(permission_string, "ftp://", |
498 base::CompareCase::SENSITIVE)) { | 621 base::CompareCase::SENSITIVE)) { |
499 continue; | 622 continue; |
500 } | 623 } |
501 LOG(ERROR) << extension->id() | 624 LOG(ERROR) << extension->id() |
502 << " requested non-whitelisted permission: " | 625 << " requested non-whitelisted permission: " |
503 << permission_string; | 626 << permission_string; |
504 safe = false; | 627 safe = false; |
505 } | 628 } |
506 // "app" may only contain "background". | |
507 } else if (it.key() == emk::kApp) { | 629 } else if (it.key() == emk::kApp) { |
| 630 if (!extension->is_hosted_app() && |
| 631 !extension->is_platform_app()) { |
| 632 LOG(ERROR) << extension->id() |
| 633 << ": app manifest entry is allowed only for hosted_app or " |
| 634 "platform_app extension type. Current extension type: " |
| 635 << extension->GetType(); |
| 636 safe = false; |
| 637 } |
508 const base::DictionaryValue *dict_value; | 638 const base::DictionaryValue *dict_value; |
509 if (!it.value().GetAsDictionary(&dict_value)) { | 639 if (!it.value().GetAsDictionary(&dict_value)) { |
510 LOG(ERROR) << extension->id() << ": app is not a dictionary."; | 640 LOG(ERROR) << extension->id() << ": app is not a dictionary."; |
511 safe = false; | 641 safe = false; |
512 continue; | 642 continue; |
513 } | 643 } |
514 for (base::DictionaryValue::Iterator it2(*dict_value); | 644 for (base::DictionaryValue::Iterator it2(*dict_value); |
515 !it2.IsAtEnd(); it2.Advance()) { | 645 !it2.IsAtEnd(); it2.Advance()) { |
516 if (it2.key() != "background") { | 646 if (!ArrayContains(kSafeAppStrings, it2.key())) { |
517 LOG(ERROR) << extension->id() | 647 LOG(ERROR) << extension->id() |
518 << " has non-whitelisted manifest entry: " | 648 << " has non-whitelisted manifest entry: " |
519 << it.key() << "." << it2.key(); | 649 << it.key() << "." << it2.key(); |
520 safe = false; | 650 safe = false; |
521 continue; | 651 continue; |
522 } | 652 } |
523 } | 653 } |
524 // Require v2 because that's the only version we understand. | 654 // Require v2 because that's the only version we understand. |
525 } else if (it.key() == emk::kManifestVersion) { | 655 } else if (it.key() == emk::kManifestVersion) { |
526 int version; | 656 int version; |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
590 if (extension->GetType() == extensions::Manifest::TYPE_HOSTED_APP) { | 720 if (extension->GetType() == extensions::Manifest::TYPE_HOSTED_APP) { |
591 return true; | 721 return true; |
592 } | 722 } |
593 | 723 |
594 // Allow extension if its specific ID is whitelisted for use in public | 724 // Allow extension if its specific ID is whitelisted for use in public |
595 // sessions. | 725 // sessions. |
596 if (ArrayContains(kPublicSessionWhitelist, extension->id())) { | 726 if (ArrayContains(kPublicSessionWhitelist, extension->id())) { |
597 return true; | 727 return true; |
598 } | 728 } |
599 | 729 |
600 // Allow force-installed platform app if all manifest contents are | 730 // Allow force-installed extension if all manifest contents are whitelisted. |
601 // whitelisted. | |
602 if ((extension->location() == extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD | 731 if ((extension->location() == extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD |
603 || extension->location() == extensions::Manifest::EXTERNAL_POLICY) | 732 || extension->location() == extensions::Manifest::EXTERNAL_POLICY) |
604 && IsPlatformAppSafeForPublicSession(extension)) { | 733 && IsSafeForPublicSession(extension)) { |
605 return true; | 734 return true; |
606 } | 735 } |
607 } else if (account_type_ == policy::DeviceLocalAccount::TYPE_KIOSK_APP) { | 736 } else if (account_type_ == policy::DeviceLocalAccount::TYPE_KIOSK_APP) { |
608 // For single-app kiosk sessions, allow platform apps, extesions and shared | 737 // For single-app kiosk sessions, allow platform apps, extesions and shared |
609 // modules. | 738 // modules. |
610 if (extension->GetType() == extensions::Manifest::TYPE_PLATFORM_APP || | 739 if (extension->GetType() == extensions::Manifest::TYPE_PLATFORM_APP || |
611 extension->GetType() == extensions::Manifest::TYPE_SHARED_MODULE || | 740 extension->GetType() == extensions::Manifest::TYPE_SHARED_MODULE || |
612 extension->GetType() == extensions::Manifest::TYPE_EXTENSION) { | 741 extension->GetType() == extensions::Manifest::TYPE_EXTENSION) { |
613 return true; | 742 return true; |
614 } | 743 } |
615 } | 744 } |
616 | 745 |
617 // Disallow all other extensions. | 746 // Disallow all other extensions. |
618 if (error) { | 747 if (error) { |
619 *error = l10n_util::GetStringFUTF16( | 748 *error = l10n_util::GetStringFUTF16( |
620 IDS_EXTENSION_CANT_INSTALL_IN_DEVICE_LOCAL_ACCOUNT, | 749 IDS_EXTENSION_CANT_INSTALL_IN_DEVICE_LOCAL_ACCOUNT, |
621 base::UTF8ToUTF16(extension->name()), | 750 base::UTF8ToUTF16(extension->name()), |
622 base::UTF8ToUTF16(extension->id())); | 751 base::UTF8ToUTF16(extension->id())); |
623 } | 752 } |
624 return false; | 753 return false; |
625 } | 754 } |
626 | 755 |
627 } // namespace chromeos | 756 } // namespace chromeos |
OLD | NEW |