blob: a99d5b78ca972a3c04c70ad60671e66a09b2b2e5 [file] [log] [blame]
// Copyright (c) 2012 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/ui/webui/net_internals/net_internals_ui.h"
#include <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/memory/weak_ptr.h"
#include "base/task/post_task.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/download_prefs.h"
#include "chrome/browser/net/net_export_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/url_constants.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/net_internals_resources.h"
#include "components/onc/onc_constants.h"
#include "components/prefs/pref_member.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "content/public/browser/web_ui_message_handler.h"
#include "net/log/net_log_util.h"
#include "services/network/expect_ct_reporter.h"
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/chromeos/system_logs/debug_log_writer.h"
#include "chrome/browser/net/nss_context.h"
#include "chrome/browser/policy/policy_conversions.h"
#include "chrome/common/logging_chrome.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/debug_daemon_client.h"
#include "chromeos/network/onc/onc_certificate_importer_impl.h"
#include "chromeos/network/onc/onc_parsed_certificates.h"
#include "chromeos/network/onc/onc_utils.h"
#endif
using content::BrowserThread;
namespace {
content::WebUIDataSource* CreateNetInternalsHTMLSource() {
content::WebUIDataSource* source =
content::WebUIDataSource::Create(chrome::kChromeUINetInternalsHost);
source->OverrideContentSecurityPolicyScriptSrc(
"script-src chrome://resources 'self' 'unsafe-eval';");
source->SetDefaultResource(IDR_NET_INTERNALS_INDEX_HTML);
source->AddResourcePath("index.js", IDR_NET_INTERNALS_INDEX_JS);
source->SetJsonPath("strings.js");
return source;
}
void IgnoreBoolCallback(bool result) {}
// This class receives javascript messages from the renderer.
// Note that the WebUI infrastructure runs on the UI thread, therefore all of
// this class's methods are expected to run on the UI thread.
class NetInternalsMessageHandler
: public content::WebUIMessageHandler,
public base::SupportsWeakPtr<NetInternalsMessageHandler> {
public:
explicit NetInternalsMessageHandler(content::WebUI* web_ui);
~NetInternalsMessageHandler() override = default;
protected:
// WebUIMessageHandler implementation:
void RegisterMessages() override;
private:
network::mojom::NetworkContext* GetNetworkContext() const;
// Calls g_browser.receive in the renderer, passing in |command| and |arg|.
// If the renderer is displaying a log file, the message will be ignored.
void SendJavascriptCommand(const std::string& command, base::Value arg);
#if defined(OS_CHROMEOS)
// Callback to |GetNSSCertDatabaseForProfile| used to retrieve the database
// to which user's ONC defined certificates should be imported.
// It parses and imports |onc_blob|.
void ImportONCFileToNSSDB(const std::string& onc_blob,
const std::string& passcode,
net::NSSCertDatabase* nssdb);
// Called back by the CertificateImporter when a certificate import finished.
// |previous_error| contains earlier errors during this import.
void OnCertificatesImported(const std::string& previous_error,
bool cert_import_success);
#endif
void OnExpectCTTestReportCallback(bool success);
//--------------------------------
// Javascript message handlers:
//--------------------------------
void OnReloadProxySettings(const base::ListValue* list);
void OnClearBadProxies(const base::ListValue* list);
void OnClearHostResolverCache(const base::ListValue* list);
void OnDomainSecurityPolicyDelete(const base::ListValue* list);
void OnHSTSQuery(const base::ListValue* list);
void OnHSTSAdd(const base::ListValue* list);
void OnExpectCTQuery(const base::ListValue* list);
void OnExpectCTAdd(const base::ListValue* list);
void OnExpectCTTestReport(const base::ListValue* list);
void OnCloseIdleSockets(const base::ListValue* list);
void OnFlushSocketPools(const base::ListValue* list);
#if defined(OS_CHROMEOS)
void OnDumpPolicyLogsCompleted(const base::FilePath& path,
bool should_compress,
bool combined,
const char* received_event);
void OnImportONCFile(const base::ListValue* list);
void OnStoreDebugLogs(bool combined,
const char* received_event,
const base::ListValue* list);
void OnStoreDebugLogsCompleted(const char* received_event,
const base::FilePath& log_path,
bool succeeded);
void OnSetNetworkDebugMode(const base::ListValue* list);
void OnSetNetworkDebugModeCompleted(const std::string& subsystem,
bool succeeded);
#endif
const content::WebUI* web_ui_;
DISALLOW_COPY_AND_ASSIGN(NetInternalsMessageHandler);
};
NetInternalsMessageHandler::NetInternalsMessageHandler(content::WebUI* web_ui)
: web_ui_(web_ui) {}
void NetInternalsMessageHandler::RegisterMessages() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
web_ui()->RegisterMessageCallback(
"reloadProxySettings",
base::BindRepeating(&NetInternalsMessageHandler::OnReloadProxySettings,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"clearBadProxies",
base::BindRepeating(&NetInternalsMessageHandler::OnClearBadProxies,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"clearHostResolverCache",
base::BindRepeating(&NetInternalsMessageHandler::OnClearHostResolverCache,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"domainSecurityPolicyDelete",
base::BindRepeating(
&NetInternalsMessageHandler::OnDomainSecurityPolicyDelete,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"hstsQuery", base::BindRepeating(&NetInternalsMessageHandler::OnHSTSQuery,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"hstsAdd", base::BindRepeating(&NetInternalsMessageHandler::OnHSTSAdd,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"expectCTQuery",
base::BindRepeating(&NetInternalsMessageHandler::OnExpectCTQuery,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"expectCTAdd",
base::BindRepeating(&NetInternalsMessageHandler::OnExpectCTAdd,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"expectCTTestReport",
base::BindRepeating(&NetInternalsMessageHandler::OnExpectCTTestReport,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"closeIdleSockets",
base::BindRepeating(&NetInternalsMessageHandler::OnCloseIdleSockets,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"flushSocketPools",
base::BindRepeating(&NetInternalsMessageHandler::OnFlushSocketPools,
base::Unretained(this)));
#if defined(OS_CHROMEOS)
web_ui()->RegisterMessageCallback(
"importONCFile",
base::BindRepeating(&NetInternalsMessageHandler::OnImportONCFile,
base::Unretained(this)));
web_ui()->RegisterMessageCallback(
"storeDebugLogs",
base::BindRepeating(&NetInternalsMessageHandler::OnStoreDebugLogs,
base::Unretained(this), false /* combined */,
"receivedStoreDebugLogs"));
web_ui()->RegisterMessageCallback(
"storeCombinedDebugLogs",
base::BindRepeating(&NetInternalsMessageHandler::OnStoreDebugLogs,
base::Unretained(this), true /* combined */,
"receivedStoreCombinedDebugLogs"));
web_ui()->RegisterMessageCallback(
"setNetworkDebugMode",
base::BindRepeating(&NetInternalsMessageHandler::OnSetNetworkDebugMode,
base::Unretained(this)));
#endif
}
void NetInternalsMessageHandler::SendJavascriptCommand(
const std::string& command,
base::Value arg) {
std::unique_ptr<base::Value> command_value(new base::Value(command));
DCHECK_CURRENTLY_ON(BrowserThread::UI);
web_ui()->CallJavascriptFunctionUnsafe("g_browser.receive",
*command_value.get(), arg);
}
void NetInternalsMessageHandler::OnReloadProxySettings(
const base::ListValue* list) {
GetNetworkContext()->ForceReloadProxyConfig(base::NullCallback());
}
void NetInternalsMessageHandler::OnClearBadProxies(
const base::ListValue* list) {
GetNetworkContext()->ClearBadProxiesCache(base::NullCallback());
}
void NetInternalsMessageHandler::OnClearHostResolverCache(
const base::ListValue* list) {
GetNetworkContext()->ClearHostCache(/*filter=*/nullptr, base::NullCallback());
}
void NetInternalsMessageHandler::OnDomainSecurityPolicyDelete(
const base::ListValue* list) {
// |list| should be: [<domain to query>].
std::string domain;
bool result = list->GetString(0, &domain);
DCHECK(result);
if (!base::IsStringASCII(domain)) {
// There cannot be a unicode entry in the HSTS set.
return;
}
GetNetworkContext()->DeleteDynamicDataForHost(
domain, base::BindOnce(&IgnoreBoolCallback));
}
void NetInternalsMessageHandler::OnHSTSQuery(const base::ListValue* list) {
// |list| should be: [<domain to query>].
std::string domain;
bool get_domain_result = list->GetString(0, &domain);
DCHECK(get_domain_result);
GetNetworkContext()->GetHSTSState(
domain, base::BindOnce(&NetInternalsMessageHandler::SendJavascriptCommand,
this->AsWeakPtr(), "receivedHSTSResult"));
}
void NetInternalsMessageHandler::OnHSTSAdd(const base::ListValue* list) {
// |list| should be: [<domain to query>, <STS include subdomains>]
std::string domain;
bool result = list->GetString(0, &domain);
DCHECK(result);
if (!base::IsStringASCII(domain)) {
// Silently fail. The user will get a helpful error if they query for the
// name.
return;
}
bool sts_include_subdomains;
result = list->GetBoolean(1, &sts_include_subdomains);
DCHECK(result);
base::Time expiry = base::Time::Now() + base::TimeDelta::FromDays(1000);
GetNetworkContext()->AddHSTS(domain, expiry, sts_include_subdomains,
base::DoNothing());
}
void NetInternalsMessageHandler::OnExpectCTQuery(const base::ListValue* list) {
// |list| should be: [<domain to query>].
std::string domain;
bool domain_result = list->GetString(0, &domain);
DCHECK(domain_result);
GetNetworkContext()->GetExpectCTState(
domain, base::BindOnce(&NetInternalsMessageHandler::SendJavascriptCommand,
this->AsWeakPtr(), "receivedExpectCTResult"));
}
void NetInternalsMessageHandler::OnExpectCTAdd(const base::ListValue* list) {
// |list| should be: [<domain to add>, <report URI>, <enforce>].
std::string domain;
bool result = list->GetString(0, &domain);
DCHECK(result);
if (!base::IsStringASCII(domain)) {
// Silently fail. The user will get a helpful error if they query for the
// name.
return;
}
std::string report_uri_str;
result = list->GetString(1, &report_uri_str);
DCHECK(result);
bool enforce;
result = list->GetBoolean(2, &enforce);
DCHECK(result);
base::Time expiry = base::Time::Now() + base::TimeDelta::FromDays(1000);
GetNetworkContext()->AddExpectCT(domain, expiry, enforce,
GURL(report_uri_str), base::DoNothing());
}
void NetInternalsMessageHandler::OnExpectCTTestReport(
const base::ListValue* list) {
// |list| should be: [<report URI>].
std::string report_uri_str;
bool result = list->GetString(0, &report_uri_str);
DCHECK(result);
GURL report_uri(report_uri_str);
if (!report_uri.is_valid())
return;
GetNetworkContext()->SetExpectCTTestReport(
report_uri,
base::BindOnce(&NetInternalsMessageHandler::OnExpectCTTestReportCallback,
this->AsWeakPtr()));
}
void NetInternalsMessageHandler::OnExpectCTTestReportCallback(bool success) {
SendJavascriptCommand(
"receivedExpectCTTestReportResult",
success ? base::Value("success") : base::Value("failure"));
}
void NetInternalsMessageHandler::OnFlushSocketPools(
const base::ListValue* list) {
GetNetworkContext()->CloseAllConnections(base::NullCallback());
}
void NetInternalsMessageHandler::OnCloseIdleSockets(
const base::ListValue* list) {
GetNetworkContext()->CloseIdleConnections(base::NullCallback());
}
#if defined(OS_CHROMEOS)
void NetInternalsMessageHandler::ImportONCFileToNSSDB(
const std::string& onc_blob,
const std::string& passcode,
net::NSSCertDatabase* nssdb) {
const user_manager::User* user =
chromeos::ProfileHelper::Get()->GetUserByProfile(
Profile::FromWebUI(web_ui()));
if (!user) {
std::string error = "User not found.";
SendJavascriptCommand("receivedONCFileParse", base::Value(error));
return;
}
std::string error;
onc::ONCSource onc_source = onc::ONC_SOURCE_USER_IMPORT;
base::ListValue network_configs;
base::DictionaryValue global_network_config;
base::ListValue certificates;
if (!chromeos::onc::ParseAndValidateOncForImport(onc_blob,
onc_source,
passcode,
&network_configs,
&global_network_config,
&certificates)) {
error = "Errors occurred during the ONC parsing. ";
}
std::string network_error;
chromeos::onc::ImportNetworksForUser(user, network_configs, &network_error);
if (!network_error.empty())
error += network_error;
chromeos::onc::CertificateImporterImpl cert_importer(
base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO}), nssdb);
auto certs =
std::make_unique<chromeos::onc::OncParsedCertificates>(certificates);
if (certs->has_error())
error += "Some certificates couldn't be parsed. ";
cert_importer.ImportAllCertificatesUserInitiated(
certs->server_or_authority_certificates(), certs->client_certificates(),
base::Bind(&NetInternalsMessageHandler::OnCertificatesImported,
AsWeakPtr(), error /* previous_error */));
}
void NetInternalsMessageHandler::OnCertificatesImported(
const std::string& previous_error,
bool cert_import_success) {
std::string error = previous_error;
if (!cert_import_success)
error += "Some certificates couldn't be imported. ";
SendJavascriptCommand("receivedONCFileParse", base::Value(error));
}
void NetInternalsMessageHandler::OnImportONCFile(
const base::ListValue* list) {
std::string onc_blob;
std::string passcode;
if (list->GetSize() != 2 ||
!list->GetString(0, &onc_blob) ||
!list->GetString(1, &passcode)) {
NOTREACHED();
}
GetNSSCertDatabaseForProfile(
Profile::FromWebUI(web_ui()),
base::Bind(&NetInternalsMessageHandler::ImportONCFileToNSSDB, AsWeakPtr(),
onc_blob, passcode));
}
void DumpPolicyLogs(base::FilePath file_path, std::string json_policies) {
file_path = logging::GenerateTimestampedName(file_path, base::Time::Now());
base::WriteFile(file_path, json_policies.data(), json_policies.size());
}
void NetInternalsMessageHandler::OnStoreDebugLogs(bool combined,
const char* received_event,
const base::ListValue* list) {
DCHECK(list);
SendJavascriptCommand(received_event, base::Value("Creating log file..."));
Profile* profile = Profile::FromWebUI(web_ui());
const DownloadPrefs* const prefs = DownloadPrefs::FromBrowserContext(profile);
base::FilePath path = prefs->DownloadPath();
if (file_manager::util::IsUnderNonNativeLocalPath(profile, path))
path = prefs->GetDefaultDownloadDirectoryForProfile();
base::FilePath policies_path = path.Append("policies.json");
std::string json_policies = policy::GetAllPolicyValuesAsJSON(
web_ui()->GetWebContents()->GetBrowserContext(),
true /* with_user_policies */, false /* with_device_data */,
true /* is_pretty_print */);
base::PostTaskWithTraitsAndReply(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::BLOCK_SHUTDOWN},
base::BindOnce(DumpPolicyLogs, policies_path, json_policies),
base::BindOnce(&NetInternalsMessageHandler::OnDumpPolicyLogsCompleted,
AsWeakPtr(), path, true /* should_compress */, combined,
received_event));
}
void NetInternalsMessageHandler::OnDumpPolicyLogsCompleted(
const base::FilePath& path,
bool should_compress,
bool combined,
const char* received_event) {
if (combined) {
chromeos::DebugLogWriter::StoreCombinedLogs(
path,
base::BindOnce(&NetInternalsMessageHandler::OnStoreDebugLogsCompleted,
AsWeakPtr(), received_event));
} else {
chromeos::DebugLogWriter::StoreLogs(
path, should_compress,
base::BindOnce(&NetInternalsMessageHandler::OnStoreDebugLogsCompleted,
AsWeakPtr(), received_event));
}
}
void NetInternalsMessageHandler::OnStoreDebugLogsCompleted(
const char* received_event,
const base::FilePath& log_path,
bool succeeded) {
std::string status;
if (succeeded)
status = "Created log file: " + log_path.BaseName().AsUTF8Unsafe();
else
status = "Failed to create log file";
SendJavascriptCommand(received_event, base::Value(status));
}
void NetInternalsMessageHandler::OnSetNetworkDebugMode(
const base::ListValue* list) {
std::string subsystem;
if (list->GetSize() != 1 || !list->GetString(0, &subsystem))
NOTREACHED();
chromeos::DBusThreadManager::Get()->GetDebugDaemonClient()->
SetDebugMode(
subsystem,
base::Bind(
&NetInternalsMessageHandler::OnSetNetworkDebugModeCompleted,
AsWeakPtr(),
subsystem));
}
void NetInternalsMessageHandler::OnSetNetworkDebugModeCompleted(
const std::string& subsystem,
bool succeeded) {
std::string status = succeeded ? "Debug mode is changed to "
: "Failed to change debug mode to ";
status += subsystem;
SendJavascriptCommand("receivedSetNetworkDebugMode", base::Value(status));
}
#endif // defined(OS_CHROMEOS)
network::mojom::NetworkContext* NetInternalsMessageHandler::GetNetworkContext()
const {
return content::BrowserContext::GetDefaultStoragePartition(
web_ui_->GetWebContents()->GetBrowserContext())
->GetNetworkContext();
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
//
// NetInternalsUI
//
////////////////////////////////////////////////////////////////////////////////
NetInternalsUI::NetInternalsUI(content::WebUI* web_ui)
: WebUIController(web_ui) {
web_ui->AddMessageHandler(
std::make_unique<NetInternalsMessageHandler>(web_ui));
// Set up the chrome://net-internals/ source.
Profile* profile = Profile::FromWebUI(web_ui);
content::WebUIDataSource::Add(profile, CreateNetInternalsHTMLSource());
}