blob: 80193d4b7c625ec20698ad6882803d2d48268c1f [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/server_certificate_database/server_certificate_database_service.h"
#include <string>
#include <string_view>
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/task_traits.h"
#include "base/task/thread_pool.h"
#include "components/server_certificate_database/server_certificate_database.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "base/metrics/histogram_functions.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/server_certificate_database/server_certificate_database_nss_migrator.h"
#endif
namespace net {
namespace {
#if BUILDFLAG(IS_CHROMEOS)
bool g_disable_nss_cert_migration_for_testing = false;
#endif
} // namespace
#if BUILDFLAG(IS_CHROMEOS)
ServerCertificateDatabaseService::ServerCertificateDatabaseService(
base::FilePath profile_path,
PrefService* prefs,
ServerCertificateDatabaseNSSMigrator::NssSlotGetter nss_slot_getter)
: profile_path_(std::move(profile_path)),
prefs_(prefs),
nss_slot_getter_(std::move(nss_slot_getter))
#else
ServerCertificateDatabaseService::ServerCertificateDatabaseService(
base::FilePath profile_path)
: profile_path_(std::move(profile_path))
#endif
{
server_cert_database_ = base::SequenceBound<net::ServerCertificateDatabase>(
base::ThreadPool::CreateSequencedTaskRunnerForResource(
{base::MayBlock(), base::TaskPriority::USER_BLOCKING,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
profile_path_.Append(kServerCertificateDatabaseName)),
profile_path_);
}
ServerCertificateDatabaseService::~ServerCertificateDatabaseService() = default;
void ServerCertificateDatabaseService::AddOrUpdateUserCertificates(
std::vector<net::ServerCertificateDatabase::CertInformation> cert_infos,
base::OnceCallback<void(bool)> callback) {
server_cert_database_
.AsyncCall(&net::ServerCertificateDatabase::InsertOrUpdateCerts)
.WithArgs(std::move(cert_infos))
.Then(base::BindOnce(
&ServerCertificateDatabaseService::HandleModificationResult,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void ServerCertificateDatabaseService::GetAllCertificates(
base::OnceCallback<
void(std::vector<net::ServerCertificateDatabase::CertInformation>)>
callback) {
#if BUILDFLAG(IS_CHROMEOS)
// Migrate certificates from NSS and then read all certificates from the
// database. Migration will only be done once per profile. If called multiple
// times before migration completes, all the callbacks will be queued and
// processed once the migration is done.
// TODO(crbug.com/390333881): Remove the migration code once sufficient time
// has passed after the feature is launched.
if (!g_disable_nss_cert_migration_for_testing &&
prefs_->GetInteger(prefs::kNSSCertsMigratedToServerCertDb) ==
static_cast<int>(NSSMigrationResultPref::kNotMigrated)) {
if (!nss_migrator_) {
DVLOG(1) << "starting migration for profile "
<< profile_path_.AsUTF8Unsafe();
nss_migrator_ = std::make_unique<ServerCertificateDatabaseNSSMigrator>(
this, std::move(nss_slot_getter_));
// Unretained is safe as ServerCertificateDatabaseNSSMigrator will not
// run the callback after it is deleted.
nss_migrator_->MigrateCerts(base::BindOnce(
&ServerCertificateDatabaseService::NSSMigrationComplete,
base::Unretained(this)));
}
DVLOG(1) << "queuing migration request";
get_certificates_pending_migration_.push_back(std::move(callback));
return;
}
#endif // BUILDFLAG(IS_CHROMEOS)
server_cert_database_
.AsyncCall(&net::ServerCertificateDatabase::RetrieveAllCertificates)
.Then(std::move(callback));
}
#if BUILDFLAG(IS_CHROMEOS)
void ServerCertificateDatabaseService::NSSMigrationComplete(
ServerCertificateDatabaseNSSMigrator::MigrationResult result) {
DVLOG(1) << "Migration for " << profile_path_.AsUTF8Unsafe()
<< " finished: nss cert count=" << result.cert_count
<< " errors=" << result.error_count;
NSSMigrationResultHistogram result_for_histogram;
if (result.cert_count == 0) {
result_for_histogram = NSSMigrationResultHistogram::kNssDbEmpty;
} else if (result.error_count == 0) {
result_for_histogram = NSSMigrationResultHistogram::kSuccess;
} else if (result.error_count < result.cert_count) {
result_for_histogram = NSSMigrationResultHistogram::kPartialSuccess;
} else {
result_for_histogram = NSSMigrationResultHistogram::kFailed;
}
base::UmaHistogramEnumeration("Net.CertVerifier.NSSCertMigrationResult",
result_for_histogram);
base::UmaHistogramCounts100(
"Net.CertVerifier.NSSCertMigrationQueuedRequestsWhenFinished",
get_certificates_pending_migration_.size());
prefs_->SetInteger(
prefs::kNSSCertsMigratedToServerCertDb,
static_cast<int>((result.error_count == 0)
? NSSMigrationResultPref::kMigratedSuccessfully
: NSSMigrationResultPref::kMigrationHadErrors));
for (GetCertificatesCallback& callback :
get_certificates_pending_migration_) {
// TODO(https://crbug.com/40928765): kinda silly to start multiple
// simultaneous reads here, but dunno if it actually occurs enough to be
// worth optimizing. Evaluate the histograms to see if this seems worth
// addressing.
GetAllCertificates(std::move(callback));
}
get_certificates_pending_migration_.clear();
nss_migrator_.reset();
}
#endif // BUILDFLAG(IS_CHROMEOS)
void ServerCertificateDatabaseService::PostTaskWithDatabase(
base::OnceCallback<void(net::ServerCertificateDatabase*)> callback) {
server_cert_database_.PostTaskWithThisObject(std::move(callback));
}
void ServerCertificateDatabaseService::GetCertificatesCount(
base::OnceCallback<void(uint32_t)> callback) {
server_cert_database_
.AsyncCall(&net::ServerCertificateDatabase::RetrieveCertificatesCount)
.Then(std::move(callback));
}
void ServerCertificateDatabaseService::DeleteCertificate(
const std::string& sha256hash_hex,
base::OnceCallback<void(bool)> callback) {
server_cert_database_
.AsyncCall(&net::ServerCertificateDatabase::DeleteCertificate)
.WithArgs(sha256hash_hex)
.Then(base::BindOnce(
&ServerCertificateDatabaseService::HandleModificationResult,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
base::CallbackListSubscription ServerCertificateDatabaseService::AddObserver(
base::RepeatingClosure callback) {
return observers_.Add(std::move(callback));
}
void ServerCertificateDatabaseService::HandleModificationResult(
base::OnceCallback<void(bool)> callback,
bool success) {
std::move(callback).Run(success);
if (success) {
observers_.Notify();
}
}
#if BUILDFLAG(IS_CHROMEOS)
void ServerCertificateDatabaseService::RegisterProfilePrefs(
PrefRegistrySimple* registry) {
registry->RegisterIntegerPref(
prefs::kNSSCertsMigratedToServerCertDb,
static_cast<int>(net::ServerCertificateDatabaseService::
NSSMigrationResultPref::kNotMigrated));
}
void ServerCertificateDatabaseService::DisableNSSCertMigrationForTesting() {
g_disable_nss_cert_migration_for_testing = true;
}
#endif
} // namespace net