[go: nahoru, domu]

Certificates for Lacros (MVP)

This allows Lacros-Chrome access to the system-wide TPM slot,
the per-user TPM slot, and per-user storage for certificates and
trust settings, for enterprise-managed devices where the logged-in
user is an affiliate of that enterprise.

Rather than Lacros-Chrome directly interacting with the login services,
it requests that Ash-Chrome notify it when the TPM and Cryptohome are
available, receiving the path to the per-user storage and the result
of TPM initialization. Lacros-Chrome can then initialize initialize a
connection to the TPM (via a PKCS#11 module) and initialize storage at
the path provided by Ash-Chrome.

For users that are not affiliated, Chrome today enables access to both
the per-user TPM and per-user storage. However, for Lacros-Chrome, this
is a known issue and not yet supported (crbug.com/1146430). This is
because loading the TPM unconditionally loads both the system and user
slot today, while non-affiliated users should not have access to the
system slot. The per-user storage will be loaded for all users with
this CL.

Similarly, using the system slot on the login screen is not yet
supported, and will be addressed in a follow-up (crbug.com/1146430).

Bug: 1145946
Change-Id: I2edc6e04c2de09f4c8a4d3bdca9c9ba5a9dee3fa
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2435642
Commit-Queue: Michael Ershov <miersh@google.com>
Reviewed-by: David Roger <droger@chromium.org>
Reviewed-by: Roland Bock <rbock@google.com>
Reviewed-by: Pavol Marko <pmarko@chromium.org>
Reviewed-by: Ryan Sleevi <rsleevi@chromium.org>
Reviewed-by: Erik Chen <erikchen@chromium.org>
Cr-Commit-Position: refs/heads/master@{#832960}
diff --git a/crypto/BUILD.gn b/crypto/BUILD.gn
index c5d6af8..54ca26c6 100644
--- a/crypto/BUILD.gn
+++ b/crypto/BUILD.gn
@@ -123,6 +123,13 @@
     sources += [ "nss_util_chromeos.cc" ]
   }
 
+  if (is_chromeos || is_lacros) {
+    sources += [
+      "chaps_support.cc",
+      "chaps_support.h",
+    ]
+  }
+
   defines = [ "CRYPTO_IMPLEMENTATION" ]
 
   if (is_nacl) {
diff --git a/crypto/chaps_support.cc b/crypto/chaps_support.cc
new file mode 100644
index 0000000..b68c3fb
--- /dev/null
+++ b/crypto/chaps_support.cc
@@ -0,0 +1,88 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "crypto/chaps_support.h"
+
+#include <dlfcn.h>
+#include <secmodt.h>
+
+#include "base/logging.h"
+#include "base/threading/scoped_blocking_call.h"
+#include "nss_util_internal.h"
+
+namespace crypto {
+
+namespace {
+
+// Constants for loading the Chrome OS TPM-backed PKCS #11 library.
+const char kChapsModuleName[] = "Chaps";
+const char kChapsPath[] = "libchaps.so";
+
+class ScopedChapsLoadFixup {
+ public:
+  ScopedChapsLoadFixup();
+  ~ScopedChapsLoadFixup();
+
+ private:
+#if defined(COMPONENT_BUILD)
+  void* chaps_handle_;
+#endif
+};
+
+#if defined(COMPONENT_BUILD)
+
+ScopedChapsLoadFixup::ScopedChapsLoadFixup() {
+  // HACK: libchaps links the system protobuf and there are symbol conflicts
+  // with the bundled copy. Load chaps with RTLD_DEEPBIND to workaround.
+  chaps_handle_ = dlopen(kChapsPath, RTLD_LOCAL | RTLD_NOW | RTLD_DEEPBIND);
+}
+
+ScopedChapsLoadFixup::~ScopedChapsLoadFixup() {
+  // LoadNSSModule() will have taken a 2nd reference.
+  if (chaps_handle_)
+    dlclose(chaps_handle_);
+}
+
+#else
+
+ScopedChapsLoadFixup::ScopedChapsLoadFixup() = default;
+ScopedChapsLoadFixup::~ScopedChapsLoadFixup() = default;
+
+#endif  // defined(COMPONENT_BUILD)
+
+}  // namespace
+
+SECMODModule* LoadChaps() {
+  // NSS functions may reenter //net via extension hooks. If the reentered
+  // code needs to synchronously wait for a task to run but the thread pool in
+  // which that task must run doesn't have enough threads to schedule it, a
+  // deadlock occurs. To prevent that, the base::ScopedBlockingCall below
+  // increments the thread pool capacity for the duration of the TPM
+  // initialization.
+  base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
+                                                base::BlockingType::WILL_BLOCK);
+
+  ScopedChapsLoadFixup chaps_loader;
+
+  DVLOG(3) << "Loading chaps...";
+  return LoadNSSModule(
+      kChapsModuleName, kChapsPath,
+      // For more details on these parameters, see:
+      // https://developer.mozilla.org/en/PKCS11_Module_Specs
+      // slotFlags=[PublicCerts] -- Certificates and public keys can be
+      //   read from this slot without requiring a call to C_Login.
+      // askpw=only -- Only authenticate to the token when necessary.
+      "NSS=\"slotParams=(0={slotFlags=[PublicCerts] askpw=only})\"");
+}
+
+bool IsSlotProvidedByChaps(PK11SlotInfo* slot) {
+  if (!slot)
+    return false;
+
+  SECMODModule* pk11_module = PK11_GetModule(slot);
+  return pk11_module && base::StringPiece(pk11_module->commonName) ==
+                            base::StringPiece(kChapsModuleName);
+}
+
+}  // namespace crypto
diff --git a/crypto/chaps_support.h b/crypto/chaps_support.h
new file mode 100644
index 0000000..6b44dcb
--- /dev/null
+++ b/crypto/chaps_support.h
@@ -0,0 +1,22 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CRYPTO_CHAPS_SUPPORT_H_
+#define CRYPTO_CHAPS_SUPPORT_H_
+
+#include <secmodt.h>
+
+#include "crypto/crypto_export.h"
+
+namespace crypto {
+
+// Loads chaps module for this NSS session.
+CRYPTO_EXPORT SECMODModule* LoadChaps();
+
+// Returns true if chaps is the module to which |slot| is attached.
+CRYPTO_EXPORT bool IsSlotProvidedByChaps(PK11SlotInfo* slot);
+
+}  // namespace crypto
+
+#endif  // CRYPTO_CHAPS_SUPPORT_H_
diff --git a/crypto/nss_util.cc b/crypto/nss_util.cc
index 0f124c2..f7f57dc 100644
--- a/crypto/nss_util.cc
+++ b/crypto/nss_util.cc
@@ -35,13 +35,14 @@
 
 namespace {
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_LACROS)
+
 // Fake certificate authority database used for testing.
 static const base::FilePath::CharType kReadOnlyCertDB[] =
     FILE_PATH_LITERAL("/etc/fake_root_ca/nssdb");
-#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
-#if !BUILDFLAG(IS_CHROMEOS_ASH)
+#else
+
 base::FilePath GetDefaultConfigDirectory() {
   base::FilePath dir;
   base::PathService::Get(base::DIR_HOME, &dir);
@@ -57,7 +58,8 @@
   DVLOG(2) << "DefaultConfigDirectory: " << dir.value();
   return dir;
 }
-#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)
+
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_LACROS)
 
 // On non-Chrome OS platforms, return the default config directory. On Chrome OS
 // test images, return a read-only directory with fake root CA certs (which are
@@ -65,7 +67,7 @@
 // code). On Chrome OS non-test images (where the read-only directory doesn't
 // exist), return an empty path.
 base::FilePath GetInitialConfigDirectory() {
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_LACROS)
   base::FilePath database_dir = base::FilePath(kReadOnlyCertDB);
   if (!base::PathExists(database_dir))
     database_dir.clear();
@@ -158,7 +160,7 @@
       // Use "sql:" which can be shared by multiple processes safely.
       std::string nss_config_dir =
           base::StringPrintf("sql:%s", database_dir.value().c_str());
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_LACROS)
       status = NSS_Init(nss_config_dir.c_str());
 #else
       status = NSS_InitReadWrite(nss_config_dir.c_str());
diff --git a/crypto/nss_util_chromeos.cc b/crypto/nss_util_chromeos.cc
index 1b52f16..d9fd3271 100644
--- a/crypto/nss_util_chromeos.cc
+++ b/crypto/nss_util_chromeos.cc
@@ -4,7 +4,6 @@
 
 #include "crypto/nss_util.h"
 
-#include <dlfcn.h>
 #include <nss.h>
 #include <pk11pub.h>
 #include <plarena.h>
@@ -35,6 +34,7 @@
 #include "base/threading/thread_restrictions.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
+#include "crypto/chaps_support.h"
 #include "crypto/nss_util_internal.h"
 
 namespace crypto {
@@ -43,10 +43,6 @@
 
 const char kUserNSSDatabaseName[] = "UserNSSDB";
 
-// Constants for loading the Chrome OS TPM-backed PKCS #11 library.
-const char kChapsModuleName[] = "Chaps";
-const char kChapsPath[] = "libchaps.so";
-
 class ChromeOSUserData {
  public:
   using SlotReadyCallback = base::OnceCallback<void(ScopedPK11Slot)>;
@@ -110,38 +106,6 @@
   SlotReadyCallbackList tpm_ready_callback_list_;
 };
 
-class ScopedChapsLoadFixup {
- public:
-  ScopedChapsLoadFixup();
-  ~ScopedChapsLoadFixup();
-
- private:
-#if defined(COMPONENT_BUILD)
-  void* chaps_handle_;
-#endif
-};
-
-#if defined(COMPONENT_BUILD)
-
-ScopedChapsLoadFixup::ScopedChapsLoadFixup() {
-  // HACK: libchaps links the system protobuf and there are symbol conflicts
-  // with the bundled copy. Load chaps with RTLD_DEEPBIND to workaround.
-  chaps_handle_ = dlopen(kChapsPath, RTLD_LOCAL | RTLD_NOW | RTLD_DEEPBIND);
-}
-
-ScopedChapsLoadFixup::~ScopedChapsLoadFixup() {
-  // LoadNSSModule() will have taken a 2nd reference.
-  if (chaps_handle_)
-    dlclose(chaps_handle_);
-}
-
-#else
-
-ScopedChapsLoadFixup::ScopedChapsLoadFixup() {}
-ScopedChapsLoadFixup::~ScopedChapsLoadFixup() {}
-
-#endif  // defined(COMPONENT_BUILD)
-
 class ChromeOSTokenManager {
  public:
   // Used with PostTaskAndReply to pass handles to worker thread and back.
@@ -160,7 +124,7 @@
     // the current thread, due to NSS's internal locking requirements
     base::ThreadRestrictions::ScopedAllowIO allow_io;
 
-    base::FilePath nssdb_path = path.AppendASCII(".pki").AppendASCII("nssdb");
+    base::FilePath nssdb_path = GetSoftwareNSSDBPath(path);
     if (!base::CreateDirectory(nssdb_path)) {
       LOG(ERROR) << "Failed to create " << nssdb_path.value() << " directory.";
       return ScopedPK11Slot();
@@ -235,17 +199,7 @@
         FROM_HERE, base::BlockingType::WILL_BLOCK);
 
     if (!tpm_args->chaps_module) {
-      ScopedChapsLoadFixup chaps_loader;
-
-      DVLOG(3) << "Loading chaps...";
-      tpm_args->chaps_module = LoadNSSModule(
-          kChapsModuleName, kChapsPath,
-          // For more details on these parameters, see:
-          // https://developer.mozilla.org/en/PKCS11_Module_Specs
-          // slotFlags=[PublicCerts] -- Certificates and public keys can be
-          //   read from this slot without requiring a call to C_Login.
-          // askpw=only -- Only authenticate to the token when necessary.
-          "NSS=\"slotParams=(0={slotFlags=[PublicCerts] askpw=only})\"");
+      tpm_args->chaps_module = LoadChaps();
     }
     if (tpm_args->chaps_module) {
       tpm_args->tpm_slot =
@@ -527,6 +481,11 @@
     LAZY_INSTANCE_INITIALIZER;
 }  // namespace
 
+base::FilePath GetSoftwareNSSDBPath(
+    const base::FilePath& profile_directory_path) {
+  return profile_directory_path.AppendASCII(".pki").AppendASCII("nssdb");
+}
+
 ScopedPK11Slot GetSystemNSSKeySlot(
     base::OnceCallback<void(ScopedPK11Slot)> callback) {
   return g_token_manager.Get().GetSystemNSSKeySlot(std::move(callback));
@@ -605,13 +564,4 @@
       std::move(slot));
 }
 
-bool IsSlotProvidedByChaps(PK11SlotInfo* slot) {
-  if (!slot)
-    return false;
-
-  SECMODModule* pk11_module = PK11_GetModule(slot);
-  return pk11_module && base::StringPiece(pk11_module->commonName) ==
-                            base::StringPiece(kChapsModuleName);
-}
-
 }  // namespace crypto
diff --git a/crypto/nss_util_internal.h b/crypto/nss_util_internal.h
index 7221375..99fbb10 100644
--- a/crypto/nss_util_internal.h
+++ b/crypto/nss_util_internal.h
@@ -44,6 +44,11 @@
 };
 
 #if BUILDFLAG(IS_CHROMEOS_ASH)
+// Returns path to the NSS database file in the provided profile
+// directory.
+CRYPTO_EXPORT base::FilePath GetSoftwareNSSDBPath(
+    const base::FilePath& profile_directory_path);
+
 // Returns a reference to the system-wide TPM slot if it is loaded. If it is not
 // loaded and |callback| is non-null, the |callback| will be run once the slot
 // is loaded.
@@ -122,9 +127,6 @@
 CRYPTO_EXPORT void SetPrivateSoftwareSlotForChromeOSUserForTesting(
     ScopedPK11Slot slot);
 
-// Returns true if chaps is the module to which |slot| is attached.
-CRYPTO_EXPORT bool IsSlotProvidedByChaps(PK11SlotInfo* slot);
-
 #endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 
 // Loads the given module for this NSS session.