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