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}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
1340989ccd
commit
1c50ac9546
chrome
browser
BUILD.gn
chromeos
BUILD.gn
crosapi
lacros
cert_db_initializer.cccert_db_initializer.hcert_db_initializer_factory.cccert_db_initializer_factory.h
net
profiles
resources
settings
ssl
ui
webui
test
data
webui
settings
chromeos
crosapi
lacros
crypto
net/cert
@ -4559,6 +4559,10 @@ static_library("browser") {
|
||||
"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",
|
||||
|
@ -1041,6 +1041,8 @@ source_set("chromeos") {
|
||||
"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",
|
||||
|
@ -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 @@ namespace crosapi {
|
||||
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 @@ void AshChromeServiceImpl::BindMediaSessionAudioFocusDebug(
|
||||
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);
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
namespace crosapi {
|
||||
|
||||
class AccountManagerAsh;
|
||||
class CertDatabaseAsh;
|
||||
class FeedbackAsh;
|
||||
class FileManagerAsh;
|
||||
class KeystoreServiceAsh;
|
||||
@ -45,6 +46,8 @@ class AshChromeServiceImpl : public mojom::AshChromeService {
|
||||
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 @@ class AshChromeServiceImpl : public mojom::AshChromeService {
|
||||
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
|
||||
|
@ -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 @@ bool IsLacrosWindow(const aura::Window* window) {
|
||||
|
||||
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);
|
||||
|
115
chrome/browser/chromeos/crosapi/cert_database_ash.cc
Normal file
115
chrome/browser/chromeos/crosapi/cert_database_ash.cc
Normal file
@ -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
|
70
chrome/browser/chromeos/crosapi/cert_database_ash.h
Normal file
70
chrome/browser/chromeos/crosapi/cert_database_ash.h
Normal file
@ -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_
|
117
chrome/browser/lacros/cert_db_initializer.cc
Normal file
117
chrome/browser/lacros/cert_db_initializer.cc
Normal file
@ -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";
|
||||
}
|
||||
}
|
47
chrome/browser/lacros/cert_db_initializer.h
Normal file
47
chrome/browser/lacros/cert_db_initializer.h
Normal file
@ -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_
|
49
chrome/browser/lacros/cert_db_initializer_factory.cc
Normal file
49
chrome/browser/lacros/cert_db_initializer_factory.cc
Normal file
@ -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;
|
||||
}
|
27
chrome/browser/lacros/cert_db_initializer_factory.h
Normal file
27
chrome/browser/lacros/cert_db_initializer_factory.h
Normal file
@ -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_
|
@ -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 @@ net::NSSCertDatabase* GetNSSCertDatabaseForResourceContext(
|
||||
// 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.
|
||||
|
@ -576,6 +576,15 @@ ProfileNetworkContextService::CreateClientCertStore() {
|
||||
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));
|
||||
|
@ -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 @@ void ChromeBrowserMainExtraPartsProfiles::
|
||||
#endif
|
||||
WebDataServiceFactory::GetInstance();
|
||||
webrtc_event_logging::WebRtcEventLogManagerKeyedServiceFactory::GetInstance();
|
||||
|
||||
#if BUILDFLAG(IS_LACROS)
|
||||
CertDbInitializerFactory::GetInstance();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ChromeBrowserMainExtraPartsProfiles::PreProfileInit() {
|
||||
|
@ -18,7 +18,9 @@ function addPrivacyChildRoutes(r) {
|
||||
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>
|
||||
|
||||
|
@ -1994,6 +1994,11 @@ IN_PROC_BROWSER_TEST_F(SSLUITestWithHttpDangerous, MarkBlobAsNonSecure) {
|
||||
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 @@ IN_PROC_BROWSER_TEST_F(SSLUITestWithClientCert, TestWSSClientCert) {
|
||||
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 {
|
||||
|
@ -701,8 +701,12 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI* web_ui,
|
||||
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) {
|
||||
|
@ -166,6 +166,9 @@ SettingsUI::SettingsUI(content::WebUI* web_ui)
|
||||
|
||||
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 @@ SettingsUI::SettingsUI(content::WebUI* web_ui)
|
||||
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>());
|
||||
|
@ -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 @@ suite('CrSettingsSecurityPageTest', function() {
|
||||
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'));
|
||||
|
@ -8,6 +8,7 @@ mojom("mojom") {
|
||||
sources = [
|
||||
"account_manager.mojom",
|
||||
"bitmap.mojom",
|
||||
"cert_database.mojom",
|
||||
"crosapi.mojom",
|
||||
"feedback.mojom",
|
||||
"file_manager.mojom",
|
||||
|
19
chromeos/crosapi/mojom/cert_database.mojom
Normal file
19
chromeos/crosapi/mojom/cert_database.mojom
Normal file
@ -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);
|
||||
};
|
@ -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 @@ struct LacrosInfo {
|
||||
// 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 @@ interface AshChromeService {
|
||||
[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.
|
||||
|
@ -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 @@ class LacrosChromeServiceNeverBlockingState
|
||||
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 @@ void LacrosChromeServiceImpl::BindReceiver(
|
||||
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 @@ void LacrosChromeServiceImpl::BindMediaControllerManager(
|
||||
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 &&
|
||||
|
@ -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 @@ class COMPONENT_EXPORT(CHROMEOS_LACROS) LacrosChromeServiceImpl {
|
||||
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 @@ class COMPONENT_EXPORT(CHROMEOS_LACROS) LacrosChromeServiceImpl {
|
||||
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_;
|
||||
|
||||
|
@ -123,6 +123,13 @@ component("crypto") {
|
||||
sources += [ "nss_util_chromeos.cc" ]
|
||||
}
|
||||
|
||||
if (is_chromeos || is_lacros) {
|
||||
sources += [
|
||||
"chaps_support.cc",
|
||||
"chaps_support.h",
|
||||
]
|
||||
}
|
||||
|
||||
defines = [ "CRYPTO_IMPLEMENTATION" ]
|
||||
|
||||
if (is_nacl) {
|
||||
|
88
crypto/chaps_support.cc
Normal file
88
crypto/chaps_support.cc
Normal file
@ -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
|
22
crypto/chaps_support.h
Normal file
22
crypto/chaps_support.h
Normal file
@ -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_
|
@ -35,13 +35,14 @@ namespace crypto {
|
||||
|
||||
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 @@ base::FilePath GetDefaultConfigDirectory() {
|
||||
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 @@ base::FilePath GetDefaultConfigDirectory() {
|
||||
// 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 @@ class NSSInitSingleton {
|
||||
// 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());
|
||||
|
@ -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 @@ namespace {
|
||||
|
||||
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 @@ class ChromeOSUserData {
|
||||
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 @@ class ChromeOSTokenManager {
|
||||
// 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 @@ class ChromeOSTokenManager {
|
||||
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 @@ base::LazyInstance<ChromeOSTokenManager>::Leaky g_token_manager =
|
||||
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 @@ void SetPrivateSoftwareSlotForChromeOSUserForTesting(ScopedPK11Slot slot) {
|
||||
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
|
||||
|
@ -44,6 +44,11 @@ class CRYPTO_EXPORT AutoSECMODListReadLock {
|
||||
};
|
||||
|
||||
#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 CloseChromeOSUserForTesting(
|
||||
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.
|
||||
|
@ -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 @@ bool NSSCertDatabase::IsHardwareBacked(const CERTCertificate* cert) {
|
||||
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
|
||||
|
Reference in New Issue
Block a user