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