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.