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/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 6a61f55c..9d9aab0 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -4559,6 +4559,10 @@
"lacros/account_manager_facade_lacros.h",
"lacros/account_manager_util.cc",
"lacros/account_manager_util.h",
+ "lacros/cert_db_initializer.cc",
+ "lacros/cert_db_initializer.h",
+ "lacros/cert_db_initializer_factory.cc",
+ "lacros/cert_db_initializer_factory.h",
"lacros/feedback_util.cc",
"lacros/feedback_util.h",
"lacros/immersive_context_lacros.cc",
diff --git a/chrome/browser/chromeos/BUILD.gn b/chrome/browser/chromeos/BUILD.gn
index 8a9627d..2a3e550 100644
--- a/chrome/browser/chromeos/BUILD.gn
+++ b/chrome/browser/chromeos/BUILD.gn
@@ -1041,6 +1041,8 @@
"crosapi/browser_manager_observer.h",
"crosapi/browser_util.cc",
"crosapi/browser_util.h",
+ "crosapi/cert_database_ash.cc",
+ "crosapi/cert_database_ash.h",
"crosapi/environment_provider.cc",
"crosapi/environment_provider.h",
"crosapi/fake_browser_manager.cc",
diff --git a/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.cc b/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.cc
index 2a051c1..4e5accf 100644
--- a/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.cc
+++ b/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.cc
@@ -13,6 +13,7 @@
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/chromeos/crosapi/account_manager_ash.h"
#include "chrome/browser/chromeos/crosapi/browser_manager.h"
+#include "chrome/browser/chromeos/crosapi/cert_database_ash.h"
#include "chrome/browser/chromeos/crosapi/feedback_ash.h"
#include "chrome/browser/chromeos/crosapi/file_manager_ash.h"
#include "chrome/browser/chromeos/crosapi/keystore_service_ash.h"
@@ -39,7 +40,8 @@
AshChromeServiceImpl::AshChromeServiceImpl(
mojo::PendingReceiver<mojom::AshChromeService> pending_receiver)
: receiver_(this, std::move(pending_receiver)),
- screen_manager_ash_(std::make_unique<ScreenManagerAsh>()) {
+ screen_manager_ash_(std::make_unique<ScreenManagerAsh>()),
+ cert_database_ash_(std::make_unique<CertDatabaseAsh>()) {
// TODO(hidehiko): Remove non-critical log from here.
// Currently this is the signal that the connection is established.
LOG(WARNING) << "AshChromeService connected.";
@@ -146,6 +148,11 @@
std::move(receiver));
}
+void AshChromeServiceImpl::BindCertDatabase(
+ mojo::PendingReceiver<mojom::CertDatabase> receiver) {
+ cert_database_ash_->BindReceiver(std::move(receiver));
+}
+
void AshChromeServiceImpl::OnLacrosStartup(mojom::LacrosInfoPtr lacros_info) {
BrowserManager::Get()->set_lacros_version(lacros_info->lacros_version);
}
diff --git a/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.h b/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.h
index 972fbeb5..9fb6e4e 100644
--- a/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.h
+++ b/chrome/browser/chromeos/crosapi/ash_chrome_service_impl.h
@@ -14,6 +14,7 @@
namespace crosapi {
class AccountManagerAsh;
+class CertDatabaseAsh;
class FeedbackAsh;
class FileManagerAsh;
class KeystoreServiceAsh;
@@ -45,6 +46,8 @@
void BindHidManager(
mojo::PendingReceiver<device::mojom::HidManager> receiver) override;
void BindFeedback(mojo::PendingReceiver<mojom::Feedback> receiver) override;
+ void BindCertDatabase(
+ mojo::PendingReceiver<mojom::CertDatabase> receiver) override;
void OnLacrosStartup(mojom::LacrosInfoPtr lacros_info) override;
void BindMediaSessionController(
mojo::PendingReceiver<media_session::mojom::MediaControllerManager>
@@ -66,6 +69,7 @@
std::unique_ptr<ScreenManagerAsh> screen_manager_ash_;
std::unique_ptr<SelectFileAsh> select_file_ash_;
std::unique_ptr<FeedbackAsh> feedback_ash_;
+ std::unique_ptr<CertDatabaseAsh> cert_database_ash_;
};
} // namespace crosapi
diff --git a/chrome/browser/chromeos/crosapi/browser_util.cc b/chrome/browser/chromeos/crosapi/browser_util.cc
index f89b81dc..a7e77d5 100644
--- a/chrome/browser/chromeos/crosapi/browser_util.cc
+++ b/chrome/browser/chromeos/crosapi/browser_util.cc
@@ -23,6 +23,7 @@
#include "chrome/common/pref_names.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/crosapi/cpp/crosapi_constants.h"
+#include "chromeos/crosapi/mojom/cert_database.mojom.h"
#include "chromeos/crosapi/mojom/crosapi.mojom.h"
#include "components/exo/shell_surface_util.h"
#include "components/metrics/metrics_pref_names.h"
@@ -156,11 +157,12 @@
base::flat_map<base::Token, uint32_t> GetInterfaceVersions() {
static_assert(
- crosapi::mojom::AshChromeService::Version_ == 6,
+ crosapi::mojom::AshChromeService::Version_ == 7,
"if you add a new crosapi, please add it to the version map here");
InterfaceVersions versions;
AddVersion<crosapi::mojom::AccountManager>(&versions);
AddVersion<crosapi::mojom::AshChromeService>(&versions);
+ AddVersion<crosapi::mojom::CertDatabase>(&versions);
AddVersion<crosapi::mojom::Feedback>(&versions);
AddVersion<crosapi::mojom::FileManager>(&versions);
AddVersion<crosapi::mojom::KeystoreService>(&versions);
diff --git a/chrome/browser/chromeos/crosapi/cert_database_ash.cc b/chrome/browser/chromeos/crosapi/cert_database_ash.cc
new file mode 100644
index 0000000..1e2f8e1
--- /dev/null
+++ b/chrome/browser/chromeos/crosapi/cert_database_ash.cc
@@ -0,0 +1,115 @@
+// 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 "chrome/browser/chromeos/crosapi/cert_database_ash.h"
+
+#include "chrome/browser/chromeos/profiles/profile_helper.h"
+#include "chrome/browser/profiles/profile_manager.h"
+#include "chromeos/crosapi/mojom/cert_database.mojom.h"
+#include "chromeos/login/login_state/login_state.h"
+
+#include "chromeos/tpm/tpm_token_info_getter.h"
+#include "components/account_id/account_id.h"
+#include "components/user_manager/user.h"
+#include "content/public/browser/browser_thread.h"
+#include "crypto/nss_util_internal.h"
+
+namespace crosapi {
+
+CertDatabaseAsh::CertDatabaseAsh() {
+ DCHECK(chromeos::LoginState::IsInitialized());
+ chromeos::LoginState::Get()->AddObserver(this);
+}
+
+CertDatabaseAsh::~CertDatabaseAsh() {
+ chromeos::LoginState::Get()->RemoveObserver(this);
+}
+
+void CertDatabaseAsh::BindReceiver(
+ mojo::PendingReceiver<mojom::CertDatabase> pending_receiver) {
+ receivers_.Add(this, std::move(pending_receiver));
+}
+
+void CertDatabaseAsh::GetCertDatabaseInfo(
+ GetCertDatabaseInfoCallback callback) {
+ // TODO(crbug.com/1146430): For now Lacros-Chrome will initialize certificate
+ // database only in session. Revisit later to decide what to do on the login
+ // screen.
+ if (!chromeos::LoginState::Get()->IsUserLoggedIn()) {
+ LOG(ERROR) << "Not implemented";
+ std::move(callback).Run(nullptr);
+ return;
+ }
+
+ // If this is the first attempt to load the TPM, begin the async load.
+ if (!is_tpm_token_ready_.has_value()) {
+ WaitForTpmTokenReady(std::move(callback));
+ return;
+ }
+
+ const user_manager::User* user =
+ user_manager::UserManager::Get()->GetPrimaryUser();
+
+ // If user is not available or the TPM was previously attempted to be loaded,
+ // and failed, don't retry, just return an empty result that indicates error.
+ if (!user || !is_tpm_token_ready_.value()) {
+ std::move(callback).Run(nullptr);
+ return;
+ }
+
+ // Otherwise, if the TPM was already loaded previously, let the
+ // caller know.
+ // TODO(crbug.com/1146430) For now Lacros-Chrome loads chaps and has access to
+ // TPM operations only for affiliated users, because it gives access to
+ // system token. Find a way to give unaffiliated users access only to user TPM
+ // token.
+ mojom::GetCertDatabaseInfoResultPtr result =
+ mojom::GetCertDatabaseInfoResult::New();
+ result->should_load_chaps = user->IsAffiliated();
+ result->software_nss_db_path =
+ crypto::GetSoftwareNSSDBPath(
+ ProfileManager::GetPrimaryUserProfile()->GetPath())
+ .value();
+ std::move(callback).Run(std::move(result));
+}
+
+void CertDatabaseAsh::WaitForTpmTokenReady(
+ GetCertDatabaseInfoCallback callback) {
+ DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+
+ Profile* profile = ProfileManager::GetPrimaryUserProfile();
+ AccountId account_id =
+ chromeos::ProfileHelper::Get()->GetUserByProfile(profile)->GetAccountId();
+
+ std::unique_ptr<chromeos::TPMTokenInfoGetter> scoped_token_info_getter =
+ chromeos::TPMTokenInfoGetter::CreateForUserToken(
+ account_id, chromeos::CryptohomeClient::Get(),
+ base::ThreadTaskRunnerHandle::Get());
+ chromeos::TPMTokenInfoGetter* token_info_getter =
+ scoped_token_info_getter.get();
+
+ token_info_getter->Start(base::BindOnce(
+ &CertDatabaseAsh::OnTpmTokenReady, weak_factory_.GetWeakPtr(),
+ std::move(scoped_token_info_getter), std::move(callback)));
+}
+
+void CertDatabaseAsh::OnTpmTokenReady(
+ std::unique_ptr<chromeos::TPMTokenInfoGetter> token_getter,
+ GetCertDatabaseInfoCallback callback,
+ base::Optional<chromeos::CryptohomeClient::TpmTokenInfo> token_info) {
+ is_tpm_token_ready_ = token_info.has_value();
+
+ // Calling the initial method again. Since |is_tpm_token_ready_| is not empty
+ // this time, it will return some result via mojo.
+ GetCertDatabaseInfo(std::move(callback));
+}
+
+void CertDatabaseAsh::LoggedInStateChanged() {
+ // Cached result is valid only within one session and should be reset on
+ // sign out. Currently it is not necessary to reset it on sign in, but doesn't
+ // hurt.
+ is_tpm_token_ready_.reset();
+}
+
+} // namespace crosapi
diff --git a/chrome/browser/chromeos/crosapi/cert_database_ash.h b/chrome/browser/chromeos/crosapi/cert_database_ash.h
new file mode 100644
index 0000000..6ac4f997
--- /dev/null
+++ b/chrome/browser/chromeos/crosapi/cert_database_ash.h
@@ -0,0 +1,70 @@
+// 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 CHROME_BROWSER_CHROMEOS_CROSAPI_CERT_DATABASE_ASH_H_
+#define CHROME_BROWSER_CHROMEOS_CROSAPI_CERT_DATABASE_ASH_H_
+
+#include "base/memory/weak_ptr.h"
+#include "base/optional.h"
+#include "chromeos/crosapi/mojom/cert_database.mojom.h"
+#include "chromeos/dbus/cryptohome/cryptohome_client.h"
+#include "chromeos/login/login_state/login_state.h"
+#include "mojo/public/cpp/bindings/pending_receiver.h"
+#include "mojo/public/cpp/bindings/receiver_set.h"
+
+namespace chromeos {
+class TPMTokenInfoGetter;
+} // namespace chromeos
+
+namespace crosapi {
+
+// Implements the crosapi interface for certificate database. Lives in
+// Ash-Chrome on the UI thread.
+//
+// It is expected that during Lacros-Chrome initialization when it creates the
+// main profile (that contains device account), it will call GetCertDatabaseInfo
+// mojo API. If the ChromeOS user session was just started, it can take time for
+// Ash-Chrome to initialize TPM and certificate database. When it is done, the
+// API call will be resolved. If Lacros-Chrome is restarted, it will call
+// GetCertDatabaseInfo again and receive a cached result from the first call.
+// The cached result is reset on login state change (i.e. sign in / sign out).
+class CertDatabaseAsh : public mojom::CertDatabase,
+ chromeos::LoginState::Observer {
+ public:
+ CertDatabaseAsh();
+ CertDatabaseAsh(const CertDatabaseAsh&) = delete;
+ CertDatabaseAsh& operator=(const CertDatabaseAsh&) = delete;
+ ~CertDatabaseAsh() override;
+
+ void BindReceiver(mojo::PendingReceiver<mojom::CertDatabase> receiver);
+
+ // Returns to Lacros-Chrome all necessary data to initialize certificate
+ // database when it is ready. Caches the result of first call for all
+ // subsequent calls during current user session.
+ void GetCertDatabaseInfo(GetCertDatabaseInfoCallback callback) override;
+
+ private:
+ // chromeos::LoginState::Observer
+ void LoggedInStateChanged() override;
+
+ // The fact that TpmTokenInfo can be retrieved is used as a signal that
+ // certificate database is ready to be initialized in Lacros-Chrome.
+ void WaitForTpmTokenReady(GetCertDatabaseInfoCallback callback);
+ void OnTpmTokenReady(
+ std::unique_ptr<chromeos::TPMTokenInfoGetter> token_getter,
+ GetCertDatabaseInfoCallback callback,
+ base::Optional<chromeos::CryptohomeClient::TpmTokenInfo> token_info);
+
+ base::Optional<bool> is_tpm_token_ready_;
+
+ // This class supports any number of connections. This allows the client to
+ // have multiple, potentially thread-affine, remotes.
+ mojo::ReceiverSet<mojom::CertDatabase> receivers_;
+
+ base::WeakPtrFactory<CertDatabaseAsh> weak_factory_{this};
+};
+
+} // namespace crosapi
+
+#endif // CHROME_BROWSER_CHROMEOS_CROSAPI_CERT_DATABASE_ASH_H_
diff --git a/chrome/browser/lacros/cert_db_initializer.cc b/chrome/browser/lacros/cert_db_initializer.cc
new file mode 100644
index 0000000..8fb5fbc
--- /dev/null
+++ b/chrome/browser/lacros/cert_db_initializer.cc
@@ -0,0 +1,117 @@
+// 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 "chrome/browser/lacros/cert_db_initializer.h"
+
+#include "base/callback_forward.h"
+#include "base/check.h"
+#include "base/files/file_path.h"
+#include "base/memory/weak_ptr.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chromeos/crosapi/cpp/crosapi_constants.h"
+#include "chromeos/crosapi/mojom/cert_database.mojom.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "crypto/chaps_support.h"
+#include "crypto/nss_util.h"
+#include "crypto/nss_util_internal.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+class IdentityManagerObserver : public signin::IdentityManager::Observer {
+ public:
+ explicit IdentityManagerObserver(signin::IdentityManager* identity_manager)
+ : identity_manager_(identity_manager) {
+ DCHECK(identity_manager_);
+ identity_manager_->AddObserver(this);
+ }
+
+ IdentityManagerObserver(const IdentityManagerObserver&) = delete;
+ IdentityManagerObserver& operator==(const IdentityManagerObserver&) = delete;
+
+ ~IdentityManagerObserver() override {
+ identity_manager_->RemoveObserver(this);
+ }
+
+ void WaitForRefreshTokensLoaded(base::OnceClosure callback) {
+ DCHECK(callback_.is_null());
+ callback_ = std::move(callback);
+ }
+
+ private:
+ void OnRefreshTokensLoaded() override {
+ if (!callback_) {
+ return;
+ }
+ std::move(callback_).Run(); // NOTE: may delete `this`.
+ }
+
+ signin::IdentityManager* identity_manager_ = nullptr;
+ base::OnceClosure callback_;
+};
+
+// =============================================================================
+
+CertDbInitializer::CertDbInitializer(
+ mojo::Remote<crosapi::mojom::CertDatabase>& cert_database_remote,
+ Profile* profile)
+ : cert_database_remote_(cert_database_remote), profile_(profile) {}
+
+CertDbInitializer::~CertDbInitializer() = default;
+
+void CertDbInitializer::Start(signin::IdentityManager* identity_manager) {
+ DCHECK(identity_manager);
+ // TODO(crbug.com/1148300): This is temporary. Until ~2021
+ // `Profile::IsMainProfile()` method can return a false negative response if
+ // called before refresh tokens are loaded. This should be removed together
+ // with the entire usage of `IdentityManager` in this class when it is not the
+ // case anymore.
+ if (!identity_manager->AreRefreshTokensLoaded()) {
+ identity_manager_observer_ =
+ std::make_unique<IdentityManagerObserver>(identity_manager);
+ identity_manager_observer_->WaitForRefreshTokensLoaded(base::BindOnce(
+ &CertDbInitializer::OnRefreshTokensLoaded, weak_factory_.GetWeakPtr()));
+ return;
+ }
+ WaitForCertDbReady();
+}
+
+void CertDbInitializer::OnRefreshTokensLoaded() {
+ identity_manager_observer_.reset();
+ WaitForCertDbReady();
+}
+
+void CertDbInitializer::WaitForCertDbReady() {
+ if (!profile_->IsMainProfile()) {
+ return;
+ }
+
+ cert_database_remote_->GetCertDatabaseInfo(base::BindOnce(
+ &CertDbInitializer::OnCertDbInfoReceived, weak_factory_.GetWeakPtr()));
+}
+
+void CertDbInitializer::OnCertDbInfoReceived(
+ crosapi::mojom::GetCertDatabaseInfoResultPtr cert_db_info) {
+ if (!cert_db_info) {
+ LOG(WARNING) << "Certificate database is not accesible";
+ return;
+ }
+
+ crypto::EnsureNSSInit();
+
+ // There's no need to save the result of `LoadChaps()`, chaps will stay loaded
+ // and can be used anyway. Using the result only to determine success/failure.
+ if (cert_db_info->should_load_chaps && !crypto::LoadChaps()) {
+ LOG(ERROR) << "Failed to load chaps.";
+ return;
+ }
+
+ // The slot doesn't need to be saved either. `description` doesn't affect
+ // anything. `software_nss_db_path` file path should be already created by
+ // Ash-Chrome.
+ base::FilePath software_nss_db_path(cert_db_info->software_nss_db_path);
+ auto slot = crypto::OpenSoftwareNSSDB(software_nss_db_path,
+ /*description=*/"cert_db");
+ if (!slot) {
+ LOG(ERROR) << "Failed to open user certificate database";
+ }
+}
diff --git a/chrome/browser/lacros/cert_db_initializer.h b/chrome/browser/lacros/cert_db_initializer.h
new file mode 100644
index 0000000..62fc56d4
--- /dev/null
+++ b/chrome/browser/lacros/cert_db_initializer.h
@@ -0,0 +1,47 @@
+// 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 CHROME_BROWSER_LACROS_CERT_DB_INITIALIZER_H_
+#define CHROME_BROWSER_LACROS_CERT_DB_INITIALIZER_H_
+
+#include "base/memory/weak_ptr.h"
+#include "chromeos/crosapi/mojom/cert_database.mojom.h"
+#include "components/keyed_service/core/keyed_service.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "mojo/public/cpp/bindings/remote.h"
+
+class Profile;
+class IdentityManagerObserver;
+
+// Initializes certificate database in Lacros-Chrome.
+class CertDbInitializer : public KeyedService {
+ public:
+ CertDbInitializer(
+ mojo::Remote<crosapi::mojom::CertDatabase>& cert_database_remote,
+ Profile* profile);
+ ~CertDbInitializer() override;
+
+ void Start(signin::IdentityManager* identity_manager);
+
+ private:
+ // Starts the initialization. The first step is to wait for IdentityManager.
+
+ void OnRefreshTokensLoaded();
+
+ // Checks that the current profile is the main profile and, if yes, makes a
+ // mojo request to Ash-Chrome to get information about certificate database.
+ void WaitForCertDbReady();
+
+ // Checks from the result that the certificate database should be initialized.
+ // If yes, loads Chaps and opens user's certificate database.
+ void OnCertDbInfoReceived(
+ crosapi::mojom::GetCertDatabaseInfoResultPtr result);
+
+ mojo::Remote<crosapi::mojom::CertDatabase>& cert_database_remote_;
+ Profile* profile_ = nullptr;
+ std::unique_ptr<IdentityManagerObserver> identity_manager_observer_;
+ base::WeakPtrFactory<CertDbInitializer> weak_factory_{this};
+};
+
+#endif // CHROME_BROWSER_LACROS_CERT_DB_INITIALIZER_H_
diff --git a/chrome/browser/lacros/cert_db_initializer_factory.cc b/chrome/browser/lacros/cert_db_initializer_factory.cc
new file mode 100644
index 0000000..292c86f2
--- /dev/null
+++ b/chrome/browser/lacros/cert_db_initializer_factory.cc
@@ -0,0 +1,49 @@
+// 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 "chrome/browser/lacros/cert_db_initializer_factory.h"
+
+#include "chrome/browser/lacros/cert_db_initializer.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/identity_manager_factory.h"
+#include "chromeos/lacros/lacros_chrome_service_impl.h"
+#include "components/keyed_service/content/browser_context_dependency_manager.h"
+
+class Profile;
+
+// static
+CertDbInitializerFactory* CertDbInitializerFactory::GetInstance() {
+ static base::NoDestructor<CertDbInitializerFactory> factory;
+ return factory.get();
+}
+
+CertDbInitializerFactory::CertDbInitializerFactory()
+ : BrowserContextKeyedServiceFactory(
+ "CertDbInitializerFactory",
+ BrowserContextDependencyManager::GetInstance()) {
+ DependsOn(IdentityManagerFactory::GetInstance());
+}
+
+bool CertDbInitializerFactory::ServiceIsCreatedWithBrowserContext() const {
+ return true;
+}
+
+KeyedService* CertDbInitializerFactory::BuildServiceInstanceFor(
+ content::BrowserContext* context) const {
+ Profile* profile = Profile::FromBrowserContext(context);
+
+ if (!chromeos::LacrosChromeServiceImpl::Get()->IsCertDbAvailable()) {
+ return nullptr;
+ }
+
+ CertDbInitializer* result = new CertDbInitializer(
+ chromeos::LacrosChromeServiceImpl::Get()->cert_database_remote(),
+ profile);
+ // TODO(crbug.com/1145946): Enable certificate database initialization when
+ // the policy stack is ready (expected to happen before Feb 2021).
+ if (/* DISABLES CODE */ (false)) {
+ result->Start(IdentityManagerFactory::GetForProfile(profile));
+ }
+ return result;
+}
diff --git a/chrome/browser/lacros/cert_db_initializer_factory.h b/chrome/browser/lacros/cert_db_initializer_factory.h
new file mode 100644
index 0000000..f3dcb40
--- /dev/null
+++ b/chrome/browser/lacros/cert_db_initializer_factory.h
@@ -0,0 +1,27 @@
+// 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 CHROME_BROWSER_LACROS_CERT_DB_INITIALIZER_FACTORY_H_
+#define CHROME_BROWSER_LACROS_CERT_DB_INITIALIZER_FACTORY_H_
+
+#include "base/no_destructor.h"
+#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
+
+class CertDbInitializerFactory : public BrowserContextKeyedServiceFactory {
+ public:
+ static CertDbInitializerFactory* GetInstance();
+
+ private:
+ friend class base::NoDestructor<CertDbInitializerFactory>;
+
+ CertDbInitializerFactory();
+ ~CertDbInitializerFactory() override = default;
+
+ // BrowserStateKeyedServiceFactory
+ bool ServiceIsCreatedWithBrowserContext() const override;
+ KeyedService* BuildServiceInstanceFor(
+ content::BrowserContext* context) const override;
+};
+
+#endif // CHROME_BROWSER_LACROS_CERT_DB_INITIALIZER_FACTORY_H_
diff --git a/chrome/browser/net/nss_context_linux.cc b/chrome/browser/net/nss_context_linux.cc
index ce90909..2de6025 100644
--- a/chrome/browser/net/nss_context_linux.cc
+++ b/chrome/browser/net/nss_context_linux.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/net/nss_context.h"
+#include "build/chromeos_buildflags.h"
#include "content/public/browser/browser_thread.h"
#include "crypto/nss_util.h"
#include "net/cert/nss_cert_database.h"
@@ -18,6 +19,13 @@
// This initialization is not thread safe. This CHECK ensures that this code
// is only run on a single thread.
CHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+#if BUILDFLAG(IS_LACROS)
+ // TODO(crbug.com/1147032): remove the CHECK after the certificates settings
+ // page is updated.
+ CHECK(false) << "Currently disabled for Lacros-Chrome.";
+#endif
+
if (!g_nss_cert_database) {
// Linux has only a single persistent slot compared to ChromeOS's separate
// public and private slot.
diff --git a/chrome/browser/net/profile_network_context_service.cc b/chrome/browser/net/profile_network_context_service.cc
index 0b849227..0cc13c4 100644
--- a/chrome/browser/net/profile_network_context_service.cc
+++ b/chrome/browser/net/profile_network_context_service.cc
@@ -576,6 +576,15 @@
base::BindRepeating(&CreateCryptoModuleBlockingPasswordDelegate,
kCryptoModulePasswordClientAuth));
#elif defined(USE_NSS_CERTS)
+
+#if BUILDFLAG(IS_LACROS)
+ if (!profile_->IsMainProfile()) {
+ // TODO(crbug.com/1148298): return some cert store for secondary profiles in
+ // Lacros-Chrome.
+ return nullptr;
+ }
+#endif // BUILDFLAG(IS_LACROS)
+
return std::make_unique<net::ClientCertStoreNSS>(
base::BindRepeating(&CreateCryptoModuleBlockingPasswordDelegate,
kCryptoModulePasswordClientAuth));
diff --git a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
index 26e1cdd..172c27d 100644
--- a/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
+++ b/chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.cc
@@ -189,6 +189,10 @@
#include "chrome/browser/nearby_sharing/nearby_sharing_service_factory.h"
#endif
+#if BUILDFLAG(IS_LACROS)
+#include "chrome/browser/lacros/cert_db_initializer_factory.h"
+#endif
+
namespace chrome {
void AddProfilesExtraParts(ChromeBrowserMainParts* main_parts) {
@@ -432,6 +436,10 @@
#endif
WebDataServiceFactory::GetInstance();
webrtc_event_logging::WebRtcEventLogManagerKeyedServiceFactory::GetInstance();
+
+#if BUILDFLAG(IS_LACROS)
+ CertDbInitializerFactory::GetInstance();
+#endif
}
void ChromeBrowserMainExtraPartsProfiles::PreProfileInit() {
diff --git a/chrome/browser/resources/settings/route.js b/chrome/browser/resources/settings/route.js
index 0e3a27e..661c989 100644
--- a/chrome/browser/resources/settings/route.js
+++ b/chrome/browser/resources/settings/route.js
@@ -18,7 +18,9 @@
r.COOKIES = r.PRIVACY.createChild('/cookies');
r.SECURITY = r.PRIVACY.createChild('/security');
- // <if expr="use_nss_certs">
+ // TODO(crbug.com/1147032): The certificates settings page is temporarily
+ // disabled for Lacros-Chrome until a better solution is found.
+ // <if expr="use_nss_certs and not lacros">
r.CERTIFICATES = r.SECURITY.createChild('/certificates');
// </if>
diff --git a/chrome/browser/ssl/ssl_browsertest.cc b/chrome/browser/ssl/ssl_browsertest.cc
index c8b27efb..0cd324e 100644
--- a/chrome/browser/ssl/ssl_browsertest.cc
+++ b/chrome/browser/ssl/ssl_browsertest.cc
@@ -1994,6 +1994,11 @@
EXPECT_EQ(security_state::NONE, helper->GetSecurityLevel());
}
+// TODO(crbug.com/1148302): This class directly calls
+// `GetNSSCertDatabaseForProfile()` that causes crash at the moment and is never
+// called from Lacros-Chrome. This should be revisited when there is a solution
+// for the client certificates settings page on Lacros-Chrome.
+#if !BUILDFLAG(IS_LACROS)
#if defined(USE_NSS_CERTS)
class SSLUITestWithClientCert : public SSLUITestBase {
public:
@@ -2084,6 +2089,7 @@
EXPECT_TRUE(base::LowerCaseEqualsASCII(result, "pass"));
}
#endif // defined(USE_NSS_CERTS)
+#endif // !BUILDFLAG(IS_LACROS)
// A stub ClientCertStore that returns a FakeClientCertIdentity.
class ClientCertStoreStub : public net::ClientCertStore {
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 9033c2c4..7118b2d 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -701,8 +701,12 @@
return &NewWebUI<chromeos::BluetoothPairingDialogUI>;
if (url.host_piece() == chrome::kChromeUICellularSetupHost)
return &NewWebUI<chromeos::cellular_setup::CellularSetupDialogUI>;
+// TODO(crbug.com/1147032): The certificates settings page is temporarily
+// disabled for Lacros-Chrome until a better solution is found.
+#if !BUILDFLAG(IS_LACROS)
if (url.host_piece() == chrome::kChromeUICertificateManagerHost)
return &NewWebUI<chromeos::CertificateManagerDialogUI>;
+#endif // !BUILDFLAG(IS_LACROS)
if (base::FeatureList::IsEnabled(
chromeos::features::kConnectivityDiagnosticsWebUi) &&
url.host_piece() == chromeos::kChromeUIConnectivityDiagnosticsHost) {
diff --git a/chrome/browser/ui/webui/settings/settings_ui.cc b/chrome/browser/ui/webui/settings/settings_ui.cc
index b9e4325b..10e8bc6d 100644
--- a/chrome/browser/ui/webui/settings/settings_ui.cc
+++ b/chrome/browser/ui/webui/settings/settings_ui.cc
@@ -166,6 +166,9 @@
AddSettingsPageUIHandler(std::make_unique<AppearanceHandler>(web_ui));
+// TODO(crbug.com/1147032): The certificates settings page is temporarily
+// disabled for Lacros-Chrome until a better solution is found.
+#if !BUILDFLAG(IS_LACROS)
#if defined(USE_NSS_CERTS)
AddSettingsPageUIHandler(
std::make_unique<certificate_manager::CertificatesHandler>());
@@ -176,7 +179,8 @@
AddSettingsPageUIHandler(
chromeos::cert_provisioning::CertificateProvisioningUiHandler::
CreateForProfile(profile));
-#endif
+#endif // defined(OS_CHROMEOS)
+#endif // !BUILDFLAG(IS_LACROS)
AddSettingsPageUIHandler(std::make_unique<AccessibilityMainHandler>());
AddSettingsPageUIHandler(std::make_unique<BrowserLifetimeHandler>());
diff --git a/chrome/test/data/webui/settings/security_page_test.js b/chrome/test/data/webui/settings/security_page_test.js
index e5fc850..696fc3b8 100644
--- a/chrome/test/data/webui/settings/security_page_test.js
+++ b/chrome/test/data/webui/settings/security_page_test.js
@@ -3,7 +3,7 @@
// found in the LICENSE file.
// clang-format off
-import {isMac, isWindows} from 'chrome://resources/js/cr.m.js';
+import {isLacros, isMac, isWindows} from 'chrome://resources/js/cr.m.js';
import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {SafeBrowsingSetting} from 'chrome://settings/lazy_load.js';
@@ -87,12 +87,18 @@
assertTrue(page.$$('#safeBrowsingStandard').expanded);
});
- test('LogManageCerfificatesClick', async function() {
- page.$$('#manageCertificates').click();
- const result =
- await testMetricsBrowserProxy.whenCalled('recordSettingsPageHistogram');
- assertEquals(PrivacyElementInteractions.MANAGE_CERTIFICATES, result);
- });
+ // TODO(crbug.com/1148302): This class directly calls
+ // `GetNSSCertDatabaseForProfile()` that causes crash at the moment and is
+ // never called from Lacros-Chrome. This should be revisited when there is a
+ // solution for the client certificates settings page on Lacros-Chrome.
+ if (!isLacros) {
+ test('LogManageCerfificatesClick', async function() {
+ page.$$('#manageCertificates').click();
+ const result = await testMetricsBrowserProxy.whenCalled(
+ 'recordSettingsPageHistogram');
+ assertEquals(PrivacyElementInteractions.MANAGE_CERTIFICATES, result);
+ });
+ }
test('ManageSecurityKeysSubpageVisible', function() {
assertTrue(isChildVisible(page, '#security-keys-subpage-trigger'));
diff --git a/chromeos/crosapi/mojom/BUILD.gn b/chromeos/crosapi/mojom/BUILD.gn
index d1f9ee8..d0105e4 100644
--- a/chromeos/crosapi/mojom/BUILD.gn
+++ b/chromeos/crosapi/mojom/BUILD.gn
@@ -8,6 +8,7 @@
sources = [
"account_manager.mojom",
"bitmap.mojom",
+ "cert_database.mojom",
"crosapi.mojom",
"feedback.mojom",
"file_manager.mojom",
diff --git a/chromeos/crosapi/mojom/cert_database.mojom b/chromeos/crosapi/mojom/cert_database.mojom
new file mode 100644
index 0000000..c33f84fe
--- /dev/null
+++ b/chromeos/crosapi/mojom/cert_database.mojom
@@ -0,0 +1,19 @@
+// 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.
+
+module crosapi.mojom;
+
+[Stable, Extensible]
+struct GetCertDatabaseInfoResult {
+ string software_nss_db_path@0;
+ bool should_load_chaps@1;
+};
+
+// This interface is implemented by Ash-Chrome.
+[Stable, Uuid="e7f924bf-0e10-4aef-98d3-6e2f216dc914"]
+interface CertDatabase {
+ // Waits until Ash-Chrome finishes certificate database initialization and
+ // returns necessary data for Lacros-Chrome to connect to it.
+ GetCertDatabaseInfo@0() => (GetCertDatabaseInfoResult? result);
+};
diff --git a/chromeos/crosapi/mojom/crosapi.mojom b/chromeos/crosapi/mojom/crosapi.mojom
index 299610ca..e0af8d3 100644
--- a/chromeos/crosapi/mojom/crosapi.mojom
+++ b/chromeos/crosapi/mojom/crosapi.mojom
@@ -5,6 +5,7 @@
module crosapi.mojom;
import "chromeos/crosapi/mojom/account_manager.mojom";
+import "chromeos/crosapi/mojom/cert_database.mojom";
import "chromeos/crosapi/mojom/feedback.mojom";
import "chromeos/crosapi/mojom/file_manager.mojom";
import "chromeos/crosapi/mojom/keystore_service.mojom";
@@ -39,8 +40,8 @@
// milestone when you added it, to help us reason about compatibility between
// lacros-chrome and older ash-chrome binaries.
//
-// Next version: 7
-// Next method id: 12
+// Next version: 8
+// Next method id: 13
[Stable, Uuid="8b79c34f-2bf8-4499-979a-b17cac522c1e"]
interface AshChromeService {
// Binds Chrome OS Account Manager for Identity management.
@@ -96,6 +97,11 @@
[MinVersion=6] BindMediaSessionAudioFocusDebug@11(
pending_receiver<media_session.mojom.AudioFocusManagerDebug> receiver);
+ // Binds the CertDatabase interface for initializing certificate database in
+ // Lacros-Chrome.
+ // Added in M89.
+ [MinVersion=7] BindCertDatabase@12(pending_receiver<CertDatabase> receiver);
+
// Passes generic lacros information such as lacros version, etc into ash
// in |lacros_info| during startup.
// Added in M87.
diff --git a/chromeos/lacros/lacros_chrome_service_impl.cc b/chromeos/lacros/lacros_chrome_service_impl.cc
index e32e3103..ce91c97 100644
--- a/chromeos/lacros/lacros_chrome_service_impl.cc
+++ b/chromeos/lacros/lacros_chrome_service_impl.cc
@@ -10,6 +10,7 @@
#include "base/logging.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
+#include "chromeos/crosapi/mojom/crosapi.mojom.h"
#include "chromeos/lacros/lacros_chrome_service_delegate.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "url/gurl.h"
@@ -164,6 +165,12 @@
ash_chrome_service_->BindFeedback(std::move(pending_receiver));
}
+ void BindCertDbReceiver(
+ mojo::PendingReceiver<crosapi::mojom::CertDatabase> pending_receiver) {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+ ash_chrome_service_->BindCertDatabase(std::move(pending_receiver));
+ }
+
void OnLacrosStartup(crosapi::mojom::LacrosInfoPtr lacros_info) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
ash_chrome_service_->OnLacrosStartup(std::move(lacros_info));
@@ -355,6 +362,15 @@
feedback_remote_.BindNewPipeAndPassReceiver()));
}
+ if (IsCertDbAvailable()) {
+ never_blocking_sequence_->PostTask(
+ FROM_HERE,
+ base::BindOnce(
+ &LacrosChromeServiceNeverBlockingState::BindCertDbReceiver,
+ weak_sequenced_state_,
+ cert_database_remote_.BindNewPipeAndPassReceiver()));
+ }
+
if (IsOnLacrosStartupAvailable()) {
never_blocking_sequence_->PostTask(
FROM_HERE,
@@ -500,6 +516,13 @@
weak_sequenced_state_, std::move(remote)));
}
+bool LacrosChromeServiceImpl::IsCertDbAvailable() {
+ base::Optional<uint32_t> version = AshChromeServiceVersion();
+ return version &&
+ version.value() >=
+ AshChromeService::MethodMinVersions::kBindCertDatabaseMinVersion;
+}
+
bool LacrosChromeServiceImpl::IsOnLacrosStartupAvailable() {
base::Optional<uint32_t> version = AshChromeServiceVersion();
return version &&
diff --git a/chromeos/lacros/lacros_chrome_service_impl.h b/chromeos/lacros/lacros_chrome_service_impl.h
index de5f71b..b949e812 100644
--- a/chromeos/lacros/lacros_chrome_service_impl.h
+++ b/chromeos/lacros/lacros_chrome_service_impl.h
@@ -14,6 +14,7 @@
#include "base/sequence_checker.h"
#include "base/sequenced_task_runner.h"
#include "chromeos/crosapi/mojom/account_manager.mojom.h"
+#include "chromeos/crosapi/mojom/cert_database.mojom.h"
#include "chromeos/crosapi/mojom/crosapi.mojom.h"
#include "chromeos/crosapi/mojom/feedback.mojom.h"
#include "chromeos/crosapi/mojom/keystore_service.mojom.h"
@@ -161,6 +162,15 @@
void BindMediaControllerManager(
mojo::PendingReceiver<media_session::mojom::MediaControllerManager>
remote);
+ // cert_database_remote() can only be used when this method returns true;
+ bool IsCertDbAvailable();
+
+ // This must be called on the affine sequence.
+ mojo::Remote<crosapi::mojom::CertDatabase>& cert_database_remote() {
+ DCHECK_CALLED_ON_VALID_SEQUENCE(affine_sequence_checker_);
+ DCHECK(IsCertDbAvailable());
+ return cert_database_remote_;
+ }
// file_manager_remote() can only be used if this method returns true.
bool IsFileManagerAvailable();
@@ -251,6 +261,7 @@
mojo::Remote<crosapi::mojom::SelectFile> select_file_remote_;
mojo::Remote<device::mojom::HidManager> hid_manager_remote_;
mojo::Remote<crosapi::mojom::Feedback> feedback_remote_;
+ mojo::Remote<crosapi::mojom::CertDatabase> cert_database_remote_;
mojo::Remote<crosapi::mojom::KeystoreService> keystore_service_remote_;
mojo::Remote<crosapi::mojom::FileManager> file_manager_remote_;
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.
diff --git a/net/cert/nss_cert_database.cc b/net/cert/nss_cert_database.cc
index e582b57..e880b1ab 100644
--- a/net/cert/nss_cert_database.cc
+++ b/net/cert/nss_cert_database.cc
@@ -32,6 +32,10 @@
#include "net/third_party/mozilla_security_manager/nsNSSCertificateDB.h"
#include "net/third_party/mozilla_security_manager/nsPKCS12Blob.h"
+#if defined(OS_CHROMEOS) || BUILDFLAG(IS_LACROS)
+#include "crypto/chaps_support.h"
+#endif
+
// PSM = Mozilla's Personal Security Manager.
namespace psm = mozilla_security_manager;
@@ -436,7 +440,7 @@
if (!slot || !PK11_IsHW(slot))
return false;
-#if BUILDFLAG(IS_CHROMEOS_ASH)
+#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_LACROS)
// Chaps announces PK11_IsHW(slot) for all slots. However, it is possible for
// a key in chaps to be not truly hardware-backed, either because it has been
// requested to be software-backed, or because the TPM does not support the