// 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 "content/browser/network_service_instance_impl.h" #include #include #include #include #include "base/bind.h" #include "base/environment.h" #include "base/feature_list.h" #include "base/files/file.h" #include "base/message_loop/message_pump_type.h" #include "base/metrics/histogram_macros.h" #include "base/no_destructor.h" #include "base/sequenced_task_runner.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "base/synchronization/waitable_event.h" #include "base/threading/sequence_local_storage_slot.h" #include "base/threading/thread.h" #include "base/threading/thread_restrictions.h" #include "build/build_config.h" #include "content/browser/browser_main_loop.h" #include "content/browser/network_service_client.h" #include "content/browser/service_sandbox_type.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/content_browser_client.h" #include "content/public/browser/network_service_instance.h" #include "content/public/browser/service_process_host.h" #include "content/public/browser/system_connector.h" #include "content/public/common/content_client.h" #include "content/public/common/network_service_util.h" #include "content/public/common/service_names.mojom.h" #include "mojo/public/cpp/bindings/pending_receiver.h" #include "mojo/public/cpp/bindings/remote.h" #include "net/log/net_log_util.h" #include "services/cert_verifier/cert_verifier_service_factory.h" #include "services/network/network_service.h" #include "services/network/public/cpp/features.h" #include "services/network/public/cpp/network_switches.h" #include "services/network/public/mojom/net_log.mojom.h" #include "services/network/public/mojom/network_change_manager.mojom.h" #include "services/network/public/mojom/network_service.mojom.h" #include "services/network/public/mojom/network_service_test.mojom.h" namespace content { namespace { #if defined(OS_POSIX) // Environment variable pointing to credential cache file. constexpr char kKrb5CCEnvName[] = "KRB5CCNAME"; // Environment variable pointing to Kerberos config file. constexpr char kKrb5ConfEnvName[] = "KRB5_CONFIG"; #endif bool g_force_create_network_service_directly = false; mojo::Remote* g_network_service_remote = nullptr; network::NetworkConnectionTracker* g_network_connection_tracker; bool g_network_service_is_responding = false; base::Time g_last_network_service_crash; std::unique_ptr& GetLocalNetworkService() { static base::NoDestructor< base::SequenceLocalStorageSlot>> service; return service->GetOrCreateValue(); } // If this feature is enabled, the Network Service will run on its own thread // when running in-process; otherwise it will run on the IO thread. // // On Chrome OS, the Network Service must run on the IO thread because // ProfileIOData and NetworkContext both try to set up NSS, which has to be // called from the IO thread. const base::Feature kNetworkServiceDedicatedThread { "NetworkServiceDedicatedThread", #if defined(OS_CHROMEOS) base::FEATURE_DISABLED_BY_DEFAULT #else base::FEATURE_ENABLED_BY_DEFAULT #endif }; base::Thread& GetNetworkServiceDedicatedThread() { static base::NoDestructor thread{"NetworkService"}; DCHECK(base::FeatureList::IsEnabled(kNetworkServiceDedicatedThread)); return *thread; } // The instance NetworkService used when hosting the service in-process. This is // set up by |CreateInProcessNetworkServiceOnThread()| and destroyed by // |ShutDownNetworkService()|. network::NetworkService* g_in_process_instance = nullptr; void CreateInProcessNetworkServiceOnThread( mojo::PendingReceiver receiver) { // The test interface doesn't need to be implemented in the in-process case. auto registry = std::make_unique(); registry->AddInterface(base::BindRepeating( [](mojo::PendingReceiver) {})); g_in_process_instance = new network::NetworkService( std::move(registry), std::move(receiver), true /* delay_initialization_until_set_client */); } scoped_refptr& GetNetworkTaskRunnerStorage() { static base::NoDestructor> storage; return *storage; } void CreateInProcessNetworkService( mojo::PendingReceiver receiver) { scoped_refptr task_runner; if (base::FeatureList::IsEnabled(kNetworkServiceDedicatedThread)) { base::Thread::Options options(base::MessagePumpType::IO, 0); GetNetworkServiceDedicatedThread().StartWithOptions(options); task_runner = GetNetworkServiceDedicatedThread().task_runner(); } else { task_runner = GetIOThreadTaskRunner({}); } GetNetworkTaskRunnerStorage() = std::move(task_runner); GetNetworkTaskRunner()->PostTask( FROM_HERE, base::BindOnce(&CreateInProcessNetworkServiceOnThread, std::move(receiver))); } network::mojom::NetworkServiceParamsPtr CreateNetworkServiceParams() { network::mojom::NetworkServiceParamsPtr network_service_params = network::mojom::NetworkServiceParams::New(); network_service_params->initial_connection_type = network::mojom::ConnectionType( net::NetworkChangeNotifier::GetConnectionType()); network_service_params->initial_connection_subtype = network::mojom::ConnectionSubtype( net::NetworkChangeNotifier::GetConnectionSubtype()); #if defined(OS_POSIX) // Send Kerberos environment variables to the network service. if (IsOutOfProcessNetworkService()) { std::unique_ptr env(base::Environment::Create()); std::string value; if (env->HasVar(kKrb5CCEnvName)) { env->GetVar(kKrb5CCEnvName, &value); network_service_params->environment.push_back( network::mojom::EnvironmentVariable::New(kKrb5CCEnvName, value)); } if (env->HasVar(kKrb5ConfEnvName)) { env->GetVar(kKrb5ConfEnvName, &value); network_service_params->environment.push_back( network::mojom::EnvironmentVariable::New(kKrb5ConfEnvName, value)); } } #endif return network_service_params; } void CreateNetworkServiceOnIOForTesting( mojo::PendingReceiver receiver, base::WaitableEvent* completion_event) { if (GetLocalNetworkService()) { GetLocalNetworkService()->Bind(std::move(receiver)); return; } GetLocalNetworkService() = std::make_unique( nullptr /* registry */, std::move(receiver), true /* delay_initialization_until_set_client */); GetLocalNetworkService()->StopMetricsTimerForTesting(); GetLocalNetworkService()->Initialize( network::mojom::NetworkServiceParams::New(), true /* mock_network_change_notifier */); if (completion_event) completion_event->Signal(); } void BindNetworkChangeManagerReceiver( mojo::PendingReceiver receiver) { GetNetworkService()->GetNetworkChangeManager(std::move(receiver)); } base::CallbackList& GetCrashHandlersList() { static base::NoDestructor> s_list; return *s_list; } void OnNetworkServiceCrash() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(g_network_service_remote); DCHECK(g_network_service_remote->is_bound()); DCHECK(!g_network_service_remote->is_connected()); g_last_network_service_crash = base::Time::Now(); GetCrashHandlersList().Notify(); } // Parses the desired granularity of NetLog capturing specified by the command // line. net::NetLogCaptureMode GetNetCaptureModeFromCommandLine( const base::CommandLine& command_line) { base::StringPiece switch_name = network::switches::kNetLogCaptureMode; if (command_line.HasSwitch(switch_name)) { std::string value = command_line.GetSwitchValueASCII(switch_name); if (value == "Default") return net::NetLogCaptureMode::kDefault; if (value == "IncludeSensitive") return net::NetLogCaptureMode::kIncludeSensitive; if (value == "Everything") return net::NetLogCaptureMode::kEverything; // Warn when using the old command line switches. if (value == "IncludeCookiesAndCredentials") { LOG(ERROR) << "Deprecated value for --" << switch_name << ". Use IncludeSensitive instead"; return net::NetLogCaptureMode::kIncludeSensitive; } if (value == "IncludeSocketBytes") { LOG(ERROR) << "Deprecated value for --" << switch_name << ". Use Everything instead"; return net::NetLogCaptureMode::kEverything; } LOG(ERROR) << "Unrecognized value for --" << switch_name; } return net::NetLogCaptureMode::kDefault; } static NetworkServiceClient* g_client = nullptr; } // namespace class NetworkServiceInstancePrivate { public: // Opens the specified file, blocking until the file is open. Used to open // files specified by network::switches::kLogNetLog or // network::switches::kSSLKeyLogFile. Since these arguments can be used to // debug startup behavior, asynchronously opening the file on another thread // would result in losing data, hence the need for blocking open operations. // |file_flags| specifies the flags passed to the base::File constructor call. // // ThreadRestrictions needs to be able to friend the class/method to allow // blocking, but can't friend CONTENT_EXPORT methods, so have it friend // NetworkServiceInstancePrivate instead of GetNetworkService(). static base::File BlockingOpenFile(const base::FilePath& path, int file_flags) { base::ScopedAllowBlocking allow_blocking; return base::File(path, file_flags); } }; network::mojom::NetworkService* GetNetworkService() { if (!g_network_service_remote) g_network_service_remote = new mojo::Remote; if (!g_network_service_remote->is_bound() || !g_network_service_remote->is_connected()) { bool service_was_bound = g_network_service_remote->is_bound(); g_network_service_remote->reset(); if (GetContentClient()->browser()->IsShuttingDown()) { // This happens at system shutdown, since in other scenarios the network // process would only be torn down once the message loop stopped running. // We don't want to start the network service again so just create message // pipe that's not bound to stop consumers from requesting creation of the // service. auto receiver = g_network_service_remote->BindNewPipeAndPassReceiver(); auto leaked_pipe = receiver.PassPipe().release(); } else { if (!g_force_create_network_service_directly) { mojo::PendingReceiver receiver = g_network_service_remote->BindNewPipeAndPassReceiver(); g_network_service_remote->set_disconnect_handler( base::BindOnce(&OnNetworkServiceCrash)); if (IsInProcessNetworkService()) { CreateInProcessNetworkService(std::move(receiver)); } else { if (service_was_bound) LOG(ERROR) << "Network service crashed, restarting service."; ServiceProcessHost::Launch( std::move(receiver), ServiceProcessHost::Options() .WithDisplayName(base::UTF8ToUTF16("Network Service")) .Pass()); } } else { // This should only be reached in unit tests. if (BrowserThread::CurrentlyOn(BrowserThread::IO)) { CreateNetworkServiceOnIOForTesting( g_network_service_remote->BindNewPipeAndPassReceiver(), /*completion_event=*/nullptr); } else { base::WaitableEvent event; GetIOThreadTaskRunner({})->PostTask( FROM_HERE, base::BindOnce( CreateNetworkServiceOnIOForTesting, g_network_service_remote->BindNewPipeAndPassReceiver(), base::Unretained(&event))); event.Wait(); } } mojo::PendingRemote client_remote; auto client_receiver = client_remote.InitWithNewPipeAndPassReceiver(); // Call SetClient before creating NetworkServiceClient, as the latter // might make requests to NetworkService that depend on initialization. (*g_network_service_remote) ->SetClient(std::move(client_remote), CreateNetworkServiceParams()); g_network_service_is_responding = false; g_network_service_remote->QueryVersion(base::BindOnce( [](base::Time start_time, uint32_t) { g_network_service_is_responding = true; base::TimeDelta delta = base::Time::Now() - start_time; UMA_HISTOGRAM_MEDIUM_TIMES("NetworkService.TimeToFirstResponse", delta); if (g_last_network_service_crash.is_null()) { UMA_HISTOGRAM_MEDIUM_TIMES( "NetworkService.TimeToFirstResponse.OnStartup", delta); } else { UMA_HISTOGRAM_MEDIUM_TIMES( "NetworkService.TimeToFirstResponse.AfterCrash", delta); } }, base::Time::Now())); delete g_client; // In case we're recreating the network service. g_client = new NetworkServiceClient(std::move(client_receiver)); const base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(network::switches::kLogNetLog)) { base::FilePath log_path = command_line->GetSwitchValuePath(network::switches::kLogNetLog); base::File file = NetworkServiceInstancePrivate::BlockingOpenFile( log_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE); if (!file.IsValid()) { LOG(ERROR) << "Failed opening NetLog: " << log_path.value(); } else { (*g_network_service_remote) ->StartNetLog( std::move(file), GetNetCaptureModeFromCommandLine(*command_line), GetContentClient()->browser()->GetNetLogConstants()); } } base::FilePath ssl_key_log_path; if (command_line->HasSwitch(network::switches::kSSLKeyLogFile)) { UMA_HISTOGRAM_ENUMERATION(kSSLKeyLogFileHistogram, SSLKeyLogFileAction::kSwitchFound); ssl_key_log_path = command_line->GetSwitchValuePath(network::switches::kSSLKeyLogFile); LOG_IF(WARNING, ssl_key_log_path.empty()) << "ssl-key-log-file argument missing"; } else { std::unique_ptr env(base::Environment::Create()); std::string env_str; if (env->GetVar("SSLKEYLOGFILE", &env_str)) { UMA_HISTOGRAM_ENUMERATION(kSSLKeyLogFileHistogram, SSLKeyLogFileAction::kEnvVarFound); #if defined(OS_WIN) // base::Environment returns environment variables in UTF-8 on // Windows. ssl_key_log_path = base::FilePath(base::UTF8ToUTF16(env_str)); #else ssl_key_log_path = base::FilePath(env_str); #endif } } if (!ssl_key_log_path.empty()) { base::File file = NetworkServiceInstancePrivate::BlockingOpenFile( ssl_key_log_path, base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND); if (!file.IsValid()) { LOG(ERROR) << "Failed opening SSL key log file: " << ssl_key_log_path.value(); } else { UMA_HISTOGRAM_ENUMERATION(kSSLKeyLogFileHistogram, SSLKeyLogFileAction::kLogFileEnabled); (*g_network_service_remote)->SetSSLKeyLogFile(std::move(file)); } } GetContentClient()->browser()->OnNetworkServiceCreated( g_network_service_remote->get()); } } return g_network_service_remote->get(); } std::unique_ptr::Subscription> RegisterNetworkServiceCrashHandler(base::RepeatingClosure handler) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); DCHECK(!handler.is_null()); return GetCrashHandlersList().Add(std::move(handler)); } #if defined(OS_CHROMEOS) net::NetworkChangeNotifier* GetNetworkChangeNotifier() { return BrowserMainLoop::GetInstance()->network_change_notifier(); } #endif void FlushNetworkServiceInstanceForTesting() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (g_network_service_remote) g_network_service_remote->FlushForTesting(); } network::NetworkConnectionTracker* GetNetworkConnectionTracker() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || !BrowserThread::IsThreadInitialized(BrowserThread::UI)); if (!g_network_connection_tracker) { g_network_connection_tracker = new network::NetworkConnectionTracker( base::BindRepeating(&BindNetworkChangeManagerReceiver)); } return g_network_connection_tracker; } void GetNetworkConnectionTrackerFromUIThread( base::OnceCallback callback) { GetUIThreadTaskRunner({base::TaskPriority::BEST_EFFORT}) ->PostTaskAndReplyWithResult(FROM_HERE, base::BindOnce(&GetNetworkConnectionTracker), std::move(callback)); } network::NetworkConnectionTrackerAsyncGetter CreateNetworkConnectionTrackerAsyncGetter() { return base::BindRepeating(&content::GetNetworkConnectionTrackerFromUIThread); } void SetNetworkConnectionTrackerForTesting( network::NetworkConnectionTracker* network_connection_tracker) { if (g_network_connection_tracker != network_connection_tracker) { DCHECK(!g_network_connection_tracker || !network_connection_tracker); g_network_connection_tracker = network_connection_tracker; } } const scoped_refptr& GetNetworkTaskRunner() { DCHECK(IsInProcessNetworkService()); return GetNetworkTaskRunnerStorage(); } void ForceCreateNetworkServiceDirectlyForTesting() { g_force_create_network_service_directly = true; } void ResetNetworkServiceForTesting() { ShutDownNetworkService(); } void ShutDownNetworkService() { delete g_network_service_remote; g_network_service_remote = nullptr; delete g_client; g_client = nullptr; if (g_in_process_instance) { GetNetworkTaskRunner()->DeleteSoon(FROM_HERE, g_in_process_instance); g_in_process_instance = nullptr; } GetNetworkTaskRunnerStorage().reset(); } NetworkServiceAvailability GetNetworkServiceAvailability() { if (!g_network_service_remote) return NetworkServiceAvailability::NOT_CREATED; else if (!g_network_service_remote->is_bound()) return NetworkServiceAvailability::NOT_BOUND; else if (!g_network_service_remote->is_connected()) return NetworkServiceAvailability::ENCOUNTERED_ERROR; else if (!g_network_service_is_responding) return NetworkServiceAvailability::NOT_RESPONDING; else return NetworkServiceAvailability::AVAILABLE; } base::TimeDelta GetTimeSinceLastNetworkServiceCrash() { if (g_last_network_service_crash.is_null()) return base::TimeDelta(); return base::Time::Now() - g_last_network_service_crash; } void PingNetworkService(base::OnceClosure closure) { GetNetworkService(); // Unfortunately, QueryVersion requires a RepeatingCallback. g_network_service_remote->QueryVersion(base::BindOnce( [](base::OnceClosure closure, uint32_t) { if (closure) std::move(closure).Run(); }, base::Passed(std::move(closure)))); } namespace { mojo::PendingRemote GetNewCertVerifierServiceRemote( cert_verifier::mojom::CertVerifierServiceFactory* cert_verifier_service_factory, network::mojom::CertVerifierCreationParamsPtr creation_params) { mojo::PendingRemote cert_verifier_remote; cert_verifier_service_factory->GetNewCertVerifier( cert_verifier_remote.InitWithNewPipeAndPassReceiver(), std::move(creation_params)); return cert_verifier_remote; } void CreateInProcessCertVerifierServiceOnThread( mojo::PendingReceiver receiver) { // Except in tests, our CertVerifierServiceFactoryImpl is a singleton. static base::NoDestructor cv_service_factory(std::move(receiver)); } // Owns the CertVerifierServiceFactory used by the browser. // Lives on the UI thread. class CertVerifierServiceFactoryOwner { public: CertVerifierServiceFactoryOwner() = default; CertVerifierServiceFactoryOwner(const CertVerifierServiceFactoryOwner&) = delete; CertVerifierServiceFactoryOwner& operator=( const CertVerifierServiceFactoryOwner&) = delete; ~CertVerifierServiceFactoryOwner() = delete; static CertVerifierServiceFactoryOwner* Get() { static base::NoDestructor cert_verifier_service_factory_owner; return &*cert_verifier_service_factory_owner; } // Passing nullptr will reset the current remote. void SetCertVerifierServiceFactoryForTesting( cert_verifier::mojom::CertVerifierServiceFactory* service_factory) { if (service_factory) { DCHECK(!service_factory_); } service_factory_ = service_factory; service_factory_remote_.reset(); } // Returns a pointer to a CertVerifierServiceFactory usable on the UI thread. cert_verifier::mojom::CertVerifierServiceFactory* GetCertVerifierServiceFactory() { if (!service_factory_) { #if defined(OS_CHROMEOS) // ChromeOS's in-process CertVerifierService should run on the IO thread // because it interacts with IO-bound NSS and ChromeOS user slots. // See for example InitializeNSSForChromeOSUser(). GetIOThreadTaskRunner({})->PostTask( FROM_HERE, base::BindOnce(&CreateInProcessCertVerifierServiceOnThread, service_factory_remote_.BindNewPipeAndPassReceiver())); #else CreateInProcessCertVerifierServiceOnThread( service_factory_remote_.BindNewPipeAndPassReceiver()); #endif service_factory_ = service_factory_remote_.get(); } return service_factory_; } private: // Bound to UI thread. mojo::Remote service_factory_remote_; cert_verifier::mojom::CertVerifierServiceFactory* service_factory_ = nullptr; }; } // namespace network::mojom::CertVerifierParamsPtr GetCertVerifierParams( network::mojom::CertVerifierCreationParamsPtr cert_verifier_creation_params) { if (!base::FeatureList::IsEnabled(network::features::kCertVerifierService)) { return network::mojom::CertVerifierParams::NewCreationParams( std::move(cert_verifier_creation_params)); } auto cv_service_remote_params = network::mojom::CertVerifierServiceRemoteParams::New(); // Create a cert verifier service. cv_service_remote_params ->cert_verifier_service = GetNewCertVerifierServiceRemote( CertVerifierServiceFactoryOwner::Get()->GetCertVerifierServiceFactory(), std::move(cert_verifier_creation_params)); return network::mojom::CertVerifierParams::NewRemoteParams( std::move(cv_service_remote_params)); } void SetCertVerifierServiceFactoryForTesting( cert_verifier::mojom::CertVerifierServiceFactory* service_factory) { CertVerifierServiceFactoryOwner::Get() ->SetCertVerifierServiceFactoryForTesting(service_factory); } } // namespace content