1 // Copyright 2014 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.
7 #include "base/macros.h"
8 #include "base/strings/stringprintf.h"
9 #include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
10 #include "chrome/browser/extensions/extension_apitest.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/net/nss_context.h"
13 #include "chrome/browser/net/url_request_mock_util.h"
14 #include "chromeos/chromeos_switches.h"
15 #include "chromeos/login/user_names.h"
16 #include "components/policy/core/browser/browser_policy_connector.h"
17 #include "components/policy/core/common/mock_configuration_policy_provider.h"
18 #include "components/policy/core/common/policy_map.h"
19 #include "content/public/browser/notification_service.h"
20 #include "content/public/common/content_switches.h"
21 #include "content/public/test/test_utils.h"
22 #include "crypto/nss_util_internal.h"
23 #include "crypto/scoped_test_system_nss_key_slot.h"
24 #include "extensions/browser/notification_types.h"
25 #include "net/base/net_errors.h"
26 #include "net/cert/nss_cert_database.h"
27 #include "net/test/url_request/url_request_mock_http_job.h"
28 #include "policy/policy_constants.h"
29 #include "testing/gmock/include/gmock/gmock.h"
33 // The test extension has a certificate referencing this private key which will
34 // be stored in the user's token in the test setup.
36 // openssl genrsa > privkey.pem
37 // openssl pkcs8 -inform pem -in privkey.pem -topk8
38 // -outform der -out privkey8.der -nocrypt
39 // xxd -i privkey8.der
40 const unsigned char privateKeyPkcs8User[] = {
41 0x30, 0x82, 0x01, 0x55, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a,
42 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82,
43 0x01, 0x3f, 0x30, 0x82, 0x01, 0x3b, 0x02, 0x01, 0x00, 0x02, 0x41, 0x00,
44 0xc7, 0xc1, 0x4d, 0xd5, 0xdc, 0x3a, 0x2e, 0x1f, 0x42, 0x30, 0x3d, 0x21,
45 0x1e, 0xa2, 0x1f, 0x60, 0xcb, 0x71, 0x11, 0x53, 0xb0, 0x75, 0xa0, 0x62,
46 0xfe, 0x5e, 0x0a, 0xde, 0xb0, 0x0f, 0x48, 0x97, 0x5e, 0x42, 0xa7, 0x3a,
47 0xd1, 0xca, 0x4c, 0xe3, 0xdb, 0x5f, 0x31, 0xc2, 0x99, 0x08, 0x89, 0xcd,
48 0x6d, 0x20, 0xaa, 0x75, 0xe6, 0x2b, 0x98, 0xd2, 0xf3, 0x7b, 0x4b, 0xe5,
49 0x9b, 0xfe, 0xe2, 0x6d, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x40, 0x4a,
50 0xf5, 0x76, 0x10, 0xe7, 0xb8, 0x89, 0x70, 0x3f, 0x75, 0x3c, 0xab, 0x3e,
51 0x04, 0x96, 0x83, 0xcb, 0x34, 0x1d, 0xcd, 0x6a, 0xed, 0x69, 0x07, 0x5c,
52 0xee, 0xcb, 0x63, 0x6f, 0x6b, 0xfc, 0xcf, 0xee, 0xa2, 0xc4, 0x67, 0x05,
53 0x68, 0x4d, 0x21, 0x7e, 0x3e, 0xde, 0x74, 0x72, 0xf8, 0x04, 0x35, 0x66,
54 0x1e, 0x6b, 0x1d, 0xef, 0x77, 0xf7, 0x33, 0xf0, 0x35, 0xcf, 0x35, 0x6e,
55 0x53, 0x3f, 0x9d, 0x02, 0x21, 0x00, 0xee, 0x48, 0x67, 0x1b, 0x24, 0x6e,
56 0x3d, 0x7b, 0xa0, 0xc3, 0xee, 0x8a, 0x2e, 0xc7, 0xd0, 0xa1, 0xdb, 0x25,
57 0x31, 0x12, 0x99, 0x43, 0x06, 0x3c, 0xb0, 0x80, 0x35, 0x2b, 0xf4, 0xc5,
58 0xa2, 0xd3, 0x02, 0x21, 0x00, 0xd6, 0x9b, 0x8b, 0x75, 0x91, 0x52, 0xd4,
59 0xf0, 0x76, 0xcf, 0xa2, 0xbe, 0xa6, 0xaf, 0x72, 0x6c, 0x52, 0xf9, 0xc9,
60 0x0e, 0xea, 0x4a, 0x4c, 0xd2, 0xdf, 0x25, 0x70, 0xc6, 0x66, 0x35, 0x9d,
61 0xbf, 0x02, 0x21, 0x00, 0xe8, 0x9e, 0x40, 0x21, 0xcc, 0x37, 0xde, 0xc7,
62 0xd1, 0x13, 0x55, 0xcd, 0x0a, 0x8c, 0x40, 0xcd, 0xb1, 0xed, 0xa5, 0xf1,
63 0x7d, 0x33, 0x64, 0x64, 0x5c, 0xfe, 0x5c, 0x6a, 0x34, 0x03, 0xb8, 0xc7,
64 0x02, 0x20, 0x17, 0xe1, 0xb5, 0x52, 0x3e, 0xfa, 0xc5, 0xc1, 0x80, 0xa7,
65 0x38, 0x88, 0x18, 0xca, 0x7b, 0x64, 0x3c, 0x93, 0x99, 0x61, 0x34, 0x87,
66 0x52, 0x27, 0x41, 0x37, 0xcc, 0x65, 0xf7, 0xa7, 0xcd, 0xc7, 0x02, 0x21,
67 0x00, 0x8a, 0x17, 0x7f, 0xf9, 0x45, 0xf3, 0xfd, 0xf7, 0x96, 0x62, 0xf3,
68 0x7a, 0x09, 0xfb, 0xe9, 0x9e, 0xc7, 0x7a, 0x1f, 0x53, 0x1a, 0xb8, 0xd5,
69 0x88, 0x9d, 0xd4, 0x79, 0x57, 0x88, 0x68, 0x72, 0x6f};
71 // The test extension has a certificate referencing this private key which will
72 // be stored in the system token in the test setup.
73 const unsigned char privateKeyPkcs8System[] = {
74 0x30, 0x82, 0x01, 0x54, 0x02, 0x01, 0x00, 0x30, 0x0d, 0x06, 0x09, 0x2a,
75 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82,
76 0x01, 0x3e, 0x30, 0x82, 0x01, 0x3a, 0x02, 0x01, 0x00, 0x02, 0x41, 0x00,
77 0xe8, 0xb3, 0x04, 0xb1, 0xad, 0xef, 0x6b, 0xe5, 0xbe, 0xc9, 0x05, 0x75,
78 0x07, 0x41, 0xf5, 0x70, 0x50, 0xc2, 0xe8, 0xee, 0xeb, 0x09, 0x9d, 0x49,
79 0x64, 0x4c, 0x60, 0x61, 0x80, 0xbe, 0xc5, 0x41, 0xf3, 0x8c, 0x57, 0x90,
80 0x3a, 0x44, 0x62, 0x6d, 0x51, 0xb8, 0xbb, 0xc6, 0x9a, 0x16, 0xdf, 0xf9,
81 0xce, 0xe3, 0xb8, 0x8c, 0x2e, 0xa2, 0x16, 0xc8, 0xed, 0xc7, 0xf8, 0x4f,
82 0xbd, 0xd3, 0x6e, 0x63, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x40, 0x76,
83 0xc9, 0x83, 0xf8, 0xeb, 0xd0, 0x8f, 0xa4, 0xdd, 0x4a, 0xa2, 0xe5, 0x85,
84 0xc9, 0xee, 0xef, 0xe1, 0xda, 0x4d, 0xac, 0x41, 0x01, 0x4c, 0x70, 0x7d,
85 0xa9, 0xdb, 0x7d, 0x8a, 0x8a, 0x58, 0x09, 0x04, 0x45, 0x43, 0xa4, 0xf3,
86 0xb4, 0x98, 0xf6, 0x34, 0x68, 0x5f, 0xc1, 0xc2, 0xa7, 0x86, 0x3e, 0xec,
87 0x84, 0x0b, 0x18, 0xbc, 0xb1, 0xee, 0x6f, 0x3f, 0xb1, 0x6d, 0xbc, 0x3e,
88 0xbf, 0x6d, 0x31, 0x02, 0x21, 0x00, 0xff, 0x9d, 0x90, 0x4f, 0x0e, 0xe8,
89 0x7e, 0xf3, 0x38, 0xa7, 0xec, 0x73, 0x80, 0xf9, 0x39, 0x2c, 0xaa, 0x33,
90 0x91, 0x72, 0x10, 0x7c, 0x8b, 0xc3, 0x61, 0x6d, 0x40, 0x96, 0xac, 0xb3,
91 0x5e, 0xc9, 0x02, 0x21, 0x00, 0xe9, 0x0c, 0xa1, 0x34, 0xf2, 0x43, 0x3c,
92 0x74, 0xec, 0x1a, 0xf6, 0x80, 0x8e, 0x50, 0x10, 0x6d, 0x55, 0x64, 0xce,
93 0x47, 0x4a, 0x1e, 0x34, 0x27, 0x6c, 0x49, 0x79, 0x6a, 0x23, 0xc6, 0x9d,
94 0xcb, 0x02, 0x20, 0x48, 0xda, 0xa8, 0xc1, 0xcf, 0xb6, 0xf6, 0x4f, 0xee,
95 0x4a, 0xf6, 0x3a, 0xa9, 0x7c, 0xdf, 0x0d, 0xda, 0xe8, 0xdd, 0xc0, 0x8b,
96 0xf0, 0x63, 0x89, 0x69, 0x60, 0x51, 0x33, 0x60, 0xbf, 0xb2, 0xf9, 0x02,
97 0x21, 0x00, 0xb4, 0x77, 0x81, 0x46, 0x7c, 0xec, 0x30, 0x1e, 0xe2, 0xcf,
98 0x26, 0x5f, 0xfa, 0xd4, 0x69, 0x44, 0x21, 0x42, 0x84, 0xb2, 0x93, 0xe4,
99 0xbb, 0xc2, 0x63, 0x8a, 0xaa, 0x28, 0xd5, 0x37, 0x72, 0xed, 0x02, 0x20,
100 0x16, 0xde, 0x3d, 0x57, 0xc5, 0xd5, 0x3d, 0x90, 0x8b, 0xfd, 0x90, 0x3b,
101 0xd8, 0x71, 0x69, 0x5e, 0x8d, 0xb4, 0x48, 0x1c, 0xa4, 0x01, 0xce, 0xc1,
102 0xb5, 0x6f, 0xe9, 0x1b, 0x32, 0x91, 0x34, 0x38
105 const base::FilePath::CharType kTestExtensionDir[] =
106 FILE_PATH_LITERAL("extensions/api_test/enterprise_platform_keys");
107 const base::FilePath::CharType kUpdateManifestFileName[] =
108 FILE_PATH_LITERAL("update_manifest.xml");
110 void ImportPrivateKeyPKCS8ToSlot(const unsigned char* pkcs8_der,
111 size_t pkcs8_der_size,
112 PK11SlotInfo* slot) {
113 SECItem pki_der_user = {
115 // NSS requires non-const data even though it is just for input.
116 const_cast<unsigned char*>(pkcs8_der),
119 SECKEYPrivateKey* seckey = NULL;
120 ASSERT_EQ(SECSuccess,
121 PK11_ImportDERPrivateKeyInfoAndReturnKey(slot,
132 // The managed_storage extension has a key defined in its manifest, so that
133 // its extension ID is well-known and the policy system can push policies for
135 const char kTestExtensionID[] = "aecpbnckhoppanpmefllkdkohionpmig";
139 SYSTEM_TOKEN_NOT_EXISTS
143 DEVICE_STATUS_ENROLLED,
144 DEVICE_STATUS_NOT_ENROLLED
147 enum UserAffiliation {
148 USER_AFFILIATION_ENROLLED_DOMAIN,
149 USER_AFFILIATION_UNRELATED
153 Params(SystemToken system_token,
154 DeviceStatus device_status,
155 UserAffiliation user_affiliation)
156 : system_token_(system_token),
157 device_status_(device_status),
158 user_affiliation_(user_affiliation) {}
160 SystemToken system_token_;
161 DeviceStatus device_status_;
162 UserAffiliation user_affiliation_;
165 class EnterprisePlatformKeysTest
166 : public ExtensionApiTest,
167 public ::testing::WithParamInterface<Params> {
169 EnterprisePlatformKeysTest() {}
171 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
172 ExtensionApiTest::SetUpCommandLine(command_line);
174 // Enable the WebCrypto API.
175 command_line->AppendSwitch(
176 switches::kEnableExperimentalWebPlatformFeatures);
178 std::string user_email = "someuser@anydomain.com";
180 // The command line flag kLoginUser determines the user's email and thus
181 // his affiliation to the domain that the device is enrolled to.
182 if (GetParam().user_affiliation_ == USER_AFFILIATION_ENROLLED_DOMAIN)
183 user_email = chromeos::login::kStubUser;
185 command_line->AppendSwitchASCII(chromeos::switches::kLoginUser, user_email);
188 virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
189 ExtensionApiTest::SetUpInProcessBrowserTestFixture();
191 if (GetParam().device_status_ == DEVICE_STATUS_ENROLLED) {
192 device_policy_test_helper_.device_policy()->policy_data().set_username(
193 chromeos::login::kStubUser);
195 device_policy_test_helper_.device_policy()->Build();
196 device_policy_test_helper_.MarkAsEnterpriseOwned();
199 EXPECT_CALL(policy_provider_, IsInitializationComplete(testing::_))
200 .WillRepeatedly(testing::Return(true));
201 policy_provider_.SetAutoRefresh();
202 policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
206 virtual void SetUpOnMainThread() OVERRIDE {
207 if (GetParam().system_token_ == SYSTEM_TOKEN_EXISTS) {
209 content::BrowserThread::PostTask(
210 content::BrowserThread::IO,
212 base::Bind(&EnterprisePlatformKeysTest::SetUpTestSystemSlotOnIO,
213 base::Unretained(this),
214 browser()->profile()->GetResourceContext(),
215 loop.QuitClosure()));
219 ExtensionApiTest::SetUpOnMainThread();
221 // Enable the URLRequestMock, which is required for force-installing the
222 // test extension through policy.
223 content::BrowserThread::PostTask(
224 content::BrowserThread::IO,
226 base::Bind(chrome_browser_net::SetUrlRequestMocksEnabled, true));
230 GetNSSCertDatabaseForProfile(
231 browser()->profile(),
232 base::Bind(&EnterprisePlatformKeysTest::DidGetCertDatabase,
233 base::Unretained(this),
234 loop.QuitClosure()));
241 virtual void TearDownOnMainThread() OVERRIDE {
242 ExtensionApiTest::TearDownOnMainThread();
244 if (GetParam().system_token_ == SYSTEM_TOKEN_EXISTS) {
246 content::BrowserThread::PostTask(
247 content::BrowserThread::IO,
249 base::Bind(&EnterprisePlatformKeysTest::TearDownTestSystemSlotOnIO,
250 base::Unretained(this),
251 loop.QuitClosure()));
257 void DidGetCertDatabase(const base::Closure& done_callback,
258 net::NSSCertDatabase* cert_db) {
259 // In order to use a prepared certificate, import a private key to the
260 // user's token for which the Javscript test will import the certificate.
261 ImportPrivateKeyPKCS8ToSlot(privateKeyPkcs8User,
262 arraysize(privateKeyPkcs8User),
263 cert_db->GetPrivateSlot().get());
267 void SetUpTestSystemSlotOnIO(content::ResourceContext* context,
268 const base::Closure& done_callback) {
269 test_system_slot_.reset(new crypto::ScopedTestSystemNSSKeySlot());
270 ASSERT_TRUE(test_system_slot_->ConstructedSuccessfully());
272 // Import a private key to the system slot. The Javascript part of this
273 // test has a prepared certificate for this key.
274 ImportPrivateKeyPKCS8ToSlot(privateKeyPkcs8System,
275 arraysize(privateKeyPkcs8System),
276 test_system_slot_->slot());
278 content::BrowserThread::PostTask(
279 content::BrowserThread::UI, FROM_HERE, done_callback);
282 void TearDownTestSystemSlotOnIO(const base::Closure& done_callback) {
283 test_system_slot_.reset();
285 content::BrowserThread::PostTask(
286 content::BrowserThread::UI, FROM_HERE, done_callback);
290 // Extensions that are force-installed come from an update URL, which
291 // defaults to the webstore. Use a mock URL for this test with an update
292 // manifest that includes the crx file of the test extension.
293 base::FilePath update_manifest_path =
294 base::FilePath(kTestExtensionDir).Append(kUpdateManifestFileName);
295 GURL update_manifest_url(
296 net::URLRequestMockHTTPJob::GetMockUrl(update_manifest_path));
298 scoped_ptr<base::ListValue> forcelist(new base::ListValue);
299 forcelist->AppendString(base::StringPrintf(
300 "%s;%s", kTestExtensionID, update_manifest_url.spec().c_str()));
302 policy::PolicyMap policy;
303 policy.Set(policy::key::kExtensionInstallForcelist,
304 policy::POLICY_LEVEL_MANDATORY,
305 policy::POLICY_SCOPE_MACHINE,
309 // Set the policy and wait until the extension is installed.
310 content::WindowedNotificationObserver observer(
311 extensions::NOTIFICATION_EXTENSION_WILL_BE_INSTALLED_DEPRECATED,
312 content::NotificationService::AllSources());
313 policy_provider_.UpdateChromePolicy(policy);
317 policy::DevicePolicyCrosTestHelper device_policy_test_helper_;
318 scoped_ptr<crypto::ScopedTestSystemNSSKeySlot> test_system_slot_;
319 policy::MockConfigurationPolicyProvider policy_provider_;
324 IN_PROC_BROWSER_TEST_P(EnterprisePlatformKeysTest, Basic) {
325 // By default, the system token is disabled.
326 std::string system_token_availability = "";
328 // Only if the system token exists, and the current user is of the same domain
329 // as the device is enrolled to, the system token is available to the
331 if (GetParam().system_token_ == SYSTEM_TOKEN_EXISTS &&
332 GetParam().device_status_ == DEVICE_STATUS_ENROLLED &&
333 GetParam().user_affiliation_ == USER_AFFILIATION_ENROLLED_DOMAIN) {
334 system_token_availability = "systemTokenEnabled";
337 ASSERT_TRUE(RunExtensionSubtest(
339 base::StringPrintf("chrome-extension://%s/basic.html?%s",
341 system_token_availability.c_str())))
345 INSTANTIATE_TEST_CASE_P(
346 CheckSystemTokenAvailability,
347 EnterprisePlatformKeysTest,
348 ::testing::Values(Params(SYSTEM_TOKEN_EXISTS,
349 DEVICE_STATUS_ENROLLED,
350 USER_AFFILIATION_ENROLLED_DOMAIN),
351 Params(SYSTEM_TOKEN_EXISTS,
352 DEVICE_STATUS_ENROLLED,
353 USER_AFFILIATION_UNRELATED),
354 Params(SYSTEM_TOKEN_EXISTS,
355 DEVICE_STATUS_NOT_ENROLLED,
356 USER_AFFILIATION_UNRELATED),
357 Params(SYSTEM_TOKEN_NOT_EXISTS,
358 DEVICE_STATUS_ENROLLED,
359 USER_AFFILIATION_ENROLLED_DOMAIN)));
361 class EnterprisePlatformKeysTestNonPolicyInstalledExtension
362 : public EnterprisePlatformKeysTest {};
364 // Ensure that extensions that are not pre-installed by policy throw an install
365 // warning if they request the enterprise.platformKeys permission in the
366 // manifest and that such extensions don't see the
367 // chrome.enterprise.platformKeys namespace.
368 IN_PROC_BROWSER_TEST_F(ExtensionApiTest,
369 EnterprisePlatformKeysIsRestrictedToPolicyExtension) {
370 ASSERT_TRUE(RunExtensionSubtest("enterprise_platform_keys",
371 "api_not_available.html",
372 kFlagIgnoreManifestWarnings));
374 base::FilePath extension_path =
375 test_data_dir_.AppendASCII("enterprise_platform_keys");
376 ExtensionService* service =
377 extensions::ExtensionSystem::Get(profile())->extension_service();
378 const extensions::Extension* extension =
379 GetExtensionByPath(service->extensions(), extension_path);
380 ASSERT_FALSE(extension->install_warnings().empty());
382 "'enterprise.platformKeys' is not allowed for specified install "
384 extension->install_warnings()[0].message);