From c9db1113a64b5b41cba57b7f17fdf1cc6dda80e6 Mon Sep 17 00:00:00 2001 From: Andre Moreira Magalhaes Date: Thu, 17 Dec 2020 20:49:24 -0300 Subject: [PATCH] Add support for respecting system proxy settings Closes https://github.com/flathub/org.chromium.Chromium/issues/36 --- org.chromium.Chromium.metainfo.xml | 1 + org.chromium.Chromium.yaml | 1 + ...respecting-system-proxy-settings-whe.patch | 504 ++++++++++++++++++ 3 files changed, 506 insertions(+) create mode 100644 patches/0023-Add-support-for-respecting-system-proxy-settings-whe.patch diff --git a/org.chromium.Chromium.metainfo.xml b/org.chromium.Chromium.metainfo.xml index 9560ae9b..2e00bf23 100644 --- a/org.chromium.Chromium.metainfo.xml +++ b/org.chromium.Chromium.metainfo.xml @@ -26,6 +26,7 @@ + diff --git a/org.chromium.Chromium.yaml b/org.chromium.Chromium.yaml index 350c5e00..726e89a2 100644 --- a/org.chromium.Chromium.yaml +++ b/org.chromium.Chromium.yaml @@ -258,6 +258,7 @@ modules: - patches/0020-Add-OpenURI-portal-support-for-opening-directories.patch - patches/0021-Remove-references-to-enable-dse-memoryssa.patch - patches/0022-Enable-new-dtags-on-non-component-builds.patch + - patches/0023-Implement-support-for-respecting-system-proxy-settin.patch - type: file path: org.chromium.Chromium.desktop - type: file diff --git a/patches/0023-Add-support-for-respecting-system-proxy-settings-whe.patch b/patches/0023-Add-support-for-respecting-system-proxy-settings-whe.patch new file mode 100644 index 00000000..dfa837e1 --- /dev/null +++ b/patches/0023-Add-support-for-respecting-system-proxy-settings-whe.patch @@ -0,0 +1,504 @@ +From ac2aa05e6f6a257e629c8bb9e868f93bda5b67f1 Mon Sep 17 00:00:00 2001 +From: Andre Moreira Magalhaes +Date: Thu, 17 Dec 2020 19:05:10 -0300 +Subject: [PATCH 23/23] Add support for respecting system proxy settings when + running on Linux/Flatpak + +Some general notes on implementation: +- The implementation uses the same codepath used when + `--winhttp-proxy-resolver` is passed as param, which uses the + `net::ProxyResolver` interface to determine the proxy given an url: + - This is due to limitations in the portal APIs which don't allow + access to the system proxy settings, and with that no reliable + way to implement a proper ProxyConfigService when on flatpak + - Despite the name of the param, this flag is not Windows(tm) + specific and also used on Mac builds + - This param may be removed at some point in the future, see + https://bugs.chromium.org/p/chromium/issues/detail?id=644030 but + it should be replaced with + https://bugs.chromium.org/p/chromium/issues/detail?id=1032820, + which from the looks of it would not require too many changes + to the impl here + - To avoid having to pass this param, this codepath is enabled + automatically when detecting we are running on flatpak +- This means that proxy resolution is done by invoking + `net::ProxyResolver::GetProxyForURL()` (instead of relying on the + internal chromium mechanism based on the proxy settings, set to + "mode=auto" in this case), which in turn invokes + `g_proxy_resolver_lookup` which uses the portal API via D-Bus + - To avoid having one D-Bus call per + `net::ProxyResolver::GetProxyForURL()`, this implements a (very) + simple cache that gets invalidated every 1min (same default + timeout used on chromium dns refresh) +- Another issue is that the impl will invoke the portal even if + proxy is disabled in the system, also because of a limitation + in the portal API where there is no way to know whether the + proxy is disabled +- We could skip using the portal on KDE, given the flatpak has + access to $HOME today (and the impl just reads .ini files), + but this impl uses the same codepath no matter the DE when running + on flatpak + +Closes https://github.com/flathub/org.chromium.Chromium/issues/36 +--- + chrome/app/generated_resources.grd | 6 + + .../net/system_network_context_manager.cc | 9 +- + chrome/browser/ui/webui/about_ui.cc | 14 +- + net/BUILD.gn | 3 + + .../configured_proxy_resolution_service.cc | 7 +- + .../proxy_config_service_linux.cc | 10 +- + net/proxy_resolution/proxy_resolver_linux.cc | 234 ++++++++++++++++++ + net/proxy_resolution/proxy_resolver_linux.h | 31 +++ + 8 files changed, 310 insertions(+), 4 deletions(-) + create mode 100644 net/proxy_resolution/proxy_resolver_linux.cc + create mode 100644 net/proxy_resolution/proxy_resolver_linux.h + +diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd +index eca8e40478142..c83ef84dcc7e6 100644 +--- a/chrome/app/generated_resources.grd ++++ b/chrome/app/generated_resources.grd +@@ -9295,6 +9295,12 @@ Please help our engineers fix this problem. Tell us what happened right before y + + <p>But you can still configure via the command line. Please see <code>man $2google-chrome</code> for more information on flags and environment variables.</p> + ++ ++ <p>The flatpak version of $1Google Chrome does not support changing the system proxy settings. However, it does respect those settings.</p> ++ <p>If you need to adjust the proxy settings, please do so through the configuration system of your desktop environment.</p> ++ ++ <p>You can also override your system proxy settings via the command line. Please see <code>man $2google-chrome</code> for more information on flags and environment variables.</p> ++ + + + +diff --git a/chrome/browser/net/system_network_context_manager.cc b/chrome/browser/net/system_network_context_manager.cc +index c53056f7a4ba5..b0d0d422912a9 100644 +--- a/chrome/browser/net/system_network_context_manager.cc ++++ b/chrome/browser/net/system_network_context_manager.cc +@@ -80,6 +80,7 @@ + #if defined(OS_LINUX) && !defined(OS_CHROMEOS) + #include "chrome/common/chrome_paths_internal.h" + #include "chrome/grit/chromium_strings.h" ++#include "sandbox/linux/services/flatpak_sandbox.h" + #include "ui/base/l10n/l10n_util.h" + #endif // defined(OS_LINUX) && !defined(OS_CHROMEOS) + +@@ -579,7 +580,13 @@ void SystemNetworkContextManager::ConfigureDefaultNetworkContextParams( + // or if it does work, now. + // Should be possible now that a private isolate is used. + // http://crbug.com/474654 +- if (!command_line.HasSwitch(switches::kWinHttpProxyResolver)) { ++ bool use_system_proxy_resolver = command_line.HasSwitch(switches::kWinHttpProxyResolver); ++#if defined(OS_LINUX) ++ bool use_flatpak_sandbox = (sandbox::FlatpakSandbox::GetInstance()->GetSandboxLevel() > ++ sandbox::FlatpakSandbox::SandboxLevel::kNone); ++ use_system_proxy_resolver |= use_flatpak_sandbox; ++#endif ++ if (!use_system_proxy_resolver) { + if (command_line.HasSwitch(switches::kSingleProcess)) { + LOG(ERROR) << "Cannot use V8 Proxy resolver in single process mode."; + } else { +diff --git a/chrome/browser/ui/webui/about_ui.cc b/chrome/browser/ui/webui/about_ui.cc +index ba4753e68de1b..8cff42d24ea59 100644 +--- a/chrome/browser/ui/webui/about_ui.cc ++++ b/chrome/browser/ui/webui/about_ui.cc +@@ -93,6 +93,10 @@ + #include "third_party/cros_system_api/dbus/service_constants.h" + #endif + ++#if defined(OS_LINUX) ++#include "sandbox/linux/services/flatpak_sandbox.h" ++#endif ++ + using content::BrowserThread; + + namespace { +@@ -587,8 +591,16 @@ std::string AboutLinuxProxyConfig() { + data.append(""); + AppendBody(&data); + base::FilePath binary = base::CommandLine::ForCurrentProcess()->GetProgram(); ++ ++ int id = IDS_ABOUT_LINUX_PROXY_CONFIG_BODY; ++#if defined(OS_LINUX) ++ bool use_flatpak_sandbox = (sandbox::FlatpakSandbox::GetInstance()->GetSandboxLevel() > ++ sandbox::FlatpakSandbox::SandboxLevel::kNone); ++ if (use_flatpak_sandbox) ++ id = IDS_ABOUT_LINUX_PROXY_CONFIG_FLATPAK_BODY; ++#endif + data.append( +- l10n_util::GetStringFUTF8(IDS_ABOUT_LINUX_PROXY_CONFIG_BODY, ++ l10n_util::GetStringFUTF8(id, + l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), + base::ASCIIToUTF16(binary.BaseName().value()))); + AppendFooter(&data); +diff --git a/net/BUILD.gn b/net/BUILD.gn +index c399590e2b02d..77be307aec8b8 100644 +--- a/net/BUILD.gn ++++ b/net/BUILD.gn +@@ -1248,6 +1248,8 @@ component("net") { + "base/network_change_notifier_linux.h", + "proxy_resolution/proxy_config_service_linux.cc", + "proxy_resolution/proxy_config_service_linux.h", ++ "proxy_resolution/proxy_resolver_linux.cc", ++ "proxy_resolution/proxy_resolver_linux.h", + ] + } + +@@ -1712,6 +1714,7 @@ source_set("net_deps") { + + if (use_gio) { + public_configs += [ "//build/linux:gio_config" ] ++ public_deps += [ "//dbus" ] + } + + if (is_android) { +diff --git a/net/proxy_resolution/configured_proxy_resolution_service.cc b/net/proxy_resolution/configured_proxy_resolution_service.cc +index 7a7ca7518ee05..2ba3dc64a7711 100644 +--- a/net/proxy_resolution/configured_proxy_resolution_service.cc ++++ b/net/proxy_resolution/configured_proxy_resolution_service.cc +@@ -8,6 +8,8 @@ + #include + #include + ++#include "base/debug/stack_trace.h" ++ + #include "base/bind.h" + #include "base/bind_helpers.h" + #include "base/compiler_specific.h" +@@ -48,6 +50,7 @@ + #include "net/proxy_resolution/proxy_resolver_mac.h" + #elif defined(OS_LINUX) && !defined(OS_CHROMEOS) + #include "net/proxy_resolution/proxy_config_service_linux.h" ++#include "net/proxy_resolution/proxy_resolver_linux.h" + #elif defined(OS_ANDROID) + #include "net/proxy_resolution/proxy_config_service_android.h" + #endif +@@ -258,6 +261,8 @@ class ProxyResolverFactoryForSystem : public MultiThreadedProxyResolverFactory { + return std::make_unique(); + #elif defined(OS_APPLE) + return std::make_unique(); ++#elif defined(OS_LINUX) ++ return std::make_unique(); + #else + NOTREACHED(); + return nullptr; +@@ -265,7 +270,7 @@ class ProxyResolverFactoryForSystem : public MultiThreadedProxyResolverFactory { + } + + static bool IsSupported() { +-#if defined(OS_WIN) || defined(OS_APPLE) ++#if defined(OS_WIN) || defined(OS_APPLE) || defined(OS_LINUX) + return true; + #else + return false; +diff --git a/net/proxy_resolution/proxy_config_service_linux.cc b/net/proxy_resolution/proxy_config_service_linux.cc +index c11685ff4f221..cba0225d69512 100644 +--- a/net/proxy_resolution/proxy_config_service_linux.cc ++++ b/net/proxy_resolution/proxy_config_service_linux.cc +@@ -31,6 +31,7 @@ + #include "base/threading/thread_restrictions.h" + #include "base/timer/timer.h" + #include "net/base/proxy_server.h" ++#include "sandbox/linux/services/flatpak_sandbox.h" + + #if defined(USE_GIO) + #include +@@ -1042,11 +1043,18 @@ ProxyConfigServiceLinux::Delegate::GetConfigFromSettings() { + ProxyConfig config; + + std::string mode; +- if (!setting_getter_->GetString(SettingGetter::PROXY_MODE, &mode)) { ++ bool use_flatpak_sandbox = (sandbox::FlatpakSandbox::GetInstance()->GetSandboxLevel() > ++ sandbox::FlatpakSandbox::SandboxLevel::kNone); ++ ++ if (!use_flatpak_sandbox && ++ !setting_getter_->GetString(SettingGetter::PROXY_MODE, &mode)) { + // We expect this to always be set, so if we don't see it then we probably + // have a gsettings problem, and so we don't have a valid proxy config. + return base::nullopt; ++ } else if (use_flatpak_sandbox) { ++ mode = "auto"; + } ++ + if (mode == "none") { + // Specifically specifies no proxy. + return ProxyConfigWithAnnotation( +diff --git a/net/proxy_resolution/proxy_resolver_linux.cc b/net/proxy_resolution/proxy_resolver_linux.cc +new file mode 100644 +index 0000000000000..93e2f5c2b399e +--- /dev/null ++++ b/net/proxy_resolution/proxy_resolver_linux.cc +@@ -0,0 +1,234 @@ ++// Copyright (c) 2020 Endless OS Foundation LLC ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#include "net/proxy_resolution/proxy_resolver_linux.h" ++ ++#include "base/message_loop/message_pump_type.h" ++#include "base/no_destructor.h" ++#include "base/strings/string_util.h" ++#include "base/synchronization/waitable_event.h" ++#include "base/threading/thread_task_runner_handle.h" ++#include "base/threading/thread.h" ++#include "base/time/time.h" ++#include "base/timer/timer.h" ++#include "net/base/net_errors.h" ++#include "net/proxy_resolution/proxy_info.h" ++#include "net/proxy_resolution/proxy_resolver.h" ++#include "sandbox/linux/services/flatpak_sandbox.h" ++ ++#if defined(USE_GIO) ++#include ++#endif // defined(USE_GIO) ++ ++namespace net { ++ ++class NetworkIsolationKey; ++ ++namespace { ++ ++class GLibThread { ++ public: ++ scoped_refptr task_runner() const { ++ return task_runner_; ++ } ++ ++ private: ++ friend base::NoDestructor; ++ ++ GLibThread() : thread_("GLibThread") { ++ base::Thread::Options options; ++ options.message_pump_type = base::MessagePumpType::UI; ++ options.joinable = false; ++ thread_.StartWithOptions(options); ++ task_runner_ = thread_.task_runner(); ++ thread_.DetachFromSequence(); ++ } ++ ++ ~GLibThread() = delete; ++ ++ // The |thread_| object is not thread-safe. This should not be accessed ++ // outside the constructor. ++ base::Thread thread_; ++ ++ // Saved TaskRunner handle that can be accessed from any thread. ++ scoped_refptr task_runner_; ++ ++ DISALLOW_COPY_AND_ASSIGN(GLibThread); ++}; ++ ++scoped_refptr GetGLibThread() { ++ static base::NoDestructor glib_thread; ++ return glib_thread->task_runner(); ++} ++ ++#if defined(USE_GIO) ++// Same TTL used for DNS caches, see net/dns/host_resolver_manager.cc ++static const unsigned kCacheExpireSeconds = 60; ++ ++class ProxyResolverLinuxImplPortal : public ProxyResolver { ++ public: ++ struct GetProxyForURLData { ++ public: ++ GetProxyForURLData(ProxyResolverLinuxImplPortal* proxy_resolver, ++ const GURL& query_url, ++ ProxyInfo* results) ++ : proxy_resolver(proxy_resolver) ++ , query_url(query_url) ++ , results(results) { ++ waitable_event.Reset(); ++ } ++ ++ ProxyResolverLinuxImplPortal* proxy_resolver; ++ GURL query_url; ++ ProxyInfo* results; ++ base::WaitableEvent waitable_event; ++ ++ private: ++ DISALLOW_COPY_AND_ASSIGN(GetProxyForURLData); ++ }; ++ ++ explicit ProxyResolverLinuxImplPortal(); ++ ~ProxyResolverLinuxImplPortal() override; ++ ++ // ProxyResolver methods: ++ int GetProxyForURL(const GURL& url, ++ const NetworkIsolationKey& network_isolation_key, ++ ProxyInfo* results, ++ CompletionOnceCallback callback, ++ std::unique_ptr* request, ++ const NetLogWithSource& net_log) override; ++ ++ private: ++ void InvalidateCache(); ++ void DoInvalidateCache(); ++ ++ void DoGetProxyForURL(GetProxyForURLData *user_data); ++ static void DoGetProxyForURLFinish(GProxyResolver *resolver, ++ GAsyncResult *res, ++ GetProxyForURLData *user_data); ++ ++ scoped_refptr glib_thread_task_runner_; ++ ++ // only used from glib thread ++ std::map cache_; ++ ++ base::RepeatingTimer cacheInvalidateTimer_; ++ GCancellable* cancellable_; ++}; ++ ++ProxyResolverLinuxImplPortal::ProxyResolverLinuxImplPortal() ++{ ++ glib_thread_task_runner_ = GetGLibThread(); ++ ++ cancellable_ = g_cancellable_new(); ++ ++ cacheInvalidateTimer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kCacheExpireSeconds), ++ this, &ProxyResolverLinuxImplPortal::InvalidateCache); ++} ++ ++ProxyResolverLinuxImplPortal::~ProxyResolverLinuxImplPortal() ++{ ++ g_cancellable_cancel(cancellable_); ++ glib_thread_task_runner_->RunsTasksInCurrentSequence(); ++ g_clear_object(&cancellable_); ++ cacheInvalidateTimer_.Stop(); ++} ++ ++int ProxyResolverLinuxImplPortal::GetProxyForURL( ++ const GURL& query_url, ++ const NetworkIsolationKey& network_isolation_key, ++ ProxyInfo* results, ++ CompletionOnceCallback callback, ++ std::unique_ptr* request, ++ const NetLogWithSource& net_log) { ++ ProxyResolverLinuxImplPortal::GetProxyForURLData data(this, query_url, results); ++ ++ // Let's run the resolver in the glib thread ++ glib_thread_task_runner_->PostTask( ++ FROM_HERE, ++ base::BindOnce(&ProxyResolverLinuxImplPortal::DoGetProxyForURL, ++ base::Unretained(this), &data)); ++ ++ // We wait here as this class is used by MultiThreadProxyResolver which expects ++ // a sync call ++ data.waitable_event.Wait(); ++ ++ return OK; ++} ++ ++void ProxyResolverLinuxImplPortal::InvalidateCache() ++{ ++ // Run it in the glib thread so we don't have to worry about locks ++ glib_thread_task_runner_->PostTask( ++ FROM_HERE, ++ base::BindOnce(&ProxyResolverLinuxImplPortal::DoInvalidateCache, ++ base::Unretained(this))); ++} ++ ++void ProxyResolverLinuxImplPortal::DoInvalidateCache() ++{ ++ // TODO: make it smarter ++ cache_.clear(); ++} ++ ++void ProxyResolverLinuxImplPortal::DoGetProxyForURL( ++ ProxyResolverLinuxImplPortal::GetProxyForURLData *user_data) { ++ std::string query_url = user_data->query_url.spec(); ++ std::map::const_iterator it = cache_.find(query_url); ++ if (it != cache_.end()) { ++ user_data->results->UseNamedProxy(it->second); ++ user_data->waitable_event.Signal(); ++ return; ++ } ++ ++ GProxyResolver *resolver = g_proxy_resolver_get_default(); ++ g_proxy_resolver_lookup_async(resolver, ++ query_url.c_str(), cancellable_, ++ (GAsyncReadyCallback) &ProxyResolverLinuxImplPortal::DoGetProxyForURLFinish, ++ user_data); ++} ++ ++void ProxyResolverLinuxImplPortal::DoGetProxyForURLFinish( ++ GProxyResolver *resolver, ++ GAsyncResult *res, ++ ProxyResolverLinuxImplPortal::GetProxyForURLData *user_data) ++{ ++ // TODO: handle/warn on error ++ g_auto(GStrv) proxy_list = g_proxy_resolver_lookup_finish(resolver, res, NULL); ++ if (proxy_list) { ++ g_autofree gchar *proxy_list_str = g_strjoinv(";", proxy_list); ++ if (proxy_list_str && proxy_list_str[0] != '\0') { ++ std::string query_url = user_data->query_url.spec(); ++ user_data->proxy_resolver->cache_[query_url] = proxy_list_str; ++ user_data->results->UseNamedProxy(proxy_list_str); ++ } ++ } ++ ++ user_data->waitable_event.Signal(); ++} ++ ++#endif // defined(USE_GIO) ++ ++} // namespace ++ ++ProxyResolverFactoryLinux::ProxyResolverFactoryLinux() ++ : ProxyResolverFactory(false /* expects_pac_bytes */) { ++} ++ ++int ProxyResolverFactoryLinux::CreateProxyResolver( ++ const scoped_refptr& pac_script, ++ std::unique_ptr* resolver, ++ CompletionOnceCallback callback, ++ std::unique_ptr* request) { ++#if defined(USE_GIO) ++ if (sandbox::FlatpakSandbox::GetInstance()->GetSandboxLevel() > ++ sandbox::FlatpakSandbox::SandboxLevel::kNone) { ++ resolver->reset(new ProxyResolverLinuxImplPortal()); ++ return OK; ++ } ++#endif ++ return ERR_NOT_IMPLEMENTED; ++} ++ ++} // namespace net +diff --git a/net/proxy_resolution/proxy_resolver_linux.h b/net/proxy_resolution/proxy_resolver_linux.h +new file mode 100644 +index 0000000000000..0a7a22aa44d59 +--- /dev/null ++++ b/net/proxy_resolution/proxy_resolver_linux.h +@@ -0,0 +1,31 @@ ++// Copyright (c) 2020 Endless OS Foundation LLC ++// Use of this source code is governed by a BSD-style license that can be ++// found in the LICENSE file. ++ ++#ifndef NET_PROXY_RESOLUTION_PROXY_RESOLVER_LINUX_H_ ++#define NET_PROXY_RESOLUTION_PROXY_RESOLVER_LINUX_H_ ++ ++#include "base/macros.h" ++#include "net/base/completion_once_callback.h" ++#include "net/base/net_export.h" ++#include "net/proxy_resolution/proxy_resolver_factory.h" ++#include "url/gurl.h" ++ ++namespace net { ++ ++class NET_EXPORT ProxyResolverFactoryLinux : public ProxyResolverFactory { ++ public: ++ ProxyResolverFactoryLinux(); ++ ++ int CreateProxyResolver(const scoped_refptr& pac_script, ++ std::unique_ptr* resolver, ++ CompletionOnceCallback callback, ++ std::unique_ptr* request) override; ++ ++ private: ++ DISALLOW_COPY_AND_ASSIGN(ProxyResolverFactoryLinux); ++}; ++ ++} // namespace net ++ ++#endif // NET_PROXY_RESOLUTION_PROXY_RESOLVER_LINUX_H_ +-- +2.20.1 +