// Copyright 2020 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/ui/webui/ash/onc_import_message_handler.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/strings/stringprintf.h"
#include "base/task/bind_post_task.h"
#include "base/task/sequenced_task_runner.h"
#include "base/values.h"
#include "chrome/browser/ash/profiles/profile_helper.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/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/onc/onc_constants.h"
#include "components/policy/core/browser/policy_conversions.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
namespace ash {
namespace {
void GetCertDBOnIOThread(
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
OncImportMessageHandler::OncImportMessageHandler() = default;
OncImportMessageHandler::~OncImportMessageHandler() = default;
void OncImportMessageHandler::RegisterMessages() {
web_ui()->RegisterMessageCallback(
"importONC", base::BindRepeating(&OncImportMessageHandler::OnImportONC,
base::Unretained(this)));
}
void OncImportMessageHandler::Respond(const std::string& callback_id,
const std::string& result,
bool is_error) {
base::Value::List response;
response.Append(result);
response.Append(is_error);
ResolveJavascriptCallback(base::Value(callback_id), response);
}
void OncImportMessageHandler::OnImportONC(const base::Value::List& list) {
CHECK_EQ(2u, list.size());
std::string callback_id = list[0].GetString();
std::string onc_blob = list[1].GetString();
AllowJavascript();
// TODO(crbug.com/40753707): Pass the `NssCertDatabaseGetter` to
// the `CertImporter`. This is not unsafe if the profile shuts down during
// this operation.
content::GetIOThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(
&GetCertDBOnIOThread,
NssServiceFactory::GetForContext(Profile::FromWebUI(web_ui()))
->CreateNSSCertDatabaseGetterForIOThread(),
base::BindPostTaskToCurrentDefault(base::BindOnce(
&OncImportMessageHandler::ImportONCToNSSDB,
weak_factory_.GetWeakPtr(), callback_id, onc_blob))));
}
void OncImportMessageHandler::ImportONCToNSSDB(const std::string& callback_id,
const std::string& onc_blob,
net::NSSCertDatabase* nssdb) {
const user_manager::User* user =
ProfileHelper::Get()->GetUserByProfile(Profile::FromWebUI(web_ui()));
if (!user) {
Respond(callback_id, "User not found.", /*is_error=*/true);
return;
}
std::string result;
bool has_error = false;
::onc::ONCSource onc_source = ::onc::ONC_SOURCE_USER_IMPORT;
base::Value::List network_configs;
base::Value::Dict global_network_config;
base::Value::List certificates;
if (!chromeos::onc::ParseAndValidateOncForImport(
onc_blob, onc_source, /*passphrase=*/std::string(), &network_configs,
&global_network_config, &certificates)) {
has_error = true;
result += "Errors occurred during ONC parsing.\n";
}
std::string import_error;
int num_networks_imported =
onc::ImportNetworksForUser(user, network_configs, &import_error);
if (!import_error.empty()) {
has_error = true;
result += "Error importing networks: " + import_error + "\n";
}
result +=
base::StringPrintf("Networks imported: %d\n", num_networks_imported);
if (certificates.empty()) {
if (!num_networks_imported)
has_error = true;
Respond(callback_id, result, has_error);
return;
}
auto cert_importer = std::make_unique<onc::CertificateImporterImpl>(
content::GetIOThreadTaskRunner({}), nssdb);
auto certs =
std::make_unique<chromeos::onc::OncParsedCertificates>(certificates);
if (certs->has_error()) {
has_error = true;
result += "Some certificates could not be parsed.\n";
}
auto* const cert_importer_ptr = cert_importer.get();
cert_importer_ptr->ImportAllCertificatesUserInitiated(
certs->server_or_authority_certificates(), certs->client_certificates(),
base::BindOnce(&OncImportMessageHandler::OnCertificatesImported,
weak_factory_.GetWeakPtr(), std::move(cert_importer),
callback_id, result, has_error));
}
void OncImportMessageHandler::OnCertificatesImported(
std::unique_ptr<onc::CertificateImporterImpl> cert_importer,
const std::string& callback_id,
const std::string& previous_result,
bool has_error,
bool cert_import_success) {
std::string result = previous_result;
if (!cert_import_success) {
has_error = true;
result += "Some certificates couldn't be imported.\n";
}
Respond(callback_id, result, has_error);
// |cert_importer| will be destroyed when the callback exits.
}
} // namespace ash