Commit 33a083b0 authored by Michael Ershov's avatar Michael Ershov Committed by Chromium LUCI CQ

[Lacros] Suspend web requests until client certs are ready

This CL makes web requests that require client certificates
wait until the client certificate storage is loaded. Without
that they proceed as if there is no suitable certificate and
the browser doesn't retry to find a certificate for the same
connection (including on the page refresh).

Add a method to CertDbInitializer that allows to wait for the
initialization to finish. Wrap the base ClientCertStoreNSS
class into a new class and wait in it for the initialization to
finish before continuing the retrieval of client certificates.

Bug: 1145946
Change-Id: I1f5ab87d93c3eedea75f86a9769dcaddb0cd8404
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2574757Reviewed-by: default avatarMichael Ershov <miersh@google.com>
Reviewed-by: default avatarPavol Marko <pmarko@chromium.org>
Reviewed-by: default avatarRyan Sleevi <rsleevi@chromium.org>
Reviewed-by: default avatarErik Chen <erikchen@chromium.org>
Commit-Queue: Michael Ershov <miersh@google.com>
Cr-Commit-Position: refs/heads/master@{#836434}
parent 24289623
...@@ -4580,6 +4580,8 @@ static_library("browser") { ...@@ -4580,6 +4580,8 @@ static_library("browser") {
"lacros/cert_db_initializer_factory.h", "lacros/cert_db_initializer_factory.h",
"lacros/cert_db_initializer_impl.cc", "lacros/cert_db_initializer_impl.cc",
"lacros/cert_db_initializer_impl.h", "lacros/cert_db_initializer_impl.h",
"lacros/client_cert_store_lacros.cc",
"lacros/client_cert_store_lacros.h",
"lacros/feedback_util.cc", "lacros/feedback_util.cc",
"lacros/feedback_util.h", "lacros/feedback_util.h",
"lacros/immersive_context_lacros.cc", "lacros/immersive_context_lacros.cc",
......
...@@ -5,10 +5,22 @@ ...@@ -5,10 +5,22 @@
#ifndef CHROME_BROWSER_LACROS_CERT_DB_INITIALIZER_H_ #ifndef CHROME_BROWSER_LACROS_CERT_DB_INITIALIZER_H_
#define CHROME_BROWSER_LACROS_CERT_DB_INITIALIZER_H_ #define CHROME_BROWSER_LACROS_CERT_DB_INITIALIZER_H_
#include "base/callback.h"
#include "base/callback_list.h"
class CertDbInitializer { class CertDbInitializer {
public: public:
// TODO(miersh): implement `WaitUntilReady`. using ReadyCallback = base::OnceCallback<void(bool is_success)>;
// virtual void WaitUntilReady() = 0;
virtual ~CertDbInitializer() = default;
// Registers `callback` to be notified once initialization is complete.
// If initialization has already been completed, `callback` will be
// synchronously invoked and an empty subscription returned; otherwise,
// `callback` will be invoked when initialization is completed, as long
// as the subscription is still live.
virtual base::CallbackListSubscription WaitUntilReady(
ReadyCallback callback) = 0;
}; };
#endif // CHROME_BROWSER_LACROS_CERT_DB_INITIALIZER_H_ #endif // CHROME_BROWSER_LACROS_CERT_DB_INITIALIZER_H_
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "chromeos/lacros/lacros_chrome_service_impl.h" #include "chromeos/lacros/lacros_chrome_service_impl.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/keyed_service/content/browser_context_dependency_manager.h"
class CertDbInitializer;
class Profile; class Profile;
// static // static
...@@ -18,6 +19,13 @@ CertDbInitializerFactory* CertDbInitializerFactory::GetInstance() { ...@@ -18,6 +19,13 @@ CertDbInitializerFactory* CertDbInitializerFactory::GetInstance() {
return factory.get(); return factory.get();
} }
// static
CertDbInitializer* CertDbInitializerFactory::GetForProfileIfExists(
Profile* profile) {
return static_cast<CertDbInitializerImpl*>(
GetInstance()->GetServiceForBrowserContext(profile, /*create=*/false));
}
CertDbInitializerFactory::CertDbInitializerFactory() CertDbInitializerFactory::CertDbInitializerFactory()
: BrowserContextKeyedServiceFactory( : BrowserContextKeyedServiceFactory(
"CertDbInitializerFactory", "CertDbInitializerFactory",
......
...@@ -8,9 +8,13 @@ ...@@ -8,9 +8,13 @@
#include "base/no_destructor.h" #include "base/no_destructor.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h" #include "components/keyed_service/content/browser_context_keyed_service_factory.h"
class CertDbInitializer;
class Profile;
class CertDbInitializerFactory : public BrowserContextKeyedServiceFactory { class CertDbInitializerFactory : public BrowserContextKeyedServiceFactory {
public: public:
static CertDbInitializerFactory* GetInstance(); static CertDbInitializerFactory* GetInstance();
static CertDbInitializer* GetForProfileIfExists(Profile* profile);
private: private:
friend class base::NoDestructor<CertDbInitializerFactory>; friend class base::NoDestructor<CertDbInitializerFactory>;
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "chromeos/crosapi/cpp/crosapi_constants.h" #include "chromeos/crosapi/cpp/crosapi_constants.h"
#include "chromeos/crosapi/mojom/cert_database.mojom.h" #include "chromeos/crosapi/mojom/cert_database.mojom.h"
#include "components/signin/public/identity_manager/identity_manager.h" #include "components/signin/public/identity_manager/identity_manager.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "crypto/chaps_support.h" #include "crypto/chaps_support.h"
#include "crypto/nss_util.h" #include "crypto/nss_util.h"
...@@ -23,7 +24,7 @@ ...@@ -23,7 +24,7 @@
namespace { namespace {
void InitializeCertDbOnWorkerThread(bool should_load_chaps, bool InitializeCertDbOnWorkerThread(bool should_load_chaps,
base::FilePath software_nss_db_path) { base::FilePath software_nss_db_path) {
crypto::EnsureNSSInit(); crypto::EnsureNSSInit();
...@@ -41,7 +42,7 @@ void InitializeCertDbOnWorkerThread(bool should_load_chaps, ...@@ -41,7 +42,7 @@ void InitializeCertDbOnWorkerThread(bool should_load_chaps,
// success/failure. // success/failure.
if (!crypto::LoadChaps()) { if (!crypto::LoadChaps()) {
LOG(ERROR) << "Failed to load chaps."; LOG(ERROR) << "Failed to load chaps.";
return; return false;
} }
} }
...@@ -52,10 +53,10 @@ void InitializeCertDbOnWorkerThread(bool should_load_chaps, ...@@ -52,10 +53,10 @@ void InitializeCertDbOnWorkerThread(bool should_load_chaps,
/*description=*/"cert_db"); /*description=*/"cert_db");
if (!slot) { if (!slot) {
LOG(ERROR) << "Failed to open user certificate database"; LOG(ERROR) << "Failed to open user certificate database";
return; return false;
} }
return; return true;
} }
} // namespace } // namespace
...@@ -101,10 +102,13 @@ CertDbInitializerImpl::CertDbInitializerImpl(Profile* profile) ...@@ -101,10 +102,13 @@ CertDbInitializerImpl::CertDbInitializerImpl(Profile* profile)
CertDbInitializerImpl::~CertDbInitializerImpl() { CertDbInitializerImpl::~CertDbInitializerImpl() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// In case the initialization didn't finish, notify waiting observers.
OnCertDbInitializationFinished(false);
} }
void CertDbInitializerImpl::Start(signin::IdentityManager* identity_manager) { void CertDbInitializerImpl::Start(signin::IdentityManager* identity_manager) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(identity_manager); DCHECK(identity_manager);
// TODO(crbug.com/1148300): This is temporary. Until ~2021 // TODO(crbug.com/1148300): This is temporary. Until ~2021
// `Profile::IsMainProfile()` method can return a false negative response if // `Profile::IsMainProfile()` method can return a false negative response if
...@@ -122,6 +126,18 @@ void CertDbInitializerImpl::Start(signin::IdentityManager* identity_manager) { ...@@ -122,6 +126,18 @@ void CertDbInitializerImpl::Start(signin::IdentityManager* identity_manager) {
WaitForCertDbReady(); WaitForCertDbReady();
} }
base::CallbackListSubscription CertDbInitializerImpl::WaitUntilReady(
ReadyCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (is_ready_.has_value()) {
std::move(callback).Run(is_ready_.value());
return {};
}
return callbacks_.Add(std::move(callback));
}
void CertDbInitializerImpl::OnRefreshTokensLoaded() { void CertDbInitializerImpl::OnRefreshTokensLoaded() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
identity_manager_observer_.reset(); identity_manager_observer_.reset();
...@@ -132,6 +148,7 @@ void CertDbInitializerImpl::WaitForCertDbReady() { ...@@ -132,6 +148,7 @@ void CertDbInitializerImpl::WaitForCertDbReady() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!profile_->IsMainProfile()) { if (!profile_->IsMainProfile()) {
OnCertDbInitializationFinished(false);
return; return;
} }
...@@ -148,14 +165,23 @@ void CertDbInitializerImpl::OnCertDbInfoReceived( ...@@ -148,14 +165,23 @@ void CertDbInitializerImpl::OnCertDbInfoReceived(
if (!cert_db_info) { if (!cert_db_info) {
LOG(WARNING) << "Certificate database is not accesible"; LOG(WARNING) << "Certificate database is not accesible";
OnCertDbInitializationFinished(false);
return; return;
} }
base::ThreadPool::PostTask( base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT, {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
base::BindOnce(&InitializeCertDbOnWorkerThread, base::BindOnce(&InitializeCertDbOnWorkerThread,
cert_db_info->should_load_chaps, cert_db_info->should_load_chaps,
base::FilePath(cert_db_info->software_nss_db_path))); base::FilePath(cert_db_info->software_nss_db_path)),
base::BindOnce(&CertDbInitializerImpl::OnCertDbInitializationFinished,
weak_factory_.GetWeakPtr()));
}
void CertDbInitializerImpl::OnCertDbInitializationFinished(bool is_success) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
callbacks_.Notify(is_success);
is_ready_ = is_success;
} }
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_LACROS_CERT_DB_INITIALIZER_IMPL_H_ #ifndef CHROME_BROWSER_LACROS_CERT_DB_INITIALIZER_IMPL_H_
#define CHROME_BROWSER_LACROS_CERT_DB_INITIALIZER_IMPL_H_ #define CHROME_BROWSER_LACROS_CERT_DB_INITIALIZER_IMPL_H_
#include "base/callback_list.h"
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "chrome/browser/lacros/cert_db_initializer.h" #include "chrome/browser/lacros/cert_db_initializer.h"
#include "chromeos/crosapi/mojom/cert_database.mojom.h" #include "chromeos/crosapi/mojom/cert_database.mojom.h"
...@@ -28,6 +29,10 @@ class CertDbInitializerImpl : public CertDbInitializer, public KeyedService { ...@@ -28,6 +29,10 @@ class CertDbInitializerImpl : public CertDbInitializer, public KeyedService {
// IdentityManager. // IdentityManager.
void Start(signin::IdentityManager* identity_manager); void Start(signin::IdentityManager* identity_manager);
// CertDbInitializer
base::CallbackListSubscription WaitUntilReady(
ReadyCallback callback) override;
private: private:
// It is called when IdentityManager is ready. // It is called when IdentityManager is ready.
void OnRefreshTokensLoaded(); void OnRefreshTokensLoaded();
...@@ -41,10 +46,17 @@ class CertDbInitializerImpl : public CertDbInitializer, public KeyedService { ...@@ -41,10 +46,17 @@ class CertDbInitializerImpl : public CertDbInitializer, public KeyedService {
void OnCertDbInfoReceived( void OnCertDbInfoReceived(
crosapi::mojom::GetCertDatabaseInfoResultPtr result); crosapi::mojom::GetCertDatabaseInfoResultPtr result);
// This class is `KeyedService` based on the `Profile`. An instance is // Recieves initialization result from a worker thread and notifies observers
// about the result.
void OnCertDbInitializationFinished(bool is_success);
// This class is a `KeyedService` based on the `Profile`. An instance is
// created together with a new profile and never outlives it.` // created together with a new profile and never outlives it.`
Profile* profile_ = nullptr; Profile* profile_ = nullptr;
std::unique_ptr<IdentityManagerObserver> identity_manager_observer_; std::unique_ptr<IdentityManagerObserver> identity_manager_observer_;
base::Optional<bool> is_ready_;
base::OnceCallbackList<ReadyCallback::RunType> callbacks_;
base::WeakPtrFactory<CertDbInitializerImpl> weak_factory_{this}; base::WeakPtrFactory<CertDbInitializerImpl> weak_factory_{this};
}; };
......
// 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/client_cert_store_lacros.h"
#include "base/memory/scoped_refptr.h"
#include "chrome/browser/lacros/cert_db_initializer.h"
#include "net/ssl/client_cert_store_nss.h"
#include "net/ssl/ssl_cert_request_info.h"
ClientCertStoreLacros::ClientCertStoreLacros(
CertDbInitializer* cert_db_initializer,
std::unique_ptr<net::ClientCertStore> underlying_store)
: cert_db_initializer_(cert_db_initializer),
underlying_store_(std::move(underlying_store)) {
DCHECK(underlying_store_);
DCHECK(cert_db_initializer_);
WaitForCertDb();
}
ClientCertStoreLacros::~ClientCertStoreLacros() = default;
void ClientCertStoreLacros::GetClientCerts(
const net::SSLCertRequestInfo& cert_request_info,
ClientCertListCallback callback) {
if (!are_certs_loaded_) {
pending_requests_.push_back(std::make_pair(
WrapRefCounted(&cert_request_info), std::move(callback)));
return;
}
underlying_store_->GetClientCerts(cert_request_info, std::move(callback));
}
void ClientCertStoreLacros::WaitForCertDb() {
wait_subscription_ = cert_db_initializer_->WaitUntilReady(base::BindOnce(
&ClientCertStoreLacros::OnCertDbReady, weak_factory_.GetWeakPtr()));
}
void ClientCertStoreLacros::OnCertDbReady(bool /*is_cert_db_ready*/) {
// Ignore the initialization result. Even if it failed, some certificates
// could be accessible.
// Ensure any new requests (e.g. that result from invoking the
// callbacks) aren't queued.
are_certs_loaded_ = true;
// Move the pending requests to the stack, since `this` may
// be deleted by the last request callback.
decltype(pending_requests_) local_requests;
std::swap(pending_requests_, local_requests);
// Dispatch all the queued requests.
for (auto& request : local_requests) {
underlying_store_->GetClientCerts(*request.first,
std::move(request.second));
}
}
// 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_CLIENT_CERT_STORE_LACROS_H_
#define CHROME_BROWSER_LACROS_CLIENT_CERT_STORE_LACROS_H_
#include "base/callback_list.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "net/ssl/client_cert_store.h"
#include "net/ssl/client_cert_store_nss.h"
class CertDbInitializer;
// Provides client certs for Lacros-Chrome.
// Client certificates may not be available during initialization, as
// Lacros-Chrome needs to get configuration data from Ash-Chrome for the
// user. ClientCertStoreLacros will queue requests for client certificates
// until CertDbInitializer has completed initializing, and then dispatch
// requests to the underlying ClientCertStore.
class ClientCertStoreLacros final : public net::ClientCertStore {
public:
ClientCertStoreLacros(CertDbInitializer* cert_db_initializer,
std::unique_ptr<net::ClientCertStore> underlying_store);
ClientCertStoreLacros(const ClientCertStoreLacros&) = delete;
ClientCertStoreLacros& operator=(ClientCertStoreLacros&) = delete;
~ClientCertStoreLacros() override;
// net::ClientCertStore
void GetClientCerts(const net::SSLCertRequestInfo& cert_request_info,
ClientCertListCallback callback) override;
private:
using RequestQueue =
std::vector<std::pair<scoped_refptr<const net::SSLCertRequestInfo>,
ClientCertListCallback>>;
void WaitForCertDb();
void OnCertDbReady(bool is_cert_db_ready);
bool are_certs_loaded_ = false;
CertDbInitializer* cert_db_initializer_ = nullptr;
base::CallbackListSubscription wait_subscription_;
RequestQueue pending_requests_;
std::unique_ptr<net::ClientCertStore> underlying_store_;
base::WeakPtrFactory<ClientCertStoreLacros> weak_factory_{this};
};
#endif // CHROME_BROWSER_LACROS_CLIENT_CERT_STORE_LACROS_H_
// 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/client_cert_store_lacros.h"
#include <memory>
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "chrome/browser/lacros/cert_db_initializer.h"
#include "content/public/test/browser_task_environment.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::test::RunOnceCallback;
using testing::_;
using testing::Invoke;
namespace {
class MockClientCertStore : public net::ClientCertStore {
public:
MOCK_METHOD(void,
GetClientCerts,
(const net::SSLCertRequestInfo& cert_request_info,
ClientCertListCallback callback));
};
class MockCertDbInitializer : public CertDbInitializer {
public:
MOCK_METHOD(base::CallbackListSubscription,
WaitUntilReady,
(ReadyCallback callback));
};
class ClientCertStoreLacrosTest : public ::testing::Test {
public:
ClientCertStoreLacrosTest() {
cert_request_ = base::MakeRefCounted<net::SSLCertRequestInfo>();
}
std::unique_ptr<MockClientCertStore> CreateMockStore(
MockClientCertStore** non_owning_pointer) {
auto store = std::make_unique<MockClientCertStore>();
*non_owning_pointer = store.get();
return store;
}
void FakeGetClientCerts(
const net::SSLCertRequestInfo& cert_request_info,
ClientCertStoreLacros::ClientCertListCallback callback) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), net::ClientCertIdentityList()));
}
protected:
content::BrowserTaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
MockCertDbInitializer cert_db_initializer_;
scoped_refptr<net::SSLCertRequestInfo> cert_request_;
};
// Captures callback from `CertDbInitializer::WaitUntilReady(...)` and allows
// to imitate the "ready" notification by calling the `callback`.
struct DbInitCallbackHolder {
base::CallbackListSubscription SaveCallback(
CertDbInitializer::ReadyCallback cb) {
callback = std::move(cb);
return {};
}
CertDbInitializer::ReadyCallback callback;
};
// Provides callback for `ClientCertStore::GetClientCerts(...)` and allows to
// set expectations on it.
class GetCertsCallbackObserver {
public:
void GotClientCerts(net::ClientCertIdentityList) { loop_.Quit(); }
auto GetCallback() {
return base::BindOnce(&GetCertsCallbackObserver::GotClientCerts,
base::Unretained(this));
}
void WaitUntilGotCerts() { loop_.Run(); }
private:
base::RunLoop loop_;
};
// It is reasonable to compare SSLCertRequestInfo by its address because it is a
// non-copyable RefCounted object. `ClientCertStoreLacros` is expected to
// forward the same object (with the same address) to its underlying store.
MATCHER_P(AddressEq, expected_ptr, "") {
return (&arg == expected_ptr);
}
// Test that if CertDbInitializing is not initially ready,
// ClientCertStoreLacros will wait for it.
TEST_F(ClientCertStoreLacrosTest, WaitsForInitialization) {
DbInitCallbackHolder db_init_callback_holder;
EXPECT_CALL(cert_db_initializer_, WaitUntilReady)
.WillOnce(Invoke(&db_init_callback_holder,
&DbInitCallbackHolder::SaveCallback));
// Create ClientCertStoreLacros.
MockClientCertStore* underlying_store = nullptr;
auto cert_store_lacros = std::make_unique<ClientCertStoreLacros>(
&cert_db_initializer_, CreateMockStore(&underlying_store));
// Request client certs.
GetCertsCallbackObserver get_certs_callback_observer;
cert_store_lacros->GetClientCerts(*cert_request_,
get_certs_callback_observer.GetCallback());
// The request should be forwarded to the underlying store, when executed.
EXPECT_CALL(*underlying_store,
GetClientCerts(AddressEq(cert_request_.get()), /*callback=*/_))
.WillOnce(Invoke(this, &ClientCertStoreLacrosTest::FakeGetClientCerts));
// Imitate signal from cert_db_initializer_ that the initialization is done.
// Even if it failed, the cert store should try to continue.
std::move(db_init_callback_holder.callback).Run(/*is_success=*/false);
get_certs_callback_observer.WaitUntilGotCerts();
}
// Test that if CertDbInitializing is initially ready, ClientCertStoreLacros
// will properly forward the `GetClientCerts` request to the underlying store.
TEST_F(ClientCertStoreLacrosTest, RunsImmediatelyIfReady) {
DbInitCallbackHolder db_init_callback_holder;
EXPECT_CALL(cert_db_initializer_, WaitUntilReady)
.WillOnce(Invoke(&db_init_callback_holder,
&DbInitCallbackHolder::SaveCallback));
// Create ClientCertStoreLacros.
MockClientCertStore* underlying_store = nullptr;
auto cert_store_lacros = std::make_unique<ClientCertStoreLacros>(
&cert_db_initializer_, CreateMockStore(&underlying_store));
// Imitate signal from cert_db_initializer_ that the initialization is
// done before calling `GetClientCerts`.
std::move(db_init_callback_holder.callback).Run(/*is_success=*/true);
EXPECT_CALL(*underlying_store,
GetClientCerts(AddressEq(cert_request_.get()), /*callback=*/_))
.WillOnce(Invoke(this, &ClientCertStoreLacrosTest::FakeGetClientCerts));
GetCertsCallbackObserver get_certs_callback_observer;
// Because the cert db is already initialized, the callback should be called
// immediately.
cert_store_lacros->GetClientCerts(*cert_request_,
get_certs_callback_observer.GetCallback());
get_certs_callback_observer.WaitUntilGotCerts();
}
// Test that ClientCertStoreLacros can queue multiple requests.
TEST_F(ClientCertStoreLacrosTest, QueueMultupleRequests) {
DbInitCallbackHolder db_init_callback_holder;
EXPECT_CALL(cert_db_initializer_, WaitUntilReady)
.WillOnce(Invoke(&db_init_callback_holder,
&DbInitCallbackHolder::SaveCallback));
// Create a lot of different requests.
auto cert_request_1 = base::MakeRefCounted<net::SSLCertRequestInfo>();
auto cert_request_2 = base::MakeRefCounted<net::SSLCertRequestInfo>();
auto cert_request_3 = base::MakeRefCounted<net::SSLCertRequestInfo>();
// Create ClientCertStoreLacros.
MockClientCertStore* underlying_store = nullptr;
auto cert_store_lacros = std::make_unique<ClientCertStoreLacros>(
&cert_db_initializer_, CreateMockStore(&underlying_store));
// Request client certs for every cert request.
GetCertsCallbackObserver get_certs_callback_observer_1;
cert_store_lacros->GetClientCerts(
*cert_request_1, get_certs_callback_observer_1.GetCallback());
EXPECT_CALL(*underlying_store,
GetClientCerts(AddressEq(cert_request_1.get()), /*callback=*/_))
.WillOnce(Invoke(this, &ClientCertStoreLacrosTest::FakeGetClientCerts));
GetCertsCallbackObserver get_certs_callback_observer_2;
cert_store_lacros->GetClientCerts(
*cert_request_2, get_certs_callback_observer_2.GetCallback());
EXPECT_CALL(*underlying_store,
GetClientCerts(AddressEq(cert_request_2.get()), /*callback=*/_))
.WillOnce(Invoke(this, &ClientCertStoreLacrosTest::FakeGetClientCerts));
GetCertsCallbackObserver get_certs_callback_observer_3;
cert_store_lacros->GetClientCerts(
*cert_request_3, get_certs_callback_observer_3.GetCallback());
EXPECT_CALL(*underlying_store,
GetClientCerts(AddressEq(cert_request_3.get()), /*callback=*/_))
.WillOnce(Invoke(this, &ClientCertStoreLacrosTest::FakeGetClientCerts));
// Imitate signal from cert_db_initializer_ that the initialization is done.
std::move(db_init_callback_holder.callback).Run(/*is_success=*/true);
get_certs_callback_observer_1.WaitUntilGotCerts();
get_certs_callback_observer_2.WaitUntilGotCerts();
get_certs_callback_observer_3.WaitUntilGotCerts();
}
// Test that ClientCertStoreLacros can be deleted from the last callback.
// (Deleting from a non-last one is prohibited by the API.)
TEST_F(ClientCertStoreLacrosTest, DeletedFromLastCallback) {
DbInitCallbackHolder db_init_callback_holder;
EXPECT_CALL(cert_db_initializer_, WaitUntilReady)
.WillOnce(Invoke(&db_init_callback_holder,
&DbInitCallbackHolder::SaveCallback));
// Create ClientCertStoreLacros.
MockClientCertStore* underlying_store = nullptr;
auto cert_store_lacros = std::make_unique<ClientCertStoreLacros>(
&cert_db_initializer_, CreateMockStore(&underlying_store));
// Request client certs a couple of times.
GetCertsCallbackObserver get_certs_callback_observer_1;
cert_store_lacros->GetClientCerts(
*cert_request_, get_certs_callback_observer_1.GetCallback());
GetCertsCallbackObserver get_certs_callback_observer_2;
cert_store_lacros->GetClientCerts(
*cert_request_, get_certs_callback_observer_2.GetCallback());
// Create a callback that will delete `cert_store_lacros` when executed and
// pass it into the cert store. This code relies on the current implementation
// of ClientCertStoreLacros and assumes that it will be executed last. If the
// implementation changes, it is ok to move it around.
GetCertsCallbackObserver get_certs_callback_observer_3;
auto deleting_callback = [&](net::ClientCertIdentityList list) {
get_certs_callback_observer_3.GotClientCerts(std::move(list));
cert_store_lacros.reset();
};
cert_store_lacros->GetClientCerts(
*cert_request_, base::BindLambdaForTesting(std::move(deleting_callback)));
// All 3 requests should be forwarded to the underlying store.
EXPECT_CALL(*underlying_store,
GetClientCerts((AddressEq(cert_request_)), /*callback=*/_))
.Times(3)
.WillRepeatedly(
Invoke(this, &ClientCertStoreLacrosTest::FakeGetClientCerts));
// Imitate signal from cert_db_initializer_ that the initialization is
// done.
std::move(db_init_callback_holder.callback).Run(/*is_success=*/true);
get_certs_callback_observer_1.WaitUntilGotCerts();
get_certs_callback_observer_2.WaitUntilGotCerts();
get_certs_callback_observer_3.WaitUntilGotCerts();
// The third request should be able to delete the cert store without anything
// crashing.
EXPECT_FALSE(cert_store_lacros);
}
// Test that ClientCertStoreLacros can handle new cert requests during execution
// of another request (i.e. reentering).
TEST_F(ClientCertStoreLacrosTest, HandlesReentrancy) {
DbInitCallbackHolder db_init_callback_holder;
EXPECT_CALL(cert_db_initializer_, WaitUntilReady)
.WillOnce(Invoke(&db_init_callback_holder,
&DbInitCallbackHolder::SaveCallback));
// Create ClientCertStoreLacros.
MockClientCertStore* underlying_store = nullptr;
auto cert_store_lacros = std::make_unique<ClientCertStoreLacros>(
&cert_db_initializer_, CreateMockStore(&underlying_store));
GetCertsCallbackObserver get_certs_callback_observer_1;
GetCertsCallbackObserver get_certs_callback_observer_2;
auto reentering_callback = [&](net::ClientCertIdentityList list) {
get_certs_callback_observer_1.GotClientCerts(std::move(list));
cert_store_lacros->GetClientCerts(
*cert_request_, get_certs_callback_observer_2.GetCallback());
};
// Request client certs with the reentering callback.
cert_store_lacros->GetClientCerts(
*cert_request_,
base::BindLambdaForTesting(std::move(reentering_callback)));
EXPECT_CALL(*underlying_store,
GetClientCerts(AddressEq(cert_request_.get()), /*callback=*/_))
.Times(2)
.WillRepeatedly(
Invoke(this, &ClientCertStoreLacrosTest::FakeGetClientCerts));
// Imitate signal from cert_db_initializer_ that the initialization is done.
std::move(db_init_callback_holder.callback).Run(/*is_success=*/true);
// Verify that both callbacks are called.
get_certs_callback_observer_1.WaitUntilGotCerts();
get_certs_callback_observer_2.WaitUntilGotCerts();
}
} // namespace
...@@ -97,6 +97,11 @@ ...@@ -97,6 +97,11 @@
#include "extensions/common/constants.h" #include "extensions/common/constants.h"
#endif #endif
#if BUILDFLAG(IS_LACROS)
#include "chrome/browser/lacros/cert_db_initializer_factory.h"
#include "chrome/browser/lacros/client_cert_store_lacros.h"
#endif
namespace { namespace {
bool* g_discard_domain_reliability_uploads_for_testing = nullptr; bool* g_discard_domain_reliability_uploads_for_testing = nullptr;
...@@ -575,18 +580,24 @@ ProfileNetworkContextService::CreateClientCertStore() { ...@@ -575,18 +580,24 @@ ProfileNetworkContextService::CreateClientCertStore() {
base::BindRepeating(&CreateCryptoModuleBlockingPasswordDelegate, base::BindRepeating(&CreateCryptoModuleBlockingPasswordDelegate,
kCryptoModulePasswordClientAuth)); kCryptoModulePasswordClientAuth));
#elif defined(USE_NSS_CERTS) #elif defined(USE_NSS_CERTS)
std::unique_ptr<net::ClientCertStore> store =
#if BUILDFLAG(IS_CHROMEOS_LACROS) std::make_unique<net::ClientCertStoreNSS>(
if (!profile_->IsMainProfile()) { base::BindRepeating(&CreateCryptoModuleBlockingPasswordDelegate,
kCryptoModulePasswordClientAuth));
#if BUILDFLAG(IS_LACROS)
CertDbInitializer* cert_db_initializer =
CertDbInitializerFactory::GetForProfileIfExists(profile_);
if (!cert_db_initializer || !profile_->IsMainProfile()) {
// TODO(crbug.com/1148298): return some cert store for secondary profiles in // TODO(crbug.com/1148298): return some cert store for secondary profiles in
// Lacros-Chrome. // Lacros-Chrome.
return nullptr; return nullptr;
} }
#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
return std::make_unique<net::ClientCertStoreNSS>( store = std::make_unique<ClientCertStoreLacros>(cert_db_initializer,
base::BindRepeating(&CreateCryptoModuleBlockingPasswordDelegate, std::move(store));
kCryptoModulePasswordClientAuth)); #endif // BUILDFLAG(IS_LACROS)
return store;
#elif defined(OS_WIN) #elif defined(OS_WIN)
return std::make_unique<net::ClientCertStoreWin>(); return std::make_unique<net::ClientCertStoreWin>();
#elif defined(OS_MAC) #elif defined(OS_MAC)
......
...@@ -4909,6 +4909,7 @@ test("unit_tests") { ...@@ -4909,6 +4909,7 @@ test("unit_tests") {
assert(enable_native_notifications) assert(enable_native_notifications)
sources += [ sources += [
"../browser/lacros/account_manager_facade_lacros_unittest.cc", "../browser/lacros/account_manager_facade_lacros_unittest.cc",
"../browser/lacros/client_cert_store_lacros_unittest.cc",
"../browser/lacros/lacros_chrome_service_delegate_impl_unittest.cc", "../browser/lacros/lacros_chrome_service_delegate_impl_unittest.cc",
"../browser/lacros/metrics_reporting_observer_unittest.cc", "../browser/lacros/metrics_reporting_observer_unittest.cc",
"../browser/notifications/notification_platform_bridge_lacros_unittest.cc", "../browser/notifications/notification_platform_bridge_lacros_unittest.cc",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment