// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/policy/networking/user_network_configuration_updater_ash.h"
#include <utility>
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "base/values.h"
#include "chrome/browser/ash/login/session/user_session_manager.h"
#include "chrome/browser/net/nss_service.h"
#include "chrome/browser/net/nss_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chromeos/ash/components/network/managed_network_configuration_handler.h"
#include "chromeos/ash/components/network/network_cert_loader.h"
#include "chromeos/ash/components/network/onc/network_onc_utils.h"
#include "chromeos/ash/components/network/onc/onc_certificate_importer_impl.h"
#include "chromeos/components/onc/onc_parsed_certificates.h"
#include "chromeos/components/onc/onc_utils.h"
#include "components/policy/policy_constants.h"
#include "components/user_manager/user.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
namespace policy {
namespace {
void GetNssCertDatabaseOnIOThread(
NssCertDatabaseGetter database_getter,
base::OnceCallback<void(net::NSSCertDatabase*)> callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
auto split_callback = base::SplitOnceCallback(std::move(callback));
net::NSSCertDatabase* cert_db =
std::move(database_getter).Run(std::move(split_callback.first));
if (cert_db)
std::move(split_callback.second).Run(cert_db);
}
} // namespace
UserNetworkConfigurationUpdaterAsh::~UserNetworkConfigurationUpdaterAsh() {
// NetworkCertLoader may be not initialized in tests.
if (ash::NetworkCertLoader::IsInitialized()) {
ash::NetworkCertLoader::Get()->SetUserPolicyCertificateProvider(nullptr);
}
}
void UserNetworkConfigurationUpdaterAsh::Shutdown() {
profile_observation_.Reset();
UserNetworkConfigurationUpdater::Shutdown();
}
// static
std::unique_ptr<UserNetworkConfigurationUpdaterAsh>
UserNetworkConfigurationUpdaterAsh::CreateForUserPolicy(
Profile* profile,
const user_manager::User& user,
PolicyService* policy_service,
ash::ManagedNetworkConfigurationHandler* network_config_handler) {
std::unique_ptr<UserNetworkConfigurationUpdaterAsh> updater(
new UserNetworkConfigurationUpdaterAsh(profile, user, policy_service,
network_config_handler));
updater->Init();
return updater;
}
void UserNetworkConfigurationUpdaterAsh::SetClientCertificateImporterForTest(
std::unique_ptr<ash::onc::CertificateImporter>
client_certificate_importer) {
SetClientCertificateImporter(std::move(client_certificate_importer));
}
// static
bool UserNetworkConfigurationUpdaterAsh::
PolicyHasWebTrustedAuthorityCertificate(const PolicyMap& policy_map) {
const base::Value* policy_value = policy_map.GetValue(
key::kOpenNetworkConfiguration, base::Value::Type::STRING);
if (!policy_value)
return false;
base::Value::List certificates_value;
chromeos::onc::ParseAndValidateOncForImport(
policy_value->GetString(), onc::ONC_SOURCE_USER_POLICY,
/*passphrase=*/std::string(),
/*network_configs=*/nullptr, /*global_network_config=*/nullptr,
&certificates_value);
chromeos::onc::OncParsedCertificates onc_parsed_certificates(
certificates_value);
for (const auto& server_or_authority_cert :
onc_parsed_certificates.server_or_authority_certificates()) {
if (server_or_authority_cert.type() ==
chromeos::onc::OncParsedCertificates::ServerOrAuthorityCertificate::
Type::kAuthority &&
server_or_authority_cert.web_trust_requested()) {
return true;
}
}
return false;
}
UserNetworkConfigurationUpdaterAsh::UserNetworkConfigurationUpdaterAsh(
Profile* profile,
const user_manager::User& user,
PolicyService* policy_service,
ash::ManagedNetworkConfigurationHandler* network_config_handler)
: UserNetworkConfigurationUpdater(policy_service),
user_(&user),
network_config_handler_(network_config_handler) {
DCHECK(user_);
// The updater is created with |client_certificate_importer_| unset and is
// responsible for creating it. This requires |GetNSSCertDatabaseForProfile|
// call, which is not safe before the profile initialization is finalized.
// Thus, listen for OnProfileInitializationComplete notification, on which
// |cert_importer_| creation should start. https://crbug.com/171406
// TODO(crbug.com/40113187): Investigate if this is still required.
profile_observation_.Observe(profile);
// Make sure that the |NetworkCertLoader| which makes certificates available
// to the chromeos network code gets policy-pushed certificates from the
// primary profile. This assumes that a |UserNetworkConfigurationUpdaterAsh|
// is only created for the primary profile. NetworkCertLoader may be not
// initialized in tests.
if (ash::NetworkCertLoader::IsInitialized())
ash::NetworkCertLoader::Get()->SetUserPolicyCertificateProvider(this);
// Set profile-wide expansions for policy networks (i.e. those that apply to
// all networks in this profile). Note that this does currently not apply
// user-imported networks (through chrome://network) because those currently
// don't use the ManagedNetworkConfigurationHandler (b/235297258).
network_config_handler_->SetProfileWideVariableExpansions(
user.username_hash(), ash::onc::GetVariableExpansionsForUser(&user));
}
void UserNetworkConfigurationUpdaterAsh::ImportClientCertificates() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// If certificate importer is not yet set, the import of client certificates
// will be re-triggered in SetClientCertificateImporter.
if (client_certificate_importer_) {
client_certificate_importer_->ImportClientCertificates(
GetClientCertificates(), base::DoNothing());
}
}
void UserNetworkConfigurationUpdaterAsh::ApplyNetworkPolicy(
const base::Value::List& network_configs_onc,
const base::Value::Dict& global_network_config) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(user_);
// Call on UserSessionManager to send the user's password to session manager
// if the password substitution variable exists in the ONC.
bool save_password =
ash::onc::HasUserPasswordSubstitutionVariable(network_configs_onc);
ash::UserSessionManager::GetInstance()->VoteForSavingLoginPassword(
ash::UserSessionManager::PasswordConsumingService::kNetwork,
save_password);
network_config_handler_->SetPolicy(onc_source_, user_->username_hash(),
network_configs_onc,
global_network_config);
}
void UserNetworkConfigurationUpdaterAsh::OnProfileInitializationComplete(
Profile* profile) {
DCHECK(profile_observation_.IsObservingSource(profile));
profile_observation_.Reset();
// Note: This unsafely grabs a persistent reference to the `NssService`'s
// `NSSCertDatabase`, which may be invalidated once `profile` is shut down.
// TODO(crbug.com/40753707): Provide better lifetime guarantees and
// pass the `NssCertDatabaseGetter` to the `CertificateImporterImpl`.
content::GetIOThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(&GetNssCertDatabaseOnIOThread,
NssServiceFactory::GetForContext(profile)
->CreateNSSCertDatabaseGetterForIOThread(),
base::BindPostTaskToCurrentDefault(base::BindOnce(
&UserNetworkConfigurationUpdaterAsh::
CreateAndSetClientCertificateImporter,
weak_factory_.GetWeakPtr()))));
}
void UserNetworkConfigurationUpdaterAsh::CreateAndSetClientCertificateImporter(
net::NSSCertDatabase* database) {
DCHECK(database);
SetClientCertificateImporter(
std::make_unique<ash::onc::CertificateImporterImpl>(
content::GetIOThreadTaskRunner({}), database));
}
void UserNetworkConfigurationUpdaterAsh::SetClientCertificateImporter(
std::unique_ptr<ash::onc::CertificateImporter>
client_certificate_importer) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
bool initial_client_certificate_importer =
client_certificate_importer_ == nullptr;
client_certificate_importer_ = std::move(client_certificate_importer);
if (initial_client_certificate_importer && !GetClientCertificates().empty()) {
client_certificate_importer_->ImportClientCertificates(
GetClientCertificates(), base::DoNothing());
}
}
} // namespace policy