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.
4
5 #include <cryptohi.h>
6 #include <pk11pub.h>
7
8 #include <memory>
9 #include <utility>
10
11 #include "base/bind.h"
12 #include "base/macros.h"
13 #include "base/run_loop.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/task/post_task.h"
16 #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h"
17 #include "chrome/browser/chromeos/platform_keys/extension_platform_keys_service_factory.h"
18 #include "chrome/browser/extensions/api/platform_keys/platform_keys_test_base.h"
19 #include "chrome/browser/net/nss_context.h"
20 #include "chrome/browser/policy/profile_policy_connector.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "components/policy/policy_constants.h"
23 #include "content/public/browser/browser_task_traits.h"
24 #include "crypto/nss_util_internal.h"
25 #include "crypto/scoped_nss_types.h"
26 #include "crypto/scoped_test_nss_db.h"
27 #include "crypto/scoped_test_system_nss_key_slot.h"
28 #include "net/cert/nss_cert_database.h"
29 #include "net/cert/test_root_certs.h"
30 #include "net/cert/x509_certificate.h"
31 #include "net/test/cert_test_util.h"
32
33 namespace {
34
35 class PlatformKeysTest : public PlatformKeysTestBase {
36 public:
37 enum class UserClientCertSlot { kPrivateSlot, kPublicSlot };
38
PlatformKeysTest(EnrollmentStatus enrollment_status,UserStatus user_status,bool key_permission_policy,UserClientCertSlot user_client_cert_slot)39 PlatformKeysTest(EnrollmentStatus enrollment_status,
40 UserStatus user_status,
41 bool key_permission_policy,
42 UserClientCertSlot user_client_cert_slot)
43 : PlatformKeysTestBase(SystemTokenStatus::EXISTS,
44 enrollment_status,
45 user_status),
46 key_permission_policy_(key_permission_policy),
47 user_client_cert_slot_(user_client_cert_slot) {}
48
SetUpOnMainThread()49 void SetUpOnMainThread() override {
50 if (!IsPreTest()) {
51 // Set up the private slot before
52 // |PlatformKeysTestBase::SetUpOnMainThread| triggers the user sign-in.
53 ASSERT_TRUE(user_private_slot_db_.is_open());
54 base::RunLoop loop;
55 base::PostTaskAndReply(
56 FROM_HERE, {content::BrowserThread::IO},
57 base::BindOnce(&PlatformKeysTest::SetPrivateSoftwareSlotOnIO,
58 base::Unretained(this),
59 crypto::ScopedPK11Slot(
60 PK11_ReferenceSlot(user_private_slot_db_.slot()))),
61 loop.QuitClosure());
62 loop.Run();
63 }
64
65 PlatformKeysTestBase::SetUpOnMainThread();
66
67 if (IsPreTest())
68 return;
69
70 {
71 base::RunLoop loop;
72 GetNSSCertDatabaseForProfile(
73 profile(),
74 base::BindRepeating(&PlatformKeysTest::SetupTestCerts,
75 base::Unretained(this), loop.QuitClosure()));
76 loop.Run();
77 }
78
79 base::FilePath extension_path = test_data_dir_.AppendASCII("platform_keys");
80 extension_ = LoadExtension(extension_path);
81
82 if (user_status() != UserStatus::UNMANAGED && key_permission_policy_)
83 SetupKeyPermissionUserPolicy();
84 }
85
SetupKeyPermissionUserPolicy()86 void SetupKeyPermissionUserPolicy() {
87 policy::PolicyMap policy;
88
89 // Set up the test policy that gives |extension_| the permission to access
90 // corporate keys.
91 std::unique_ptr<base::DictionaryValue> key_permissions_policy =
92 std::make_unique<base::DictionaryValue>();
93 {
94 std::unique_ptr<base::DictionaryValue> cert1_key_permission(
95 new base::DictionaryValue);
96 cert1_key_permission->SetKey("allowCorporateKeyUsage", base::Value(true));
97 key_permissions_policy->SetWithoutPathExpansion(
98 extension_->id(), std::move(cert1_key_permission));
99 }
100
101 policy.Set(policy::key::kKeyPermissions, policy::POLICY_LEVEL_MANDATORY,
102 policy::POLICY_SCOPE_USER, policy::POLICY_SOURCE_CLOUD,
103 std::move(key_permissions_policy), nullptr);
104
105 mock_policy_provider()->UpdateChromePolicy(policy);
106 }
107
GetExtensionPlatformKeysService()108 chromeos::ExtensionPlatformKeysService* GetExtensionPlatformKeysService() {
109 return chromeos::ExtensionPlatformKeysServiceFactory::GetForBrowserContext(
110 profile());
111 }
112
RunExtensionTest(const std::string & test_suite_name)113 bool RunExtensionTest(const std::string& test_suite_name) {
114 // By default, the system token is not available.
115 std::string system_token_availability;
116
117 // Only if the current user is of the same domain as the device is enrolled
118 // to, the system token is available to the extension.
119 if (system_token_status() == SystemTokenStatus::EXISTS &&
120 enrollment_status() == EnrollmentStatus::ENROLLED &&
121 user_status() == UserStatus::MANAGED_AFFILIATED_DOMAIN) {
122 system_token_availability = "systemTokenEnabled";
123 }
124
125 GURL url = extension_->GetResourceURL(base::StringPrintf(
126 "basic.html?%s#%s", system_token_availability.c_str(),
127 test_suite_name.c_str()));
128 return TestExtension(url.spec());
129 }
130
RegisterClient1AsCorporateKey()131 void RegisterClient1AsCorporateKey() {
132 const extensions::Extension* const fake_gen_extension =
133 LoadExtension(test_data_dir_.AppendASCII("platform_keys_genkey"));
134
135 policy::ProfilePolicyConnector* const policy_connector =
136 profile()->GetProfilePolicyConnector();
137
138 extensions::StateStore* const state_store =
139 extensions::ExtensionSystem::Get(profile())->state_store();
140
141 chromeos::KeyPermissions permissions(
142 policy_connector->IsManaged(), profile()->GetPrefs(),
143 policy_connector->policy_service(), state_store);
144
145 base::RunLoop run_loop;
146 permissions.GetPermissionsForExtension(
147 fake_gen_extension->id(),
148 base::Bind(&PlatformKeysTest::GotPermissionsForExtension,
149 base::Unretained(this), run_loop.QuitClosure()));
150 run_loop.Run();
151 }
152
153 protected:
154 // Imported into user's private or public slot, depending on the value of
155 // |user_client_cert_slot_|.
156 scoped_refptr<net::X509Certificate> client_cert1_;
157 // Imported into system slot.
158 scoped_refptr<net::X509Certificate> client_cert2_;
159 const extensions::Extension* extension_;
160
161 private:
extension_path() const162 base::FilePath extension_path() const {
163 return test_data_dir_.AppendASCII("platform_keys");
164 }
165
SetPrivateSoftwareSlotOnIO(crypto::ScopedPK11Slot slot)166 void SetPrivateSoftwareSlotOnIO(crypto::ScopedPK11Slot slot) {
167 crypto::SetPrivateSoftwareSlotForChromeOSUserForTesting(std::move(slot));
168 }
169
GotPermissionsForExtension(const base::Closure & done_callback,std::unique_ptr<chromeos::KeyPermissions::PermissionsForExtension> permissions_for_ext)170 void GotPermissionsForExtension(
171 const base::Closure& done_callback,
172 std::unique_ptr<chromeos::KeyPermissions::PermissionsForExtension>
173 permissions_for_ext) {
174 std::string client_cert1_spki =
175 chromeos::platform_keys::GetSubjectPublicKeyInfo(client_cert1_);
176 permissions_for_ext->RegisterKeyForCorporateUsage(
177 client_cert1_spki, {chromeos::KeyPermissions::KeyLocation::kUserSlot});
178 done_callback.Run();
179 }
180
SetupTestCerts(const base::Closure & done_callback,net::NSSCertDatabase * cert_db)181 void SetupTestCerts(const base::Closure& done_callback,
182 net::NSSCertDatabase* cert_db) {
183 SetupTestClientCerts(cert_db);
184 SetupTestCACerts();
185 done_callback.Run();
186 }
187
SetupTestClientCerts(net::NSSCertDatabase * cert_db)188 void SetupTestClientCerts(net::NSSCertDatabase* cert_db) {
189 // Sanity check to ensure that
190 // SetPrivateSoftwareSlotForChromeOSUserForTesting took effect.
191 EXPECT_EQ(user_private_slot_db_.slot(), cert_db->GetPrivateSlot().get());
192 EXPECT_NE(cert_db->GetPrivateSlot().get(), cert_db->GetPublicSlot().get());
193
194 crypto::ScopedPK11Slot slot =
195 user_client_cert_slot_ == UserClientCertSlot::kPrivateSlot
196 ? cert_db->GetPrivateSlot()
197 : cert_db->GetPublicSlot();
198 client_cert1_ = net::ImportClientCertAndKeyFromFile(
199 extension_path(), "client_1.pem", "client_1.pk8", slot.get());
200 ASSERT_TRUE(client_cert1_.get());
201
202 // Import a second client cert signed by another CA than client_1 into the
203 // system wide key slot.
204 client_cert2_ = net::ImportClientCertAndKeyFromFile(
205 extension_path(), "client_2.pem", "client_2.pk8",
206 test_system_slot()->slot());
207 ASSERT_TRUE(client_cert2_.get());
208 }
209
SetupTestCACerts()210 void SetupTestCACerts() {
211 net::TestRootCerts* root_certs = net::TestRootCerts::GetInstance();
212 // "root.pem" is the issuer of the "l1_leaf.pem" and (transitively)
213 // "l1_leaf.pem" certs which are loaded on the JS side. Generated by
214 // create_test_certs.sh .
215 root_certs->AddFromFile(extension_path().AppendASCII("root.pem"));
216 }
217
218 const bool key_permission_policy_;
219 const UserClientCertSlot user_client_cert_slot_;
220 crypto::ScopedTestNSSDB user_private_slot_db_;
221
222 DISALLOW_COPY_AND_ASSIGN(PlatformKeysTest);
223 };
224
225 class TestSelectDelegate
226 : public chromeos::ExtensionPlatformKeysService::SelectDelegate {
227 public:
228 // On each Select call, selects the next entry in |certs_to_select| from back
229 // to front. Once the first entry is reached, that one will be selected
230 // repeatedly.
231 // Entries of |certs_to_select| can be null in which case no certificate will
232 // be selected.
233 // If |certs_to_select| is empty, any invocation of |Select| will fail.
TestSelectDelegate(net::CertificateList certs_to_select)234 explicit TestSelectDelegate(net::CertificateList certs_to_select)
235 : certs_to_select_(certs_to_select) {}
236
~TestSelectDelegate()237 ~TestSelectDelegate() override {}
238
Select(const std::string & extension_id,const net::CertificateList & certs,const CertificateSelectedCallback & callback,content::WebContents * web_contents,content::BrowserContext * context)239 void Select(const std::string& extension_id,
240 const net::CertificateList& certs,
241 const CertificateSelectedCallback& callback,
242 content::WebContents* web_contents,
243 content::BrowserContext* context) override {
244 ASSERT_TRUE(web_contents);
245 ASSERT_TRUE(context);
246 ASSERT_FALSE(certs_to_select_.empty());
247 scoped_refptr<net::X509Certificate> selection;
248 if (certs_to_select_.back()) {
249 for (scoped_refptr<net::X509Certificate> cert : certs) {
250 if (cert->EqualsExcludingChain(certs_to_select_.back().get())) {
251 selection = cert;
252 break;
253 }
254 }
255 }
256 if (certs_to_select_.size() > 1)
257 certs_to_select_.pop_back();
258 callback.Run(selection);
259 }
260
261 private:
262 net::CertificateList certs_to_select_;
263 };
264
265 struct UnmanagedPlatformKeysTestParams {
UnmanagedPlatformKeysTestParams__anon8ac9c5770111::UnmanagedPlatformKeysTestParams266 UnmanagedPlatformKeysTestParams(
267 PlatformKeysTestBase::EnrollmentStatus enrollment_status,
268 PlatformKeysTest::UserClientCertSlot user_client_cert_slot)
269 : enrollment_status_(enrollment_status),
270 user_client_cert_slot_(user_client_cert_slot) {}
271
272 PlatformKeysTestBase::EnrollmentStatus enrollment_status_;
273 PlatformKeysTest::UserClientCertSlot user_client_cert_slot_;
274 };
275
276 class UnmanagedPlatformKeysTest
277 : public PlatformKeysTest,
278 public ::testing::WithParamInterface<UnmanagedPlatformKeysTestParams> {
279 public:
UnmanagedPlatformKeysTest()280 UnmanagedPlatformKeysTest()
281 : PlatformKeysTest(GetParam().enrollment_status_,
282 UserStatus::UNMANAGED,
283 false /* unused */,
284 GetParam().user_client_cert_slot_) {}
285 };
286
287 struct ManagedPlatformKeysTestParams {
ManagedPlatformKeysTestParams__anon8ac9c5770111::ManagedPlatformKeysTestParams288 ManagedPlatformKeysTestParams(
289 PlatformKeysTestBase::EnrollmentStatus enrollment_status,
290 PlatformKeysTestBase::UserStatus user_status)
291 : enrollment_status_(enrollment_status), user_status_(user_status) {}
292
293 PlatformKeysTestBase::EnrollmentStatus enrollment_status_;
294 PlatformKeysTestBase::UserStatus user_status_;
295 };
296
297 class ManagedWithPermissionPlatformKeysTest
298 : public PlatformKeysTest,
299 public ::testing::WithParamInterface<ManagedPlatformKeysTestParams> {
300 public:
ManagedWithPermissionPlatformKeysTest()301 ManagedWithPermissionPlatformKeysTest()
302 : PlatformKeysTest(GetParam().enrollment_status_,
303 GetParam().user_status_,
304 true /* grant the extension key permission */,
305 UserClientCertSlot::kPrivateSlot) {}
306 };
307
308 class ManagedWithoutPermissionPlatformKeysTest
309 : public PlatformKeysTest,
310 public ::testing::WithParamInterface<ManagedPlatformKeysTestParams> {
311 public:
ManagedWithoutPermissionPlatformKeysTest()312 ManagedWithoutPermissionPlatformKeysTest()
313 : PlatformKeysTest(GetParam().enrollment_status_,
314 GetParam().user_status_,
315 false /* do not grant key permission */,
316 UserClientCertSlot::kPrivateSlot) {}
317 };
318
319 } // namespace
320
IN_PROC_BROWSER_TEST_P(UnmanagedPlatformKeysTest,PRE_Basic)321 IN_PROC_BROWSER_TEST_P(UnmanagedPlatformKeysTest, PRE_Basic) {
322 RunPreTest();
323 }
324
325 // At first interactively selects |client_cert1_| and |client_cert2_| to grant
326 // permissions and afterwards runs more basic tests.
327 // After the initial two interactive calls, the simulated user does not select
328 // any cert.
IN_PROC_BROWSER_TEST_P(UnmanagedPlatformKeysTest,Basic)329 IN_PROC_BROWSER_TEST_P(UnmanagedPlatformKeysTest, Basic) {
330 net::CertificateList certs;
331 certs.push_back(nullptr);
332 certs.push_back(client_cert2_);
333 certs.push_back(client_cert1_);
334
335 GetExtensionPlatformKeysService()->SetSelectDelegate(
336 std::make_unique<TestSelectDelegate>(certs));
337
338 ASSERT_TRUE(RunExtensionTest("basicTests")) << message_;
339 }
340
IN_PROC_BROWSER_TEST_P(UnmanagedPlatformKeysTest,PRE_Permissions)341 IN_PROC_BROWSER_TEST_P(UnmanagedPlatformKeysTest, PRE_Permissions) {
342 RunPreTest();
343 }
344
345 // On interactive calls, the simulated user always selects |client_cert1_| if
346 // matching.
IN_PROC_BROWSER_TEST_P(UnmanagedPlatformKeysTest,Permissions)347 IN_PROC_BROWSER_TEST_P(UnmanagedPlatformKeysTest, Permissions) {
348 net::CertificateList certs;
349 certs.push_back(client_cert1_);
350
351 GetExtensionPlatformKeysService()->SetSelectDelegate(
352 std::make_unique<TestSelectDelegate>(certs));
353
354 ASSERT_TRUE(RunExtensionTest("permissionTests")) << message_;
355 }
356
357 INSTANTIATE_TEST_SUITE_P(
358 Unmanaged,
359 UnmanagedPlatformKeysTest,
360 ::testing::Values(UnmanagedPlatformKeysTestParams(
361 PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
362 PlatformKeysTest::UserClientCertSlot::kPrivateSlot),
363 UnmanagedPlatformKeysTestParams(
364 PlatformKeysTestBase::EnrollmentStatus::NOT_ENROLLED,
365 PlatformKeysTest::UserClientCertSlot::kPrivateSlot),
366 UnmanagedPlatformKeysTestParams(
367 PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
368 PlatformKeysTest::UserClientCertSlot::kPublicSlot),
369 UnmanagedPlatformKeysTestParams(
370 PlatformKeysTestBase::EnrollmentStatus::NOT_ENROLLED,
371 PlatformKeysTest::UserClientCertSlot::kPublicSlot)));
372
IN_PROC_BROWSER_TEST_P(ManagedWithoutPermissionPlatformKeysTest,PRE_UserPermissionsBlocked)373 IN_PROC_BROWSER_TEST_P(ManagedWithoutPermissionPlatformKeysTest,
374 PRE_UserPermissionsBlocked) {
375 RunPreTest();
376 }
377
IN_PROC_BROWSER_TEST_P(ManagedWithoutPermissionPlatformKeysTest,UserPermissionsBlocked)378 IN_PROC_BROWSER_TEST_P(ManagedWithoutPermissionPlatformKeysTest,
379 UserPermissionsBlocked) {
380 // To verify that the user is not prompted for any certificate selection,
381 // set up a delegate that fails on any invocation.
382 GetExtensionPlatformKeysService()->SetSelectDelegate(
383 std::make_unique<TestSelectDelegate>(net::CertificateList()));
384
385 ASSERT_TRUE(RunExtensionTest("managedProfile")) << message_;
386 }
387
IN_PROC_BROWSER_TEST_P(ManagedWithoutPermissionPlatformKeysTest,PRE_CorporateKeyAccessBlocked)388 IN_PROC_BROWSER_TEST_P(ManagedWithoutPermissionPlatformKeysTest,
389 PRE_CorporateKeyAccessBlocked) {
390 RunPreTest();
391 }
392
393 // A corporate key must not be useable if there is no policy permitting it.
IN_PROC_BROWSER_TEST_P(ManagedWithoutPermissionPlatformKeysTest,CorporateKeyAccessBlocked)394 IN_PROC_BROWSER_TEST_P(ManagedWithoutPermissionPlatformKeysTest,
395 CorporateKeyAccessBlocked) {
396 RegisterClient1AsCorporateKey();
397
398 // To verify that the user is not prompted for any certificate selection,
399 // set up a delegate that fails on any invocation.
400 GetExtensionPlatformKeysService()->SetSelectDelegate(
401 std::make_unique<TestSelectDelegate>(net::CertificateList()));
402
403 ASSERT_TRUE(RunExtensionTest("corporateKeyWithoutPermissionTests"))
404 << message_;
405 }
406
407 INSTANTIATE_TEST_SUITE_P(
408 ManagedWithoutPermission,
409 ManagedWithoutPermissionPlatformKeysTest,
410 ::testing::Values(
411 ManagedPlatformKeysTestParams(
412 PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
413 PlatformKeysTestBase::UserStatus::MANAGED_AFFILIATED_DOMAIN),
414 ManagedPlatformKeysTestParams(
415 PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
416 PlatformKeysTestBase::UserStatus::MANAGED_OTHER_DOMAIN),
417 ManagedPlatformKeysTestParams(
418 PlatformKeysTestBase::EnrollmentStatus::NOT_ENROLLED,
419 PlatformKeysTestBase::UserStatus::MANAGED_OTHER_DOMAIN)));
420
IN_PROC_BROWSER_TEST_P(ManagedWithPermissionPlatformKeysTest,PRE_PolicyGrantsAccessToCorporateKey)421 IN_PROC_BROWSER_TEST_P(ManagedWithPermissionPlatformKeysTest,
422 PRE_PolicyGrantsAccessToCorporateKey) {
423 RunPreTest();
424 }
425
IN_PROC_BROWSER_TEST_P(ManagedWithPermissionPlatformKeysTest,PolicyGrantsAccessToCorporateKey)426 IN_PROC_BROWSER_TEST_P(ManagedWithPermissionPlatformKeysTest,
427 PolicyGrantsAccessToCorporateKey) {
428 RegisterClient1AsCorporateKey();
429
430 // Set up the test SelectDelegate to select |client_cert1_| if available for
431 // selection.
432 net::CertificateList certs;
433 certs.push_back(client_cert1_);
434
435 GetExtensionPlatformKeysService()->SetSelectDelegate(
436 std::make_unique<TestSelectDelegate>(certs));
437
438 ASSERT_TRUE(RunExtensionTest("corporateKeyWithPermissionTests")) << message_;
439 }
440
IN_PROC_BROWSER_TEST_P(ManagedWithPermissionPlatformKeysTest,PRE_PolicyDoesGrantAccessToNonCorporateKey)441 IN_PROC_BROWSER_TEST_P(ManagedWithPermissionPlatformKeysTest,
442 PRE_PolicyDoesGrantAccessToNonCorporateKey) {
443 RunPreTest();
444 }
445
IN_PROC_BROWSER_TEST_P(ManagedWithPermissionPlatformKeysTest,PolicyDoesGrantAccessToNonCorporateKey)446 IN_PROC_BROWSER_TEST_P(ManagedWithPermissionPlatformKeysTest,
447 PolicyDoesGrantAccessToNonCorporateKey) {
448 // The policy grants access to corporate keys.
449 // As the profile is managed, the user must not be able to grant any
450 // certificate permission.
451 // If the user is not affilited, no corporate keys are available. Set up a
452 // delegate that fails on any invocation. If the user is affiliated, client_2
453 // on the system token will be avialable for selection, as it is implicitly
454 // corporate.
455 net::CertificateList certs;
456 if (user_status() == UserStatus::MANAGED_AFFILIATED_DOMAIN)
457 certs.push_back(nullptr);
458
459 GetExtensionPlatformKeysService()->SetSelectDelegate(
460 std::make_unique<TestSelectDelegate>(certs));
461
462 ASSERT_TRUE(RunExtensionTest("policyDoesGrantAccessToNonCorporateKey"))
463 << message_;
464 }
465
466 INSTANTIATE_TEST_SUITE_P(
467 ManagedWithPermission,
468 ManagedWithPermissionPlatformKeysTest,
469 ::testing::Values(
470 ManagedPlatformKeysTestParams(
471 PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
472 PlatformKeysTestBase::UserStatus::MANAGED_AFFILIATED_DOMAIN),
473 ManagedPlatformKeysTestParams(
474 PlatformKeysTestBase::EnrollmentStatus::ENROLLED,
475 PlatformKeysTestBase::UserStatus::MANAGED_OTHER_DOMAIN),
476 ManagedPlatformKeysTestParams(
477 PlatformKeysTestBase::EnrollmentStatus::NOT_ENROLLED,
478 PlatformKeysTestBase::UserStatus::MANAGED_OTHER_DOMAIN)));
479