blob: f0c1fc4394d5dcf31695970b6d8c411525f62220 [file] [log] [blame]
// Copyright 2017 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 "services/network/network_service.h"
#include <map>
#include <utility>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/task/post_task.h"
#include "base/values.h"
#include "components/certificate_transparency/sth_distributor.h"
#include "components/certificate_transparency/sth_observer.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "net/base/logging_network_change_observer.h"
#include "net/base/network_change_notifier.h"
#include "net/cert/ct_log_response_parser.h"
#include "net/cert/signed_tree_head.h"
#include "net/dns/host_resolver.h"
#include "net/dns/mapped_host_resolver.h"
#include "net/http/http_auth_handler_factory.h"
#include "net/log/file_net_log_observer.h"
#include "net/log/net_log.h"
#include "net/log/net_log_util.h"
#include "net/url_request/url_request_context_builder.h"
#include "services/network/mojo_net_log.h"
#include "services/network/network_context.h"
#include "services/network/network_usage_accumulator.h"
#include "services/network/public/cpp/network_switches.h"
#include "services/network/url_request_context_builder_mojo.h"
#if defined(OS_ANDROID) && defined(ARCH_CPU_ARMEL)
#include "crypto/openssl_util.h"
#include "third_party/boringssl/src/include/openssl/cpu.h"
#endif
#if defined(OS_LINUX) && !defined(OS_CHROMEOS) && !defined(IS_CHROMECAST)
#include "components/os_crypt/key_storage_config_linux.h"
#include "components/os_crypt/os_crypt.h"
#endif
namespace network {
namespace {
NetworkService* g_network_service = nullptr;
std::unique_ptr<net::NetworkChangeNotifier>
CreateNetworkChangeNotifierIfNeeded() {
// There is a global singleton net::NetworkChangeNotifier if NetworkService
// is running inside of the browser process.
if (!net::NetworkChangeNotifier::HasNetworkChangeNotifier()) {
#if defined(OS_ANDROID)
// On Android, NetworkChangeNotifier objects are always set up in process
// before NetworkService is run.
return nullptr;
#elif defined(OS_CHROMEOS) || defined(OS_IOS) || defined(OS_FUCHSIA)
// ChromeOS has its own implementation of NetworkChangeNotifier that lives
// outside of //net. iOS doesn't embed //content. Fuchsia doesn't have an
// implementation yet.
// TODO(xunjieli): Figure out what to do for these 3 platforms.
NOTIMPLEMENTED();
return nullptr;
#endif
return base::WrapUnique(net::NetworkChangeNotifier::Create());
}
return nullptr;
}
std::unique_ptr<net::HostResolver> CreateHostResolver(net::NetLog* net_log) {
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
std::unique_ptr<net::HostResolver> host_resolver(
net::HostResolver::CreateDefaultResolver(net_log));
if (!command_line.HasSwitch(switches::kHostResolverRules))
return host_resolver;
std::unique_ptr<net::MappedHostResolver> remapped_host_resolver(
new net::MappedHostResolver(std::move(host_resolver)));
remapped_host_resolver->SetRulesFromString(
command_line.GetSwitchValueASCII(switches::kHostResolverRules));
return std::move(remapped_host_resolver);
}
} // namespace
NetworkService::NetworkService(
std::unique_ptr<service_manager::BinderRegistry> registry,
mojom::NetworkServiceRequest request,
net::NetLog* net_log)
: registry_(std::move(registry)), binding_(this) {
DCHECK(!g_network_service);
g_network_service = this;
// |registry_| is nullptr when an in-process NetworkService is
// created directly. The latter is done in concert with using
// CreateNetworkContextWithBuilder to ease the transition to using the
// network service.
if (registry_) {
DCHECK(!request.is_pending());
registry_->AddInterface<mojom::NetworkService>(
base::BindRepeating(&NetworkService::Bind, base::Unretained(this)));
} else if (request.is_pending()) {
Bind(std::move(request));
}
#if defined(OS_ANDROID) && defined(ARCH_CPU_ARMEL)
// Make sure OpenSSL is initialized before using it to histogram data.
crypto::EnsureOpenSSLInit();
// Measure CPUs with broken NEON units. See https://crbug.com/341598.
UMA_HISTOGRAM_BOOLEAN("Net.HasBrokenNEON", CRYPTO_has_broken_NEON());
// Measure Android kernels with missing AT_HWCAP2 auxv fields. See
// https://crbug.com/boringssl/46.
UMA_HISTOGRAM_BOOLEAN("Net.NeedsHWCAP2Workaround",
CRYPTO_needs_hwcap2_workaround());
#endif
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
// Record this once per session, though the switch is appled on a
// per-NetworkContext basis.
UMA_HISTOGRAM_BOOLEAN(
"Net.Certificate.IgnoreCertificateErrorsSPKIListPresent",
command_line->HasSwitch(
network::switches::kIgnoreCertificateErrorsSPKIList));
network_change_manager_ = std::make_unique<NetworkChangeManager>(
CreateNetworkChangeNotifierIfNeeded());
if (net_log) {
net_log_ = net_log;
} else {
owned_net_log_ = std::make_unique<MojoNetLog>();
// Note: The command line switches are only checked when not using the
// embedder's NetLog, as it may already be writing to the destination log
// file.
net_log_ = owned_net_log_.get();
}
// Add an observer that will emit network change events to the ChromeNetLog.
// Assuming NetworkChangeNotifier dispatches in FIFO order, we should be
// logging the network change before other IO thread consumers respond to it.
network_change_observer_.reset(
new net::LoggingNetworkChangeObserver(net_log_));
network_quality_estimator_manager_ =
std::make_unique<NetworkQualityEstimatorManager>(net_log_);
host_resolver_ = CreateHostResolver(net_log_);
network_usage_accumulator_ = std::make_unique<NetworkUsageAccumulator>();
sth_distributor_ =
std::make_unique<certificate_transparency::STHDistributor>();
}
NetworkService::~NetworkService() {
DCHECK_EQ(this, g_network_service);
g_network_service = nullptr;
// Destroy owned network contexts.
DestroyNetworkContexts();
// All NetworkContexts (Owned and unowned) must have been deleted by this
// point.
DCHECK(network_contexts_.empty());
}
void NetworkService::set_os_crypt_is_configured() {
os_crypt_config_set_ = true;
}
std::unique_ptr<NetworkService> NetworkService::Create(
mojom::NetworkServiceRequest request,
net::NetLog* net_log) {
return std::make_unique<NetworkService>(nullptr, std::move(request), net_log);
}
std::unique_ptr<mojom::NetworkContext>
NetworkService::CreateNetworkContextWithBuilder(
mojom::NetworkContextRequest request,
mojom::NetworkContextParamsPtr params,
std::unique_ptr<URLRequestContextBuilderMojo> builder,
net::URLRequestContext** url_request_context) {
std::unique_ptr<NetworkContext> network_context =
std::make_unique<NetworkContext>(this, std::move(request),
std::move(params), std::move(builder));
*url_request_context = network_context->url_request_context();
return network_context;
}
void NetworkService::SetHostResolver(
std::unique_ptr<net::HostResolver> host_resolver) {
DCHECK(network_contexts_.empty());
host_resolver_ = std::move(host_resolver);
}
std::unique_ptr<NetworkService> NetworkService::CreateForTesting() {
return base::WrapUnique(
new NetworkService(std::make_unique<service_manager::BinderRegistry>()));
}
void NetworkService::RegisterNetworkContext(NetworkContext* network_context) {
// If IsPrimaryNetworkContext() is true, there must be no other
// NetworkContexts created yet.
DCHECK(!network_context->IsPrimaryNetworkContext() ||
network_contexts_.empty());
DCHECK_EQ(0u, network_contexts_.count(network_context));
network_contexts_.insert(network_context);
if (quic_disabled_)
network_context->DisableQuic();
}
void NetworkService::DeregisterNetworkContext(NetworkContext* network_context) {
// If the NetworkContext is bthe primary network context, all other
// NetworkContexts must already have been destroyed.
DCHECK(!network_context->IsPrimaryNetworkContext() ||
network_contexts_.size() == 1);
DCHECK_EQ(1u, network_contexts_.count(network_context));
network_contexts_.erase(network_context);
}
void NetworkService::CreateNetLogEntriesForActiveObjects(
net::NetLog::ThreadSafeObserver* observer) {
std::set<net::URLRequestContext*> contexts;
for (NetworkContext* nc : network_contexts_)
contexts.insert(nc->url_request_context());
return net::CreateNetLogEntriesForActiveObjects(contexts, observer);
}
void NetworkService::SetClient(mojom::NetworkServiceClientPtr client) {
client_ = std::move(client);
}
void NetworkService::StartNetLog(base::File file,
base::Value client_constants) {
DCHECK(client_constants.is_dict());
std::unique_ptr<base::DictionaryValue> constants = net::GetNetConstants();
constants->MergeDictionary(&client_constants);
owned_net_log_->ObserveFileWithConstants(std::move(file),
std::move(*constants));
}
void NetworkService::CreateNetworkContext(
mojom::NetworkContextRequest request,
mojom::NetworkContextParamsPtr params) {
// Only the first created NetworkContext can have |primary_next_context| set
// to true.
DCHECK(!params->primary_network_context || network_contexts_.empty());
owned_network_contexts_.emplace(std::make_unique<NetworkContext>(
this, std::move(request), std::move(params),
base::BindOnce(&NetworkService::OnNetworkContextConnectionClosed,
base::Unretained(this))));
}
void NetworkService::ConfigureStubHostResolver(
bool stub_resolver_enabled,
base::Optional<std::vector<network::mojom::DnsOverHttpsServerPtr>>
dns_over_https_servers) {
// If the stub resolver is not enabled, |dns_over_https_servers| has no
// effect.
DCHECK(stub_resolver_enabled || !dns_over_https_servers);
DCHECK(!dns_over_https_servers || !dns_over_https_servers->empty());
// Enable or disable the stub resolver, as needed. "DnsClient" is class that
// implements the stub resolver.
host_resolver_->SetDnsClientEnabled(stub_resolver_enabled);
// Configure DNS over HTTPS.
host_resolver_->ClearDnsOverHttpsServers();
if (!dns_over_https_servers)
return;
for (auto* network_context : network_contexts_) {
if (!network_context->IsPrimaryNetworkContext())
continue;
host_resolver_->SetRequestContext(network_context->url_request_context());
for (const auto& doh_server : *dns_over_https_servers) {
host_resolver_->AddDnsOverHttpsServer(doh_server->url.spec(),
doh_server->use_posts);
}
return;
}
// Execution should generally not reach this line, but could run into races
// with teardown, or restarting a crashed network process, that could
// theoretically result in reaching it.
}
void NetworkService::DisableQuic() {
quic_disabled_ = true;
for (auto* network_context : network_contexts_) {
network_context->DisableQuic();
}
}
void NetworkService::SetUpHttpAuth(
mojom::HttpAuthStaticParamsPtr http_auth_static_params) {
DCHECK(!http_auth_handler_factory_);
http_auth_handler_factory_ = net::HttpAuthHandlerRegistryFactory::Create(
host_resolver_.get(), &http_auth_preferences_,
http_auth_static_params->supported_schemes
#if defined(OS_CHROMEOS)
,
http_auth_static_params->allow_gssapi_library_load
#elif (defined(OS_POSIX) && !defined(OS_ANDROID)) || defined(OS_FUCHSIA)
,
http_auth_static_params->gssapi_library_name
#endif
);
}
void NetworkService::ConfigureHttpAuthPrefs(
mojom::HttpAuthDynamicParamsPtr http_auth_dynamic_params) {
http_auth_preferences_.SetServerWhitelist(
http_auth_dynamic_params->server_whitelist);
http_auth_preferences_.SetDelegateWhitelist(
http_auth_dynamic_params->delegate_whitelist);
http_auth_preferences_.set_negotiate_disable_cname_lookup(
http_auth_dynamic_params->negotiate_disable_cname_lookup);
http_auth_preferences_.set_negotiate_enable_port(
http_auth_dynamic_params->enable_negotiate_port);
#if defined(OS_POSIX) || defined(OS_FUCHSIA)
http_auth_preferences_.set_ntlm_v2_enabled(
http_auth_dynamic_params->ntlm_v2_enabled);
#endif
#if defined(OS_ANDROID)
http_auth_preferences_.set_auth_android_negotiate_account_type(
http_auth_dynamic_params->android_negotiate_account_type);
#endif
}
void NetworkService::SetRawHeadersAccess(uint32_t process_id, bool allow) {
DCHECK(process_id);
if (allow)
processes_with_raw_headers_access_.insert(process_id);
else
processes_with_raw_headers_access_.erase(process_id);
}
bool NetworkService::HasRawHeadersAccess(uint32_t process_id) const {
// Allow raw headers for browser-initiated requests.
if (!process_id)
return true;
return processes_with_raw_headers_access_.find(process_id) !=
processes_with_raw_headers_access_.end();
}
net::NetLog* NetworkService::net_log() const {
return net_log_;
}
void NetworkService::GetNetworkChangeManager(
mojom::NetworkChangeManagerRequest request) {
network_change_manager_->AddRequest(std::move(request));
}
void NetworkService::GetNetworkQualityEstimatorManager(
mojom::NetworkQualityEstimatorManagerRequest request) {
network_quality_estimator_manager_->AddRequest(std::move(request));
}
void NetworkService::GetTotalNetworkUsages(
mojom::NetworkService::GetTotalNetworkUsagesCallback callback) {
std::move(callback).Run(network_usage_accumulator_->GetTotalNetworkUsages());
}
void NetworkService::UpdateSignedTreeHead(const net::ct::SignedTreeHead& sth) {
sth_distributor_->NewSTHObserved(sth);
}
#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
void NetworkService::SetCryptConfig(mojom::CryptConfigPtr crypt_config) {
#if !defined(IS_CHROMECAST)
DCHECK(!os_crypt_config_set_);
auto config = std::make_unique<os_crypt::Config>();
config->store = crypt_config->store;
config->product_name = crypt_config->product_name;
config->main_thread_runner = base::ThreadTaskRunnerHandle::Get();
config->should_use_preference = crypt_config->should_use_preference;
config->user_data_path = crypt_config->user_data_path;
OSCrypt::SetConfig(std::move(config));
os_crypt_config_set_ = true;
#endif
}
#endif
net::HttpAuthHandlerFactory* NetworkService::GetHttpAuthHandlerFactory() {
if (!http_auth_handler_factory_) {
http_auth_handler_factory_ = net::HttpAuthHandlerFactory::CreateDefault(
host_resolver_.get(), &http_auth_preferences_);
}
return http_auth_handler_factory_.get();
}
certificate_transparency::STHReporter* NetworkService::sth_reporter() {
return sth_distributor_.get();
}
void NetworkService::OnBindInterface(
const service_manager::BindSourceInfo& source_info,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) {
registry_->BindInterface(interface_name, std::move(interface_pipe));
}
void NetworkService::DestroyNetworkContexts() {
// Delete NetworkContexts. If there's a primary NetworkContext, it must be
// deleted after all other NetworkContexts, to avoid use-after-frees.
for (auto it = owned_network_contexts_.begin();
it != owned_network_contexts_.end();) {
const auto last = it;
++it;
if (!(*last)->IsPrimaryNetworkContext())
owned_network_contexts_.erase(last);
}
// If DNS over HTTPS is enabled, the HostResolver is currently using the
// primary NetworkContext to do DNS lookups, so need to tell the HostResolver
// to stop using DNS over HTTPS before destroying the primary NetworkContext.
// The ClearDnsOverHttpsServers() call will will fail any in-progress DNS
// lookups, but only if DNS over HTTPS is currently enabled.
host_resolver_->ClearDnsOverHttpsServers();
host_resolver_->SetRequestContext(nullptr);
DCHECK_LE(owned_network_contexts_.size(), 1u);
owned_network_contexts_.clear();
}
void NetworkService::OnNetworkContextConnectionClosed(
NetworkContext* network_context) {
if (network_context->IsPrimaryNetworkContext()) {
DestroyNetworkContexts();
return;
}
auto it = owned_network_contexts_.find(network_context);
DCHECK(it != owned_network_contexts_.end());
owned_network_contexts_.erase(it);
}
void NetworkService::Bind(mojom::NetworkServiceRequest request) {
DCHECK(!binding_.is_bound());
binding_.Bind(std::move(request));
}
NetworkService* NetworkService::GetNetworkServiceForTesting() {
return g_network_service;
}
} // namespace network