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. |
Andrew T Wilson (Slow)
2016/09/27 15:15:07
Please file a bug for this and put the bug number
Ivan Šandrk
2016/09/27 15:28:19
Done.
| |
138 // emk::kCommands, | 141 "background", |
142 | |
143 emk::kBackgroundPageLegacy, | |
144 | |
145 emk::kBackgroundPersistent, | |
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, | |
139 | 177 |
140 // An implementation detail (actually written by Chrome, not the app | 178 // An implementation detail (actually written by Chrome, not the app |
141 // author). | 179 // author). |
142 emk::kCurrentLocale, | 180 emk::kCurrentLocale, |
143 | 181 |
144 // Name of directory containg default strings. | 182 // Name of directory containg default strings. |
145 emk::kDefaultLocale, | 183 emk::kDefaultLocale, |
146 | 184 |
147 // Just a display string. | 185 // Just a display string. |
148 emk::kDescription, | 186 emk::kDescription, |
149 | 187 |
150 // TBD, looks unsafe | 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. | |
151 // emk::kEventRules, | 204 // emk::kEventRules, |
152 | 205 |
153 // Shared Modules configuration: Allow other extensions to access resources. | 206 // Shared Modules configuration: Allow other extensions to access resources. |
154 emk::kExport, | 207 emk::kExport, |
155 | 208 |
156 // TBD | 209 emk::kExternallyConnectable, |
157 // emk::kExternallyConnectable, | |
158 | 210 |
159 // TBD | 211 emk::kFileBrowserHandlers, |
160 // emk::kFileHandlers, | |
161 | 212 |
162 // TBD | 213 // Extension file handlers are restricted to whitelist which only contains |
163 // emk::kFileSystemProviderCapabilities, | 214 // quickoffice. |
215 emk::kFileHandlers, | |
216 | |
217 emk::kFileSystemProviderCapabilities, | |
218 | |
219 emk::kHomepageURL, | |
164 | 220 |
165 // Just UX. | 221 // Just UX. |
166 emk::kIcons, | 222 emk::kIcons, |
167 | 223 |
168 // Shared Modules configuration: Import resources from another extension. | 224 // Shared Modules configuration: Import resources from another extension. |
169 emk::kImport, | 225 emk::kImport, |
170 | 226 |
227 emk::kIncognito, | |
228 | |
229 // Keylogging. | |
230 // emk::kInputComponents, | |
231 | |
171 // Shared Modules configuration: Specify extension id for development. | 232 // Shared Modules configuration: Specify extension id for development. |
172 emk::kKey, | 233 emk::kKey, |
173 | 234 |
174 // Descriptive statement about the app. | 235 emk::kKiosk, |
236 | |
175 emk::kKioskEnabled, | 237 emk::kKioskEnabled, |
176 | 238 |
177 // Contradicts the purpose of running inside a Public Session. | 239 // Not useful since it will prevent app from running, but we don't care. |
178 // emk::kKioskOnly, | 240 emk::kKioskOnly, |
179 | 241 |
180 // Special-cased in IsPlatformAppSafeForPublicSession(). | 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(). | |
181 // emk::kManifestVersion, | 251 // emk::kManifestVersion, |
182 | 252 |
183 // Descriptive statement about the app. | 253 emk::kMIMETypes, |
254 | |
255 // Whitelisted to only allow browser tests and PDF viewer. | |
256 emk::kMimeTypesHandler, | |
257 | |
184 emk::kMinimumChromeVersion, | 258 emk::kMinimumChromeVersion, |
185 | 259 |
186 // 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 |
187 // and thus should not pose a risk. | 261 // and thus should not pose a risk. |
188 emk::kNaClModules, | 262 emk::kNaClModules, |
189 | 263 |
190 // Just a display string. | 264 // Just a display string. |
191 emk::kName, | 265 emk::kName, |
192 | 266 |
193 // TBD, doc missing | 267 // Used in conjunction with the identity API - not really used when there's |
194 // emk::kOAuth2, | 268 // no GAIA user signed in. |
269 emk::kOAuth2, | |
195 | 270 |
196 // Descriptive statement about the app. | 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 | |
197 emk::kOfflineEnabled, | 277 emk::kOfflineEnabled, |
198 | 278 |
199 // 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. | |
200 // emk::kOptionalPermissions, | 286 // emk::kOptionalPermissions, |
201 | 287 |
202 // 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. | |
203 // emk::kPermissions, | 296 // emk::kPermissions, |
204 | 297 |
205 // No constant in manifest_constants.cc. | 298 // No constant in manifest_constants.cc. Declared as a feature, but unused. |
206 // "platforms", | 299 // "platforms", |
207 | 300 |
208 // 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. | |
209 emk::kRequirements, | 305 emk::kRequirements, |
210 | 306 |
211 // Execute some pages in a separate sandbox. (Note: Using string literal | 307 // Execute some pages in a separate sandbox. (Note: Using string literal |
212 // since extensions::manifest_keys only has constants for sub-keys.) | 308 // since extensions::manifest_keys only has constants for sub-keys.) |
213 "sandbox", | 309 "sandbox", |
214 | 310 |
215 // Just a display string. | 311 // Just a display string. |
216 emk::kShortName, | 312 emk::kShortName, |
217 | 313 |
218 // TBD, doc missing | 314 // Doc missing. Declared as a feature, but unused. |
219 // emk::kSignature, | 315 // emk::kSignature, |
220 | 316 |
221 // Network access. | 317 // Network access. |
222 emk::kSockets, | 318 emk::kSockets, |
223 | 319 |
224 // TBD. (Note: Using string literal since extensions::manifest_keys only | 320 // Just provides dictionaries, no access to content. |
225 // has constants for sub-keys.) | 321 emk::kSpellcheck, |
226 // "storage", | |
227 | 322 |
228 // TBD, doc missing | 323 // (Note: Using string literal since extensions::manifest_keys only has |
229 // emk::kSystemIndicator, | 324 // constants for sub-keys.) |
325 "storage", | |
326 | |
327 // Only Hangouts is whitelisted. | |
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, | |
230 | 335 |
231 // TODO(tnagel): Ensure that extension updates query UserMayLoad(). | 336 // TODO(tnagel): Ensure that extension updates query UserMayLoad(). |
232 // https://crbug.com/549720 | 337 // https://crbug.com/549720 |
233 emk::kUpdateURL, | 338 emk::kUpdateURL, |
234 | 339 |
235 // 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 |
236 // 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 |
237 // 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 |
238 // 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 |
239 // IsPlatformAppSafeForPublicSession(). | 344 // IsSafeForPublicSession(). |
240 // emk::kUrlHandlers, | 345 // emk::kUrlHandlers, |
241 | 346 |
242 // TBD | 347 emk::kUsbPrinters, |
243 // emk::kUsbPrinters, | |
244 | 348 |
245 // Version string (for app updates). | 349 // Version string (for app updates). |
246 emk::kVersion, | 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 safe entries for the "app" dict in manifest. | |
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() | |
557 << " is not of a supported type. Extension type: " | |
558 << extension->GetType(); | |
437 safe = false; | 559 safe = false; |
438 } | 560 } |
439 | 561 |
440 for (base::DictionaryValue::Iterator it(*extension->manifest()->value()); | 562 for (base::DictionaryValue::Iterator it(*extension->manifest()->value()); |
441 !it.IsAtEnd(); it.Advance()) { | 563 !it.IsAtEnd(); it.Advance()) { |
442 if (ArrayContains(kSafeManifestEntries, it.key())) { | 564 if (ArrayContains(kSafeManifestEntries, it.key())) { |
443 continue; | 565 continue; |
444 } | 566 } |
445 | 567 |
446 // Permissions must be whitelisted in |kSafePermissionStrings| or | 568 // Permissions must be whitelisted in |kSafePermissionStrings| or |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
496 base::CompareCase::SENSITIVE) || | 618 base::CompareCase::SENSITIVE) || |
497 base::StartsWith(permission_string, "ftp://", | 619 base::StartsWith(permission_string, "ftp://", |
498 base::CompareCase::SENSITIVE)) { | 620 base::CompareCase::SENSITIVE)) { |
499 continue; | 621 continue; |
500 } | 622 } |
501 LOG(ERROR) << extension->id() | 623 LOG(ERROR) << extension->id() |
502 << " requested non-whitelisted permission: " | 624 << " requested non-whitelisted permission: " |
503 << permission_string; | 625 << permission_string; |
504 safe = false; | 626 safe = false; |
505 } | 627 } |
506 // "app" may only contain "background". | |
507 } else if (it.key() == emk::kApp) { | 628 } else if (it.key() == emk::kApp) { |
629 if (!extension->is_hosted_app() && | |
630 !extension->is_platform_app()) { | |
631 LOG(ERROR) << extension->id() | |
632 << ": app manifest entry is allowed only for hosted_app or " | |
633 "platform_app extension type. Current extension type: " | |
634 << extension->GetType(); | |
635 safe = false; | |
636 } | |
508 const base::DictionaryValue *dict_value; | 637 const base::DictionaryValue *dict_value; |
509 if (!it.value().GetAsDictionary(&dict_value)) { | 638 if (!it.value().GetAsDictionary(&dict_value)) { |
510 LOG(ERROR) << extension->id() << ": app is not a dictionary."; | 639 LOG(ERROR) << extension->id() << ": app is not a dictionary."; |
511 safe = false; | 640 safe = false; |
512 continue; | 641 continue; |
513 } | 642 } |
514 for (base::DictionaryValue::Iterator it2(*dict_value); | 643 for (base::DictionaryValue::Iterator it2(*dict_value); |
515 !it2.IsAtEnd(); it2.Advance()) { | 644 !it2.IsAtEnd(); it2.Advance()) { |
516 if (it2.key() != "background") { | 645 if (!ArrayContains(kSafeAppStrings, it2.key())) { |
517 LOG(ERROR) << extension->id() | 646 LOG(ERROR) << extension->id() |
518 << " has non-whitelisted manifest entry: " | 647 << " has non-whitelisted manifest entry: " |
519 << it.key() << "." << it2.key(); | 648 << it.key() << "." << it2.key(); |
520 safe = false; | 649 safe = false; |
521 continue; | 650 continue; |
522 } | 651 } |
523 } | 652 } |
524 // Require v2 because that's the only version we understand. | 653 // Require v2 because that's the only version we understand. |
525 } else if (it.key() == emk::kManifestVersion) { | 654 } else if (it.key() == emk::kManifestVersion) { |
526 int version; | 655 int version; |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
590 if (extension->GetType() == extensions::Manifest::TYPE_HOSTED_APP) { | 719 if (extension->GetType() == extensions::Manifest::TYPE_HOSTED_APP) { |
591 return true; | 720 return true; |
592 } | 721 } |
593 | 722 |
594 // Allow extension if its specific ID is whitelisted for use in public | 723 // Allow extension if its specific ID is whitelisted for use in public |
595 // sessions. | 724 // sessions. |
596 if (ArrayContains(kPublicSessionWhitelist, extension->id())) { | 725 if (ArrayContains(kPublicSessionWhitelist, extension->id())) { |
597 return true; | 726 return true; |
598 } | 727 } |
599 | 728 |
600 // Allow force-installed platform app if all manifest contents are | 729 // Allow force-installed extension if all manifest contents are whitelisted. |
601 // whitelisted. | |
602 if ((extension->location() == extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD | 730 if ((extension->location() == extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD |
603 || extension->location() == extensions::Manifest::EXTERNAL_POLICY) | 731 || extension->location() == extensions::Manifest::EXTERNAL_POLICY) |
604 && IsPlatformAppSafeForPublicSession(extension)) { | 732 && IsSafeForPublicSession(extension)) { |
605 return true; | 733 return true; |
606 } | 734 } |
607 } else if (account_type_ == policy::DeviceLocalAccount::TYPE_KIOSK_APP) { | 735 } else if (account_type_ == policy::DeviceLocalAccount::TYPE_KIOSK_APP) { |
608 // For single-app kiosk sessions, allow platform apps, extesions and shared | 736 // For single-app kiosk sessions, allow platform apps, extesions and shared |
609 // modules. | 737 // modules. |
610 if (extension->GetType() == extensions::Manifest::TYPE_PLATFORM_APP || | 738 if (extension->GetType() == extensions::Manifest::TYPE_PLATFORM_APP || |
611 extension->GetType() == extensions::Manifest::TYPE_SHARED_MODULE || | 739 extension->GetType() == extensions::Manifest::TYPE_SHARED_MODULE || |
612 extension->GetType() == extensions::Manifest::TYPE_EXTENSION) { | 740 extension->GetType() == extensions::Manifest::TYPE_EXTENSION) { |
613 return true; | 741 return true; |
614 } | 742 } |
615 } | 743 } |
616 | 744 |
617 // Disallow all other extensions. | 745 // Disallow all other extensions. |
618 if (error) { | 746 if (error) { |
619 *error = l10n_util::GetStringFUTF16( | 747 *error = l10n_util::GetStringFUTF16( |
620 IDS_EXTENSION_CANT_INSTALL_IN_DEVICE_LOCAL_ACCOUNT, | 748 IDS_EXTENSION_CANT_INSTALL_IN_DEVICE_LOCAL_ACCOUNT, |
621 base::UTF8ToUTF16(extension->name()), | 749 base::UTF8ToUTF16(extension->name()), |
622 base::UTF8ToUTF16(extension->id())); | 750 base::UTF8ToUTF16(extension->id())); |
623 } | 751 } |
624 return false; | 752 return false; |
625 } | 753 } |
626 | 754 |
627 } // namespace chromeos | 755 } // namespace chromeos |
OLD | NEW |