diff --git a/SOURCES/flatpak-Add-initial-sandbox-support.patch b/SOURCES/flatpak-Add-initial-sandbox-support.patch new file mode 100644 index 00000000..dae2d943 --- /dev/null +++ b/SOURCES/flatpak-Add-initial-sandbox-support.patch @@ -0,0 +1,1325 @@ +From ab11528f26a212417b0b6084b52c02e992fe43f7 Mon Sep 17 00:00:00 2001 +From: Ryan Gonzalez +Date: Tue, 17 Mar 2020 13:18:27 -0500 +Subject: [PATCH] flatpak: Add initial sandbox support + +--- + .gitignore | 1 + + base/threading/thread_restrictions.h | 5 + + .../sandbox_internals/sandbox_internals.ts | 4 + + .../ui/webui/sandbox/sandbox_internals_ui.cc | 6 +- + content/browser/child_process_host_impl.cc | 8 +- + .../zygote_host/zygote_host_impl_linux.cc | 39 +- + .../zygote_host/zygote_host_impl_linux.h | 1 + + content/zygote/zygote_linux.cc | 6 +- + content/zygote/zygote_linux.h | 2 + + content/zygote/zygote_main_linux.cc | 16 +- + sandbox/linux/BUILD.gn | 10 +- + sandbox/linux/services/flatpak_pid_map.cc | 57 ++ + sandbox/linux/services/flatpak_pid_map.h | 46 ++ + sandbox/linux/services/flatpak_sandbox.cc | 576 ++++++++++++++++++ + sandbox/linux/services/flatpak_sandbox.h | 118 ++++ + sandbox/policy/BUILD.gn | 3 + + sandbox/policy/linux/sandbox_linux.cc | 8 + + sandbox/policy/linux/sandbox_linux.h | 8 + + .../service_process_launcher.cc | 12 +- + 19 files changed, 907 insertions(+), 19 deletions(-) + create mode 100644 sandbox/linux/services/flatpak_pid_map.cc + create mode 100644 sandbox/linux/services/flatpak_pid_map.h + create mode 100644 sandbox/linux/services/flatpak_sandbox.cc + create mode 100644 sandbox/linux/services/flatpak_sandbox.h + +diff --git a/.gitignore b/.gitignore +index 9056030523807..2c13d0ad5f1c7 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -75,6 +75,7 @@ vs-chromium-project.txt + /.android_emulator/ + /.clangd/ + /.clangd-index/ ++/.flatpak-builder/ + # Settings directories for eclipse + /.externalToolBuilders/ + /.settings/ +diff --git a/base/threading/thread_restrictions.h b/base/threading/thread_restrictions.h +index 149838be725f2..81034acc71e97 100644 +--- a/base/threading/thread_restrictions.h ++++ b/base/threading/thread_restrictions.h +@@ -407,6 +407,9 @@ class ScopedAllowThreadJoinForWebRtcTransport; + namespace rlz_lib { + class FinancialPing; + } ++namespace sandbox { ++class FlatpakSandbox; ++} + namespace service_manager { + class ServiceProcessLauncher; + } +@@ -649,6 +652,7 @@ class BASE_EXPORT ScopedAllowBlocking { + friend class remoting:: + ScopedBypassIOThreadRestrictions; // http://crbug.com/1144161 + friend class remoting::ScopedAllowBlockingForCrashReporting; ++ friend class sandbox::FlatpakSandbox; + friend class ui::DrmDisplayHostManager; + friend class ui::ScopedAllowBlockingForGbmSurface; + friend class ui::SelectFileDialogLinux; +@@ -792,6 +796,7 @@ class BASE_EXPORT ScopedAllowBaseSyncPrimitives { + friend class rlz_lib::FinancialPing; + friend class shell_integration_linux:: + LaunchXdgUtilityScopedAllowBaseSyncPrimitives; ++ friend class sandbox::FlatpakSandbox; + friend class storage::ObfuscatedFileUtil; + friend class syncer::HttpBridge; + friend class syncer::GetLocalChangesRequest; +diff --git a/chrome/browser/resources/sandbox_internals/sandbox_internals.ts b/chrome/browser/resources/sandbox_internals/sandbox_internals.ts +index f6d2f3f0bc41c..fcc58c6735eaa 100644 +--- a/chrome/browser/resources/sandbox_internals/sandbox_internals.ts ++++ b/chrome/browser/resources/sandbox_internals/sandbox_internals.ts +@@ -140,6 +140,7 @@ function addGoodBadRow(name: string, result: boolean): HTMLElement { + function linuxHandler() { + const suidSandbox = loadTimeData.getBoolean('suid'); + const nsSandbox = loadTimeData.getBoolean('userNs'); ++ const flatpakSandbox = loadTimeData.getBoolean('flatpak'); + + let layer1SandboxType = 'None'; + let layer1SandboxCssClass = StatusClass.BAD; +@@ -149,6 +150,9 @@ function linuxHandler() { + } else if (nsSandbox) { + layer1SandboxType = 'Namespace'; + layer1SandboxCssClass = StatusClass.GOOD; ++ } else if (flatpakSandbox) { ++ layer1SandboxType = 'Flatpak'; ++ layer1SandboxCssClass = StatusClass.GOOD; + } + + addStatusRow('Layer 1 Sandbox', layer1SandboxType, layer1SandboxCssClass); +diff --git a/chrome/browser/ui/webui/sandbox/sandbox_internals_ui.cc b/chrome/browser/ui/webui/sandbox/sandbox_internals_ui.cc +index 82aa27787a5e0..32bf674f33da6 100644 +--- a/chrome/browser/ui/webui/sandbox/sandbox_internals_ui.cc ++++ b/chrome/browser/ui/webui/sandbox/sandbox_internals_ui.cc +@@ -20,6 +20,7 @@ + #include "content/public/browser/web_contents.h" + #include "content/public/browser/web_ui.h" + #include "content/public/browser/web_ui_data_source.h" ++#include "sandbox/policy/linux/sandbox_linux.h" + #include "services/network/public/mojom/content_security_policy.mojom.h" + + #if BUILDFLAG(IS_WIN) +@@ -46,6 +47,8 @@ static void SetSandboxStatusData(content::WebUIDataSource* source) { + + source->AddBoolean("suid", status & sandbox::policy::SandboxLinux::kSUID); + source->AddBoolean("userNs", status & sandbox::policy::SandboxLinux::kUserNS); ++ source->AddBoolean("flatpak", ++ status & sandbox::policy::SandboxLinux::kFlatpak); + source->AddBoolean("pidNs", status & sandbox::policy::SandboxLinux::kPIDNS); + source->AddBoolean("netNs", status & sandbox::policy::SandboxLinux::kNetNS); + source->AddBoolean("seccompBpf", +@@ -63,7 +66,8 @@ static void SetSandboxStatusData(content::WebUIDataSource* source) { + + // Require either the setuid or namespace sandbox for our first-layer sandbox. + bool good_layer1 = (status & sandbox::policy::SandboxLinux::kSUID || +- status & sandbox::policy::SandboxLinux::kUserNS) && ++ status & sandbox::policy::SandboxLinux::kUserNS || ++ status & sandbox::policy::SandboxLinux::kFlatpak) && + status & sandbox::policy::SandboxLinux::kPIDNS && + status & sandbox::policy::SandboxLinux::kNetNS; + // A second-layer sandbox is also required to be adequately sandboxed. +diff --git a/content/browser/child_process_host_impl.cc b/content/browser/child_process_host_impl.cc +index 95df576580a9e..8d5e0694660b6 100644 +--- a/content/browser/child_process_host_impl.cc ++++ b/content/browser/child_process_host_impl.cc +@@ -45,6 +45,7 @@ + + #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + #include "base/linux_util.h" ++#include "sandbox/linux/services/flatpak_sandbox.h" + #elif BUILDFLAG(IS_MAC) + #include "base/apple/foundation_util.h" + #include "content/browser/mac_helpers.h" +@@ -78,7 +79,12 @@ base::FilePath ChildProcessHost::GetChildPath(int flags) { + #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + // Use /proc/self/exe rather than our known binary path so updates + // can't swap out the binary from underneath us. +- if (child_path.empty() && flags & CHILD_ALLOW_SELF) { ++ // This is not needed for Flatpaks, where updates are going to be in ++ // a new hardlink tree. ++ if ((child_path.empty() && ++ sandbox::FlatpakSandbox::GetInstance()->GetSandboxLevel() == ++ sandbox::FlatpakSandbox::SandboxLevel::kNone) && ++ flags & CHILD_ALLOW_SELF) { + child_path = base::FilePath(base::kProcSelfExe); + } + #endif +diff --git a/content/browser/zygote_host/zygote_host_impl_linux.cc b/content/browser/zygote_host/zygote_host_impl_linux.cc +index 8eca918b65cbe..1703fb6ade044 100644 +--- a/content/browser/zygote_host/zygote_host_impl_linux.cc ++++ b/content/browser/zygote_host/zygote_host_impl_linux.cc +@@ -12,6 +12,7 @@ + #include "base/logging.h" + #include "base/posix/unix_domain_socket.h" + #include "base/process/kill.h" ++#include "base/process/launch.h" + #include "base/process/memory.h" + #include "base/strings/string_number_conversions.h" + #include "base/types/fixed_array.h" +@@ -22,6 +23,7 @@ + #include "content/common/zygote/zygote_handle_impl_linux.h" + #include "content/public/common/zygote/zygote_handle.h" + #include "sandbox/linux/services/credentials.h" ++#include "sandbox/linux/services/flatpak_sandbox.h" + #include "sandbox/linux/services/namespace_sandbox.h" + #include "sandbox/linux/suid/client/setuid_sandbox_host.h" + #include "sandbox/linux/suid/common/sandbox.h" +@@ -72,6 +74,7 @@ ZygoteHostImpl::ZygoteHostImpl() + : use_namespace_sandbox_(false), + use_suid_sandbox_(false), + use_suid_sandbox_for_adj_oom_score_(false), ++ use_flatpak_sandbox_(false), + sandbox_binary_(), + zygote_pids_lock_(), + zygote_pids_() {} +@@ -110,9 +113,12 @@ void ZygoteHostImpl::Init(const base::CommandLine& command_line) { + sandbox_binary_ = setuid_sandbox_host->GetSandboxBinaryPath().value(); + } + +- if (!command_line.HasSwitch( +- sandbox::policy::switches::kDisableNamespaceSandbox) && +- sandbox::Credentials::CanCreateProcessInNewUserNS()) { ++ if (sandbox::FlatpakSandbox::GetInstance()->GetSandboxLevel() != ++ sandbox::FlatpakSandbox::SandboxLevel::kNone) { ++ use_flatpak_sandbox_ = true; ++ } else if (!command_line.HasSwitch( ++ sandbox::policy::switches::kDisableNamespaceSandbox) && ++ sandbox::Credentials::CanCreateProcessInNewUserNS()) { + use_namespace_sandbox_ = true; + } else if (!command_line.HasSwitch( + sandbox::policy::switches::kDisableSetuidSandbox) && +@@ -183,10 +189,16 @@ pid_t ZygoteHostImpl::LaunchZygote( + sandbox_host->SetupLaunchEnvironment(); + } + +- base::Process process = +- (is_sandboxed_zygote && use_namespace_sandbox_) +- ? sandbox::NamespaceSandbox::LaunchProcess(*cmd_line, options) +- : base::LaunchProcess(*cmd_line, options); ++ base::Process process; ++ if (is_sandboxed_zygote && use_namespace_sandbox_) { ++ process = sandbox::NamespaceSandbox::LaunchProcess(*cmd_line, options); ++ } else if (is_sandboxed_zygote && use_flatpak_sandbox_) { ++ process = sandbox::FlatpakSandbox::GetInstance()->LaunchProcess(*cmd_line, ++ options); ++ } else { ++ process = base::LaunchProcess(*cmd_line, options); ++ } ++ + CHECK(process.IsValid()) << "Failed to launch zygote process"; + + dummy_fd.reset(); +@@ -195,7 +207,8 @@ pid_t ZygoteHostImpl::LaunchZygote( + + pid_t pid = process.Pid(); + +- if (is_sandboxed_zygote && (use_namespace_sandbox_ || use_suid_sandbox_)) { ++ if (is_sandboxed_zygote && ++ (use_namespace_sandbox_ || use_suid_sandbox_ || use_flatpak_sandbox_)) { + // The namespace and SUID sandbox will execute the zygote in a new + // PID namespace, and the main zygote process will then fork from + // there. Watch now our elaborate dance to find and validate the +@@ -223,7 +236,11 @@ pid_t ZygoteHostImpl::LaunchZygote( + + if (real_pid != pid) { + // Reap the sandbox. +- base::EnsureProcessGetsReaped(std::move(process)); ++ if (use_flatpak_sandbox_) { ++ sandbox::FlatpakSandbox::GetInstance()->IgnoreExitStatus(pid); ++ } else { ++ base::EnsureProcessGetsReaped(base::Process(pid)); ++ } + } + pid = real_pid; + } +@@ -274,6 +291,10 @@ void ZygoteHostImpl::AdjustRendererOOMScore(base::ProcessHandle pid, + selinux_valid = true; + } + ++ // Flatpaks cannot modify their OOM score. ++ if (use_flatpak_sandbox_) ++ return; ++ + if (!use_suid_sandbox_for_adj_oom_score_) { + if (!base::AdjustOOMScore(pid, score)) + PLOG(ERROR) << "Failed to adjust OOM score of renderer with pid " << pid; +diff --git a/content/browser/zygote_host/zygote_host_impl_linux.h b/content/browser/zygote_host/zygote_host_impl_linux.h +index 720d823d4a695..ad470963693c4 100644 +--- a/content/browser/zygote_host/zygote_host_impl_linux.h ++++ b/content/browser/zygote_host/zygote_host_impl_linux.h +@@ -70,6 +70,7 @@ class CONTENT_EXPORT ZygoteHostImpl : public ZygoteHost { + bool use_namespace_sandbox_; + bool use_suid_sandbox_; + bool use_suid_sandbox_for_adj_oom_score_; ++ bool use_flatpak_sandbox_; + std::string sandbox_binary_; + + // This lock protects the |zygote_pids_| set. +diff --git a/content/zygote/zygote_linux.cc b/content/zygote/zygote_linux.cc +index bdcb36f7eb2fe..239a6462e4b41 100644 +--- a/content/zygote/zygote_linux.cc ++++ b/content/zygote/zygote_linux.cc +@@ -127,7 +127,7 @@ bool Zygote::ProcessRequests() { + PCHECK(sigaddset(&sigset, SIGCHLD) == 0); + PCHECK(sigprocmask(SIG_BLOCK, &sigset, &orig_sigmask) == 0); + +- if (UsingSUIDSandbox() || UsingNSSandbox()) { ++ if (UsingSUIDSandbox() || UsingNSSandbox() || UsingFlatpakSandbox()) { + // Let the ZygoteHost know we are ready to go. + // The receiving code is in + // content/browser/zygote_host/zygote_host_impl_linux.cc. +@@ -233,6 +233,10 @@ bool Zygote::UsingNSSandbox() const { + return sandbox_flags_ & sandbox::policy::SandboxLinux::kUserNS; + } + ++bool Zygote::UsingFlatpakSandbox() const { ++ return sandbox_flags_ & sandbox::policy::SandboxLinux::kFlatpak; ++} ++ + bool Zygote::HandleRequestFromBrowser(int fd) { + std::vector fds; + uint8_t buf[kZygoteMaxMessageLength]; +diff --git a/content/zygote/zygote_linux.h b/content/zygote/zygote_linux.h +index 165b758efc899..961afa73f66f5 100644 +--- a/content/zygote/zygote_linux.h ++++ b/content/zygote/zygote_linux.h +@@ -64,6 +64,8 @@ class Zygote { + bool UsingSUIDSandbox() const; + // Returns true if the NS sandbox is active. + bool UsingNSSandbox() const; ++ // Returns true if the Flatpak sandbox is active. ++ bool UsingFlatpakSandbox() const; + + // --------------------------------------------------------------------------- + // Requests from the browser... +diff --git a/content/zygote/zygote_main_linux.cc b/content/zygote/zygote_main_linux.cc +index c7ee91878e6dd..9c2c7a04bd968 100644 +--- a/content/zygote/zygote_main_linux.cc ++++ b/content/zygote/zygote_main_linux.cc +@@ -35,6 +35,7 @@ + #include "content/public/common/zygote/zygote_fork_delegate_linux.h" + #include "content/zygote/zygote_linux.h" + #include "sandbox/linux/services/credentials.h" ++#include "sandbox/linux/services/flatpak_sandbox.h" + #include "sandbox/linux/services/init_process_reaper.h" + #include "sandbox/linux/services/libc_interceptor.h" + #include "sandbox/linux/services/namespace_sandbox.h" +@@ -135,6 +136,7 @@ static void EnterNamespaceSandbox(sandbox::policy::SandboxLinux* linux_sandbox, + + static void EnterLayerOneSandbox(sandbox::policy::SandboxLinux* linux_sandbox, + const bool using_layer1_sandbox, ++ const bool using_flatpak_sandbox, + base::OnceClosure post_fork_parent_callback) { + DCHECK(linux_sandbox); + +@@ -154,7 +156,8 @@ static void EnterLayerOneSandbox(sandbox::policy::SandboxLinux* linux_sandbox, + } else if (sandbox::NamespaceSandbox::InNewUserNamespace()) { + EnterNamespaceSandbox(linux_sandbox, std::move(post_fork_parent_callback)); + } else { +- CHECK(!using_layer1_sandbox); ++ // The Flatpak sandbox means that we're fully sandboxed from the start. ++ CHECK(!using_layer1_sandbox || using_flatpak_sandbox); + } + } + +@@ -178,8 +181,11 @@ bool ZygoteMain( + linux_sandbox->setuid_sandbox_client()->IsSuidSandboxChild(); + const bool using_namespace_sandbox = + sandbox::NamespaceSandbox::InNewUserNamespace(); ++ const bool using_flatpak_sandbox = ++ sandbox::FlatpakSandbox::GetInstance()->GetSandboxLevel() == ++ sandbox::FlatpakSandbox::SandboxLevel::kRestricted; + const bool using_layer1_sandbox = +- using_setuid_sandbox || using_namespace_sandbox; ++ using_setuid_sandbox || using_namespace_sandbox || using_flatpak_sandbox; + + if (using_setuid_sandbox) { + linux_sandbox->setuid_sandbox_client()->CloseDummyFile(); +@@ -206,7 +212,7 @@ bool ZygoteMain( + + // Turn on the first layer of the sandbox if the configuration warrants it. + EnterLayerOneSandbox( +- linux_sandbox, using_layer1_sandbox, ++ linux_sandbox, using_layer1_sandbox, using_flatpak_sandbox, + base::BindOnce(CloseFds, linux_sandbox->GetFileDescriptorsToClose())); + + const int sandbox_flags = linux_sandbox->GetStatus(); +@@ -218,6 +224,10 @@ bool ZygoteMain( + !!(sandbox_flags & sandbox::policy::SandboxLinux::kUserNS); + CHECK_EQ(using_namespace_sandbox, namespace_sandbox_engaged); + ++ const bool flatpak_sandbox_engaged = ++ !!(sandbox_flags & sandbox::policy::SandboxLinux::kFlatpak); ++ CHECK_EQ(using_flatpak_sandbox, flatpak_sandbox_engaged); ++ + Zygote zygote(sandbox_flags, std::move(fork_delegates), + base::GlobalDescriptors::Descriptor( + static_cast(kSandboxIPCChannel), GetSandboxFD())); +diff --git a/sandbox/linux/BUILD.gn b/sandbox/linux/BUILD.gn +index 97e3deed4f2b9..61916ed9805d3 100644 +--- a/sandbox/linux/BUILD.gn ++++ b/sandbox/linux/BUILD.gn +@@ -311,6 +311,10 @@ if (is_linux || is_chromeos) { + + component("sandbox_services") { + sources = [ ++ "services/flatpak_pid_map.cc", ++ "services/flatpak_pid_map.h", ++ "services/flatpak_sandbox.cc", ++ "services/flatpak_sandbox.h", + "services/init_process_reaper.cc", + "services/init_process_reaper.h", + "services/proc_util.cc", +@@ -329,8 +333,10 @@ component("sandbox_services") { + + defines = [ "SANDBOX_IMPLEMENTATION" ] + +- public_deps = [ "//sandbox:sandbox_export" ] +- deps = [ "//base" ] ++ public_deps = [ ++ "//dbus", ++ "//sandbox:sandbox_export", ++ ] + + if (compile_credentials) { + sources += [ +diff --git a/sandbox/linux/services/flatpak_pid_map.cc b/sandbox/linux/services/flatpak_pid_map.cc +new file mode 100644 +index 0000000000000..58b2ab552385b +--- /dev/null ++++ b/sandbox/linux/services/flatpak_pid_map.cc +@@ -0,0 +1,57 @@ ++// Copyright 2020 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 "sandbox/linux/services/flatpak_pid_map.h" ++ ++namespace sandbox { ++ ++bool FlatpakPidMap::Insert(PidPair pair) { ++ if (external_to_relative_.contains(pair.external) || ++ relative_to_external_.contains(pair.relative)) { ++ return false; ++ } ++ ++ external_to_relative_[pair.external] = pair.relative; ++ relative_to_external_[pair.relative] = pair.external; ++ return true; ++} ++ ++absl::optional FlatpakPidMap::FindRelativeByExternal(pid_t external) { ++ return FindImpl(&external_to_relative_, external); ++} ++ ++absl::optional FlatpakPidMap::FindExternalByRelative(pid_t relative) { ++ return FindImpl(&relative_to_external_, relative); ++} ++ ++absl::optional FlatpakPidMap::DeleteByExternal(pid_t external) { ++ return DeleteImpl(&external_to_relative_, &relative_to_external_, external); ++} ++ ++absl::optional FlatpakPidMap::DeleteByRelative(pid_t relative) { ++ return DeleteImpl(&relative_to_external_, &external_to_relative_, relative); ++} ++ ++absl::optional FlatpakPidMap::FindImpl(base::flat_map* map, ++ pid_t key) { ++ auto it = map->find(key); ++ return it != map->end() ? it->second : absl::optional(); ++} ++ ++absl::optional FlatpakPidMap::DeleteImpl( ++ base::flat_map* map, ++ base::flat_map* reversed, ++ pid_t key) { ++ auto it = map->find(key); ++ if (it == map->end()) { ++ return absl::optional(); ++ } ++ ++ pid_t value = it->second; ++ reversed->erase(value); ++ map->erase(it); ++ return value; ++} ++ ++} // namespace sandbox +diff --git a/sandbox/linux/services/flatpak_pid_map.h b/sandbox/linux/services/flatpak_pid_map.h +new file mode 100644 +index 0000000000000..22799eb42f782 +--- /dev/null ++++ b/sandbox/linux/services/flatpak_pid_map.h +@@ -0,0 +1,46 @@ ++// Copyright 2020 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. ++ ++#ifndef SANDBOX_LINUX_SERVICES_FLATPAK_PID_MAP_H_ ++#define SANDBOX_LINUX_SERVICES_FLATPAK_PID_MAP_H_ ++ ++#include "base/containers/flat_map.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++ ++namespace sandbox { ++ ++// A bidirectional map of external PIDs and relative PIDs for the Flatpak ++// sandbox. "External" PIDs are the PID values that Flatpak's Spawn API returns, ++// relative to the host system, and "relative" PIDs are the PIDs those processes ++// are known by from inside the sandbox. ++class FlatpakPidMap { ++ public: ++ struct PidPair { ++ pid_t external; ++ pid_t relative; ++ }; ++ ++ FlatpakPidMap() = default; ++ ++ bool Insert(PidPair pair); ++ ++ absl::optional FindRelativeByExternal(pid_t external); ++ absl::optional FindExternalByRelative(pid_t relative); ++ ++ absl::optional DeleteByRelative(pid_t relative); ++ absl::optional DeleteByExternal(pid_t external); ++ ++ private: ++ absl::optional FindImpl(base::flat_map* map, pid_t key); ++ absl::optional DeleteImpl(base::flat_map* map, ++ base::flat_map* reversed, ++ pid_t key); ++ ++ base::flat_map external_to_relative_; ++ base::flat_map relative_to_external_; ++}; // namespace sandbox ++ ++} // namespace sandbox ++ ++#endif +diff --git a/sandbox/linux/services/flatpak_sandbox.cc b/sandbox/linux/services/flatpak_sandbox.cc +new file mode 100644 +index 0000000000000..2a915a5b9fa11 +--- /dev/null ++++ b/sandbox/linux/services/flatpak_sandbox.cc +@@ -0,0 +1,576 @@ ++// Copyright 2019 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 "sandbox/linux/services/flatpak_sandbox.h" ++ ++#include ++#include ++#include ++ ++#include "base/files/file_path.h" ++#include "base/files/file_util.h" ++#include "base/functional/bind.h" ++#include "base/logging.h" ++#include "base/process/process_handle.h" ++#include "base/strings/string_number_conversions.h" ++#include "base/strings/string_util.h" ++#include "base/synchronization/lock.h" ++#include "base/threading/scoped_blocking_call.h" ++#include "base/threading/thread_restrictions.h" ++#include "dbus/bus.h" ++#include "dbus/message.h" ++#include "dbus/object_path.h" ++#include "dbus/object_proxy.h" ++#include "dbus/property.h" ++#include "sandbox/linux/services/flatpak_pid_map.h" ++ ++namespace sandbox { ++ ++namespace { ++const base::FilePath kFlatpakAppPath("/app"); ++const base::FilePath kFlatpakInfoPath("/.flatpak-info"); ++ ++const char kFlatpakPortalServiceName[] = "org.freedesktop.portal.Flatpak"; ++const char kFlatpakPortalObjectPath[] = "/org/freedesktop/portal/Flatpak"; ++const char kFlatpakPortalInterfaceName[] = "org.freedesktop.portal.Flatpak"; ++ ++#ifndef NDEBUG ++const char kDisableFullFlatpakSandbox[] = "disable-full-flatpak-sandbox"; ++#endif ++ ++struct PortalProperties : dbus::PropertySet { ++ dbus::Property version; ++ dbus::Property supports; ++ ++ enum FlatpakPortalSupports { ++ kFlatpakPortal_ExposePids = 1 << 0, ++ }; ++ ++ explicit PortalProperties(dbus::ObjectProxy* object_proxy) ++ : dbus::PropertySet(object_proxy, kFlatpakPortalInterfaceName, {}) { ++ RegisterProperty("version", &version); ++ RegisterProperty("supports", &supports); ++ } ++ ++ ~PortalProperties() override = default; ++}; ++ ++void WriteStringAsByteArray(dbus::MessageWriter* writer, ++ const std::string& str) { ++ writer->AppendArrayOfBytes(base::make_span( ++ reinterpret_cast(str.c_str()), str.size() + 1)); ++} ++ ++void WriteFdPairMap(dbus::MessageWriter* writer, int source_fd, int dest_fd) { ++ dbus::MessageWriter entry_writer(nullptr); ++ writer->OpenDictEntry(&entry_writer); ++ ++ entry_writer.AppendUint32(dest_fd); ++ entry_writer.AppendFileDescriptor(source_fd); ++ ++ writer->CloseContainer(&entry_writer); ++} ++ ++} // namespace ++ ++enum FlatpakSpawnFlags { ++ kFlatpakSpawn_ClearEnvironment = 1 << 0, ++ kFlatpakSpawn_Latest = 1 << 1, ++ kFlatpakSpawn_Sandbox = 1 << 2, ++ kFlatpakSpawn_NoNetwork = 1 << 3, ++ kFlatpakSpawn_WatchBus = 1 << 4, ++ kFlatpakSpawn_ExposePids = 1 << 5, ++ kFlatpakSpawn_NotifyStart = 1 << 6, ++}; ++ ++enum FlatpakSpawnSandboxFlags { ++ kFlatpakSpawnSandbox_ShareDisplay = 1 << 0, ++ kFlatpakSpawnSandbox_ShareSound = 1 << 1, ++ kFlatpakSpawnSandbox_ShareGpu = 1 << 2, ++ kFlatpakSpawnSandbox_ShareSessionBus = 1 << 3, ++ kFlatpakSpawnSandbox_ShareA11yBus = 1 << 4, ++}; ++ ++FlatpakSandbox::FlatpakSandbox() ++ : bus_thread_("FlatpakPortalBus"), process_info_cv_(&process_info_lock_) {} ++ ++// static ++FlatpakSandbox* FlatpakSandbox::GetInstance() { ++ static base::NoDestructor instance; ++ return instance.get(); ++} ++ ++FlatpakSandbox::SandboxLevel FlatpakSandbox::GetSandboxLevel() { ++ if (sandbox_level_) { ++ return *sandbox_level_; ++ } ++ ++ // XXX: These operations shouldn't actually have a major blocking time, ++ // as .flatpak-info is on a tmpfs. ++ base::ScopedAllowBlocking scoped_allow_blocking; ++ ++ if (!base::PathExists(kFlatpakInfoPath)) { ++ sandbox_level_ = SandboxLevel::kNone; ++ } else { ++ // chrome has an INI parser, but sandbox can't depend on anything inside ++ // chrome, so the .flatpak-info INI is manually checked for the sandbox ++ // option. ++ ++ std::string contents; ++ CHECK(ReadFileToString(kFlatpakInfoPath, &contents)); ++ DCHECK(!contents.empty()); ++ ++ std::istringstream iss(contents); ++ std::string line; ++ bool in_instance = false; ++ while (std::getline(iss, line)) { ++ if (!line.empty() && line[0] == '[') { ++ DCHECK(line.back() == ']'); ++ ++ if (line == "[Instance]") { ++ DCHECK(!in_instance); ++ in_instance = true; ++ } else if (in_instance) { ++ // Leaving the Instance section, sandbox=true can't come now. ++ break; ++ } ++ } else if (in_instance && line == "sandbox=true") { ++ sandbox_level_ = SandboxLevel::kRestricted; ++ break; ++ } ++ } ++ ++ if (!sandbox_level_) { ++ sandbox_level_ = SandboxLevel::kFlatpak; ++ } ++ } ++ ++#ifndef NDEBUG ++ if (sandbox_level_ == SandboxLevel::kFlatpak && ++ base::CommandLine::ForCurrentProcess()->HasSwitch( ++ kDisableFullFlatpakSandbox)) { ++ sandbox_level_ = SandboxLevel::kRestricted; ++ } ++#endif ++ ++ return *sandbox_level_; ++} ++ ++bool FlatpakSandbox::IsPidSandboxed(base::ProcessId relative_pid) { ++ base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, ++ base::BlockingType::MAY_BLOCK); ++ ++ base::AutoLock locker(process_info_lock_); ++ ++ return running_processes_.FindExternalByRelative(relative_pid).has_value(); ++} ++ ++base::Process FlatpakSandbox::LaunchProcess( ++ const base::CommandLine& cmdline, ++ const base::LaunchOptions& launch_options) { ++ base::ProcessId external_pid = Spawn(cmdline, launch_options); ++ if (external_pid == base::kNullProcessId) { ++ return base::Process(); ++ } ++ ++ base::ProcessId relative_pid = GetRelativePid(external_pid); ++ if (relative_pid == base::kNullProcessId) { ++ // Treat early stops as a launch failure. ++ return base::Process(); ++ } ++ ++ return base::Process(relative_pid); ++} ++ ++bool FlatpakSandbox::Wait(base::ProcessId relative_pid, int* exit_code) { ++ base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, ++ base::BlockingType::MAY_BLOCK); ++ ++ base::AutoLock locker(process_info_lock_); ++ ++ for (;;) { ++ if (running_processes_.FindExternalByRelative(relative_pid)) { ++ // Process is still running. ++ process_info_cv_.Wait(); ++ continue; ++ } ++ ++ auto it = exited_process_statuses_.find(relative_pid); ++ if (it == exited_process_statuses_.end()) { ++ // This should only happen if another caller had marked the exit status ++ // to be ignored. Treat it like waitpid returning ESRCH. ++ LOG(ERROR) << "PID " << relative_pid << " had no exit status"; ++ return false; ++ } ++ ++ if (exit_code) { ++ *exit_code = it->second; ++ } ++ exited_process_statuses_.erase(it); ++ return true; ++ } ++} ++ ++void FlatpakSandbox::IgnoreExitStatus(base::ProcessId relative_pid) { ++ base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, ++ base::BlockingType::MAY_BLOCK); ++ ++ base::AutoLock locker(process_info_lock_); ++ ++ CHECK(running_processes_.FindExternalByRelative(relative_pid)); ++ ignore_status_.insert(relative_pid); ++} ++ ++void FlatpakSandbox::StartBusThread() { ++ if (!bus_thread_.IsRunning()) { ++ base::Thread::Options options; ++ options.message_pump_type = base::MessagePumpType::IO; ++ CHECK(bus_thread_.StartWithOptions(std::move(options))); ++ ++ bus_thread_.task_runner()->PostTask( ++ FROM_HERE, base::BindOnce(&FlatpakSandbox::InitializeBusThread, ++ base::Unretained(this))); ++ } ++} ++ ++dbus::Bus* FlatpakSandbox::AcquireBusFromBusThread() { ++ // Note that destruction of the bus is not a concern, because once the ++ // thread dies its bus connection will be terminated anyway and the ++ // portal will notice. ++ static base::NoDestructor> bus([] { ++ dbus::Bus::Options options; ++ options.bus_type = dbus::Bus::SESSION; ++ options.connection_type = dbus::Bus::PRIVATE; ++ options.dbus_task_runner = base::SequencedTaskRunner::GetCurrentDefault(); ++ ++ return base::MakeRefCounted(options); ++ }()); ++ ++ return bus->get(); ++} ++ ++dbus::ObjectProxy* FlatpakSandbox::GetPortalObjectProxy() { ++ return AcquireBusFromBusThread()->GetObjectProxy( ++ kFlatpakPortalServiceName, dbus::ObjectPath(kFlatpakPortalObjectPath)); ++} ++ ++void FlatpakSandbox::InitializeBusThread() { ++ dbus::ObjectProxy* object_proxy = GetPortalObjectProxy(); ++ ++ PortalProperties properties(object_proxy); ++ properties.ConnectSignals(); ++ ++ CHECK(properties.GetAndBlock(&properties.version)) ++ << "Failed to get portal version"; ++ CHECK(properties.GetAndBlock(&properties.supports)) ++ << "Failed to get portal supports"; ++ ++ if (properties.version.value() < 4) { ++ LOG(FATAL) << "Your Flatpak version is too old, please update it"; ++ } ++ ++ if (!(properties.supports.value() & ++ PortalProperties::kFlatpakPortal_ExposePids)) { ++ LOG(FATAL) << "Your Flatpak installation is setuid, which is not supported"; ++ } ++ ++ object_proxy->ConnectToSignal( ++ kFlatpakPortalInterfaceName, "SpawnStarted", ++ base::BindRepeating(&FlatpakSandbox::OnSpawnStartedSignal, ++ base::Unretained(this)), ++ base::BindOnce(&FlatpakSandbox::OnSignalConnected, ++ base::Unretained(this))); ++ ++ object_proxy->ConnectToSignal( ++ kFlatpakPortalInterfaceName, "SpawnExited", ++ base::BindRepeating(&FlatpakSandbox::OnSpawnExitedSignal, ++ base::Unretained(this)), ++ base::BindOnce(&FlatpakSandbox::OnSignalConnected, ++ base::Unretained(this))); ++} ++ ++void FlatpakSandbox::OnSignalConnected(const std::string& interface, ++ const std::string& signal, ++ bool connected) { ++ // It's not safe to spawn processes without being able to track their deaths. ++ CHECK(connected) << "Failed to connect to signal " << signal; ++} ++ ++void FlatpakSandbox::OnSpawnStartedSignal(dbus::Signal* signal) { ++ dbus::MessageReader reader(signal); ++ uint32_t external_pid, relative_pid; ++ ++ if (!reader.PopUint32(&external_pid) || !reader.PopUint32(&relative_pid)) { ++ LOG(ERROR) << "Invalid SpawnStarted signal"; ++ return; ++ } ++ ++ VLOG(1) << "Received SpawnStarted: " << external_pid << ' ' << relative_pid; ++ ++ base::AutoLock locker(process_info_lock_); ++ ++ auto it = unmapped_processes_.find(external_pid); ++ if (it == unmapped_processes_.end()) { ++ LOG(ERROR) << "Process " << external_pid ++ << " is already dead or not tracked"; ++ return; ++ } ++ ++ unmapped_processes_.erase(it); ++ ++ // Don't try to map them if the process died too quickly (which is the cause ++ // of relative_pid == 0). ++ if (relative_pid != 0) { ++ FlatpakPidMap::PidPair pair; ++ pair.external = external_pid; ++ pair.relative = relative_pid; ++ running_processes_.Insert(pair); ++ } ++ ++ process_info_cv_.Broadcast(); ++} ++ ++void FlatpakSandbox::OnSpawnExitedSignal(dbus::Signal* signal) { ++ dbus::MessageReader reader(signal); ++ uint32_t external_pid, exit_status; ++ ++ if (!reader.PopUint32(&external_pid) || !reader.PopUint32(&exit_status)) { ++ LOG(ERROR) << "Invalid SpawnExited signal"; ++ return; ++ } ++ ++ VLOG(1) << "Received SpawnExited: " << external_pid << ' ' << exit_status; ++ ++ base::AutoLock locker(process_info_lock_); ++ ++ auto relative_pid = running_processes_.DeleteByExternal(external_pid); ++ // If this isn't found, it likely never ran long enough for SpawnStarted to be ++ // emitted, so we never bother saving the exit status. ++ if (relative_pid) { ++ auto ignore_it = ignore_status_.find(*relative_pid); ++ if (ignore_it != ignore_status_.end()) { ++ // Make sure the exit status is not set. ++ relative_pid.reset(); ++ ignore_status_.erase(ignore_it); ++ } ++ } ++ ++ if (relative_pid) { ++ exited_process_statuses_[*relative_pid] = exit_status; ++ } ++ ++ process_info_cv_.Broadcast(); ++} ++ ++base::ProcessId FlatpakSandbox::Spawn( ++ const base::CommandLine& cmdline, ++ const base::LaunchOptions& launch_options) { ++ base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, ++ base::BlockingType::MAY_BLOCK); ++ base::ScopedAllowBaseSyncPrimitives allow_wait; ++ ++ StartBusThread(); ++ ++ VLOG(1) << "Running via Flatpak: " << cmdline.GetCommandLineString(); ++ ++ DCHECK(GetSandboxLevel() != SandboxLevel::kNone); ++ ++ // These options are not supported with the Flatpak sandbox. ++ DCHECK(launch_options.clone_flags == 0); ++ DCHECK(!launch_options.wait); ++ DCHECK(!launch_options.allow_new_privs); ++ DCHECK(launch_options.real_path.empty()); ++ DCHECK(launch_options.pre_exec_delegate == nullptr); ++ DCHECK(launch_options.maximize_rlimits == nullptr); ++ ++ base::ProcessId external_pid = base::kNullProcessId; ++ base::WaitableEvent event; ++ ++ bus_thread_.task_runner()->PostTask( ++ FROM_HERE, ++ base::BindOnce(&FlatpakSandbox::SpawnOnBusThread, base::Unretained(this), ++ base::Unretained(&external_pid), base::Unretained(&event), ++ cmdline, launch_options)); ++ event.Wait(); ++ ++ return external_pid; ++} ++ ++void FlatpakSandbox::SpawnOnBusThread( ++ base::ProcessId* out_external_pid, ++ base::WaitableEvent* event, ++ const base::CommandLine& cmdline, ++ const base::LaunchOptions& launch_options) { ++ dbus::ObjectProxy* object_proxy = GetPortalObjectProxy(); ++ dbus::MethodCall method_call(kFlatpakPortalInterfaceName, "Spawn"); ++ dbus::MessageWriter writer(&method_call); ++ ++ const base::FilePath& current_directory = ++ !launch_options.current_directory.empty() ++ ? launch_options.current_directory ++ // Change to /app since it's guaranteed to always be present in ++ // the sandbox. ++ : kFlatpakAppPath; ++ WriteStringAsByteArray(&writer, current_directory.value()); ++ ++ dbus::MessageWriter argv_writer(nullptr); ++ writer.OpenArray("ay", &argv_writer); ++ ++ for (const std::string& arg : cmdline.argv()) { ++ WriteStringAsByteArray(&argv_writer, arg); ++ } ++ ++#ifndef NDEBUG ++ if (base::CommandLine::ForCurrentProcess()->HasSwitch( ++ kDisableFullFlatpakSandbox)) { ++ std::string arg = "--"; ++ arg += kDisableFullFlatpakSandbox; ++ WriteStringAsByteArray(&argv_writer, arg); ++ } ++#endif ++ ++ writer.CloseContainer(&argv_writer); ++ ++ dbus::MessageWriter fds_writer(nullptr); ++ writer.OpenArray("{uh}", &fds_writer); ++ ++ WriteFdPairMap(&fds_writer, STDIN_FILENO, STDIN_FILENO); ++ WriteFdPairMap(&fds_writer, STDOUT_FILENO, STDOUT_FILENO); ++ WriteFdPairMap(&fds_writer, STDERR_FILENO, STDERR_FILENO); ++ ++ for (const auto& pair : launch_options.fds_to_remap) { ++ WriteFdPairMap(&fds_writer, pair.first, pair.second); ++ } ++ ++ writer.CloseContainer(&fds_writer); ++ ++ dbus::MessageWriter env_writer(nullptr); ++ writer.OpenArray("{ss}", &env_writer); ++ ++ for (const auto& pair : launch_options.environment) { ++ dbus::MessageWriter entry_writer(nullptr); ++ env_writer.OpenDictEntry(&entry_writer); ++ ++ entry_writer.AppendString(pair.first); ++ entry_writer.AppendString(pair.second); ++ ++ env_writer.CloseContainer(&entry_writer); ++ } ++ ++ writer.CloseContainer(&env_writer); ++ ++ int spawn_flags = kFlatpakSpawn_Sandbox | kFlatpakSpawn_ExposePids | ++ kFlatpakSpawn_NotifyStart; ++ int sandbox_flags = 0; ++ ++#ifndef NDEBUG ++ if (base::CommandLine::ForCurrentProcess()->HasSwitch( ++ kDisableFullFlatpakSandbox)) { ++ spawn_flags &= ~kFlatpakSpawn_Sandbox; ++ } ++#else ++#endif ++ ++ if (launch_options.clear_environment) { ++ spawn_flags |= kFlatpakSpawn_ClearEnvironment; ++ } ++ ++ if (launch_options.kill_on_parent_death) { ++ spawn_flags |= kFlatpakSpawn_WatchBus; ++ } ++ ++ writer.AppendUint32(spawn_flags); ++ ++ dbus::MessageWriter options_writer(nullptr); ++ writer.OpenArray("{sv}", &options_writer); ++ ++ if (sandbox_flags != 0) { ++ dbus::MessageWriter entry_writer(nullptr); ++ options_writer.OpenDictEntry(&entry_writer); ++ ++ entry_writer.AppendString("sandbox-flags"); ++ ++ dbus::MessageWriter variant_writer(nullptr); ++ entry_writer.OpenVariant("u", &variant_writer); ++ ++ variant_writer.AppendUint32(sandbox_flags); ++ ++ entry_writer.CloseContainer(&variant_writer); ++ options_writer.CloseContainer(&entry_writer); ++ } ++ ++ writer.CloseContainer(&options_writer); ++ ++ object_proxy->CallMethodWithErrorResponse( ++ &method_call, dbus::ObjectProxy::TIMEOUT_INFINITE, ++ base::BindOnce(&FlatpakSandbox::OnSpawnResponse, base::Unretained(this), ++ base::Unretained(out_external_pid), ++ base::Unretained(event))); ++} ++ ++void FlatpakSandbox::OnSpawnResponse(base::ProcessId* out_external_pid, ++ base::WaitableEvent* event, ++ dbus::Response* response, ++ dbus::ErrorResponse* error_response) { ++ if (response) { ++ dbus::MessageReader reader(response); ++ uint32_t external_pid; ++ if (!reader.PopUint32(&external_pid)) { ++ LOG(ERROR) << "Invalid Spawn() response"; ++ } else { ++ VLOG(1) << "Spawn() returned PID " << external_pid; ++ if (out_external_pid != nullptr) { ++ *out_external_pid = external_pid; ++ } ++ ++ base::AutoLock locker(process_info_lock_); ++ unmapped_processes_.insert(external_pid); ++ } ++ } else if (error_response) { ++ std::string error_name = error_response->GetErrorName(); ++ std::string error_message; ++ dbus::MessageReader reader(error_response); ++ reader.PopString(&error_message); ++ ++ LOG(ERROR) << "Error calling Spawn(): " << error_name << ": " ++ << error_message; ++ } else { ++ LOG(ERROR) << "Unknown error occurred calling Spawn()"; ++ } ++ ++ if (event != nullptr) { ++ event->Signal(); ++ } ++} ++ ++base::ProcessId FlatpakSandbox::GetRelativePid(base::ProcessId external_pid) { ++ base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, ++ base::BlockingType::MAY_BLOCK); ++ base::ScopedAllowBaseSyncPrimitives allow_wait; ++ ++ base::AutoLock locker(process_info_lock_); ++ ++ for (;;) { ++ auto unmapped_it = unmapped_processes_.find(external_pid); ++ if (unmapped_it != unmapped_processes_.end()) { ++ // No relative PID is known yet. ++ VLOG(1) << "Waiting for " << external_pid; ++ process_info_cv_.Wait(); ++ continue; ++ } ++ ++ auto relative_pid = running_processes_.FindRelativeByExternal(external_pid); ++ if (!relative_pid) { ++ exited_process_statuses_.erase(external_pid); ++ ++ LOG(INFO) << "Already died: " << external_pid; ++ return base::kNullProcessId; ++ } ++ ++ VLOG(1) << "Got " << external_pid << " => " << *relative_pid; ++ return *relative_pid; ++ } ++} ++ ++} // namespace sandbox +diff --git a/sandbox/linux/services/flatpak_sandbox.h b/sandbox/linux/services/flatpak_sandbox.h +new file mode 100644 +index 0000000000000..167bbc85945ad +--- /dev/null ++++ b/sandbox/linux/services/flatpak_sandbox.h +@@ -0,0 +1,118 @@ ++// Copyright 2019 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. ++ ++#ifndef SANDBOX_LINUX_SERVICES_FLATPAK_SANDBOX_H_ ++#define SANDBOX_LINUX_SERVICES_FLATPAK_SANDBOX_H_ ++ ++#include "base/command_line.h" ++#include "base/compiler_specific.h" ++#include "base/containers/flat_map.h" ++#include "base/containers/flat_set.h" ++#include "base/no_destructor.h" ++#include "base/process/kill.h" ++#include "base/process/launch.h" ++#include "base/process/process_handle.h" ++#include "base/synchronization/condition_variable.h" ++#include "base/synchronization/lock.h" ++#include "base/synchronization/waitable_event.h" ++#include "base/threading/thread.h" ++#include "dbus/bus.h" ++#include "dbus/message.h" ++#include "sandbox/linux/services/flatpak_pid_map.h" ++#include "sandbox/sandbox_export.h" ++#include "third_party/abseil-cpp/absl/types/optional.h" ++ ++namespace sandbox { ++ ++// Manages the state of and access to the Flatpak sandbox. ++// Note that there is a distinction between external and internal PIDs: ++// - External PIDs are the PIDs relative to the world outside the sandbox. ++// - Internal PIDs are the PIDs relative to the current PID namespace. ++// Flatpak's sandbox APIs work primarily with external PIDs, and an ++// internal PID must be retrieved from the SpawnStarted signal before ++// it is known inside the sandbox's PID namespace. ++class SANDBOX_EXPORT FlatpakSandbox { ++ public: ++ static FlatpakSandbox* GetInstance(); ++ ++ // Represents the level of sandboxing inside a Flatpak. kNone means this is ++ // not a Flatpak, kFlatpak means it's inside a Flatpak sandbox, and ++ // kRestricted means that this is inside a nested Flatpak sandbox with most ++ // permissions revoked. ++ enum class SandboxLevel { kNone, kFlatpak, kRestricted }; ++ ++ // Get the current level of sandboxing in this Flatpak. ++ SandboxLevel GetSandboxLevel(); ++ ++ // Returns whether or not the given PID was spawned via the Flatpak sandbox. ++ bool IsPidSandboxed(base::ProcessId relative_pid); ++ ++ // Launch the given process inside of a Flatpak sandbox. If allow_x11 is true, ++ // then the process will be given access to the host's X11 display. On ++ // failure, returns kNullProcessId. Note that the return value is the PID ++ // relative to the host i.e. outside the sandbox, to get the internal one call ++ // GetRelativePid. This is the reason why a vanilla ProcessId is returned ++ // rather than a base::Process instance. ++ base::Process LaunchProcess(const base::CommandLine& cmdline, ++ const base::LaunchOptions& launch_options); ++ ++ // Indefinitely waits for the given process and fills the exit code pointer ++ // if given and non-null. Returns false on wait failure. ++ bool Wait(base::ProcessId relative_pid, int* exit_code); ++ ++ // Skips storing the exit status of the given PID. ++ void IgnoreExitStatus(base::ProcessId relative_pid); ++ ++ private: ++ friend class base::NoDestructor; ++ ++ FlatpakSandbox(); ++ FlatpakSandbox(const FlatpakSandbox&) = delete; ++ FlatpakSandbox(FlatpakSandbox&&) = delete; ++ ~FlatpakSandbox(); ++ ++ void StartBusThread(); ++ dbus::Bus* AcquireBusFromBusThread(); ++ dbus::ObjectProxy* GetPortalObjectProxy(); ++ ++ void InitializeBusThread(); ++ void OnSignalConnected(const std::string& interface, ++ const std::string& signal, ++ bool connected); ++ void OnSpawnStartedSignal(dbus::Signal* signal); ++ void OnSpawnExitedSignal(dbus::Signal* signal); ++ ++ base::ProcessId Spawn(const base::CommandLine& cmdline, ++ const base::LaunchOptions& launch_options); ++ void SpawnOnBusThread(base::ProcessId* out_external_pid, ++ base::WaitableEvent* event, ++ const base::CommandLine& cmdline, ++ const base::LaunchOptions& launch_options); ++ void OnSpawnResponse(base::ProcessId* out_external_pid, ++ base::WaitableEvent* event, ++ dbus::Response* response, ++ dbus::ErrorResponse* error_response); ++ ++ base::ProcessId GetRelativePid(base::ProcessId external_pid); ++ ++ absl::optional sandbox_level_; ++ base::Thread bus_thread_; ++ ++ base::Lock process_info_lock_; ++ // Note that broadcast is used in the source, because in general ++ // very few threads will be contending for the lock. ++ base::ConditionVariable process_info_cv_; ++ // Set of processes that have no associated relative PID yet. ++ base::flat_set unmapped_processes_; ++ // Map of running processes. ++ FlatpakPidMap running_processes_; ++ // Map of a relative process ID that has exited to its waitpid status. ++ std::map exited_process_statuses_; ++ // Relative process IDs that should have their statuses ignored on exit. ++ std::set ignore_status_; ++}; ++ ++} // namespace sandbox ++ ++#endif // SANDBOX_LINUX_SERVICES_FLATPAK_SANDBOX_H_ +diff --git a/sandbox/policy/BUILD.gn b/sandbox/policy/BUILD.gn +index 4fe53b047b9aa..794e1ad1805d8 100644 +--- a/sandbox/policy/BUILD.gn ++++ b/sandbox/policy/BUILD.gn +@@ -129,6 +129,9 @@ component("policy") { + "//sandbox/linux:suid_sandbox_client", + ] + } ++ if (is_linux) { ++ public_deps += [ "//sandbox/linux:sandbox_services" ] ++ } + if (is_chromeos_ash) { + sources += [ + "linux/bpf_ime_policy_linux.cc", +diff --git a/sandbox/policy/linux/sandbox_linux.cc b/sandbox/policy/linux/sandbox_linux.cc +index c7313416e6183..0cb8043f0ac72 100644 +--- a/sandbox/policy/linux/sandbox_linux.cc ++++ b/sandbox/policy/linux/sandbox_linux.cc +@@ -37,6 +37,7 @@ + #include "sandbox/constants.h" + #include "sandbox/linux/seccomp-bpf-helpers/sigsys_handlers.h" + #include "sandbox/linux/services/credentials.h" ++#include "sandbox/linux/services/flatpak_sandbox.h" + #include "sandbox/linux/services/libc_interceptor.h" + #include "sandbox/linux/services/namespace_sandbox.h" + #include "sandbox/linux/services/proc_util.h" +@@ -236,6 +237,9 @@ void SandboxLinux::PreinitializeSandbox() { + const int yama_status = Yama::GetStatus(); + yama_is_enforcing_ = (yama_status & Yama::STATUS_PRESENT) && + (yama_status & Yama::STATUS_ENFORCING); ++ ++ flatpak_sandbox_level_ = ++ sandbox::FlatpakSandbox::GetInstance()->GetSandboxLevel(); + pre_initialized_ = true; + } + +@@ -274,6 +278,10 @@ int SandboxLinux::GetStatus() { + sandbox_status_flags_ |= kPIDNS; + if (NamespaceSandbox::InNewNetNamespace()) + sandbox_status_flags_ |= kNetNS; ++ } else if (flatpak_sandbox_level_ == ++ sandbox::FlatpakSandbox::SandboxLevel::kRestricted) { ++ // Flatpak sandboxes always use new namespaces. ++ sandbox_status_flags_ |= kFlatpak | kPIDNS | kNetNS; + } + + // We report whether the sandbox will be activated when renderers, workers +diff --git a/sandbox/policy/linux/sandbox_linux.h b/sandbox/policy/linux/sandbox_linux.h +index 0e4f78b9e6cb7..91b638d8a96e0 100644 +--- a/sandbox/policy/linux/sandbox_linux.h ++++ b/sandbox/policy/linux/sandbox_linux.h +@@ -13,6 +13,7 @@ + #include "base/memory/raw_ptr.h" + #include "base/posix/global_descriptors.h" + #include "base/sanitizer_buildflags.h" ++#include "sandbox/linux/services/flatpak_sandbox.h" + #include "sandbox/linux/syscall_broker/broker_command.h" + #include "sandbox/linux/syscall_broker/broker_file_permission.h" + #include "sandbox/policy/export.h" +@@ -91,6 +92,9 @@ class SANDBOX_POLICY_EXPORT SandboxLinux { + // User namespace sandbox active. + kUserNS = 1 << 6, + ++ // Flatpak sandbox active. ++ kFlatpak = 1 << 7, ++ + // A flag that denotes an invalid sandbox status. + kInvalid = 1 << 31, + }; +@@ -292,6 +296,10 @@ class SANDBOX_POLICY_EXPORT SandboxLinux { + bool seccomp_bpf_with_tsync_supported_; // Accurate if pre_initialized_. + bool yama_is_enforcing_; // Accurate if pre_initialized_. + bool initialize_sandbox_ran_; // InitializeSandbox() was called. ++ // Accurate if pre_initialized_, used to save the state of the Flatpak ++ // sandbox, as once we're in the BPF sandbox any attempts to check the Flatpak ++ // state will cause EPERM errors. ++ sandbox::FlatpakSandbox::SandboxLevel flatpak_sandbox_level_; + std::unique_ptr setuid_sandbox_client_; + #if BUILDFLAG(USING_SANITIZER) + std::unique_ptr<__sanitizer_sandbox_arguments> sanitizer_args_; +diff --git a/services/service_manager/service_process_launcher.cc b/services/service_manager/service_process_launcher.cc +index e24a262fe90d5..c20fdbd88a1a6 100644 +--- a/services/service_manager/service_process_launcher.cc ++++ b/services/service_manager/service_process_launcher.cc +@@ -41,6 +41,7 @@ + #endif + + #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) ++#include "sandbox/linux/services/flatpak_sandbox.h" + #include "sandbox/linux/services/namespace_sandbox.h" + #endif + +@@ -286,8 +287,15 @@ void ServiceProcessLauncher::ProcessState::StopInBackground() { + return; + + int rv = -1; +- LOG_IF(ERROR, !child_process_.WaitForExit(&rv)) +- << "Failed to wait for child process"; ++ bool success = false; ++ auto* flatpak_sandbox = sandbox::FlatpakSandbox::GetInstance(); ++ if (flatpak_sandbox->IsPidSandboxed(child_process_.Pid())) { ++ success = flatpak_sandbox->Wait(child_process_.Pid(), &rv); ++ } else { ++ success = child_process_.WaitForExit(&rv); ++ } ++ LOG_IF(ERROR, success) << "Failed to wait for child process"; ++ + child_process_.Close(); + } + +-- +2.46.1 + diff --git a/SOURCES/flatpak-Adjust-paths-for-the-sandbox.patch b/SOURCES/flatpak-Adjust-paths-for-the-sandbox.patch new file mode 100644 index 00000000..3b1863de --- /dev/null +++ b/SOURCES/flatpak-Adjust-paths-for-the-sandbox.patch @@ -0,0 +1,86 @@ +From 997c9dc4160c468f26acb851eb65408f41e2a091 Mon Sep 17 00:00:00 2001 +From: Ryan Gonzalez +Date: Tue, 25 Aug 2020 19:26:07 -0500 +Subject: [PATCH] flatpak: Adjust paths for the sandbox + +--- + chrome/common/BUILD.gn | 4 ++++ + chrome/common/chrome_paths.cc | 26 +++++++++++++++++++++++++- + 2 files changed, 29 insertions(+), 1 deletion(-) + +diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn +index 76cf6a5985990..4044c87b656c4 100644 +--- a/chrome/common/BUILD.gn ++++ b/chrome/common/BUILD.gn +@@ -642,6 +642,10 @@ static_library("constants") { + "//third_party/widevine/cdm:headers", + ] + } ++ ++ if (is_linux) { ++ deps += [ "//sandbox/linux:sandbox_services" ] ++ } + } + + # Use a static library here because many test binaries depend on this but don't +diff --git a/chrome/common/chrome_paths.cc b/chrome/common/chrome_paths.cc +index ec09803ffabcf..ecbc300b99e1d 100644 +--- a/chrome/common/chrome_paths.cc ++++ b/chrome/common/chrome_paths.cc +@@ -38,6 +38,10 @@ + #include "base/win/registry.h" + #endif + ++#if BUILDFLAG(IS_LINUX) ++#include "sandbox/linux/services/flatpak_sandbox.h" ++#endif ++ + #if BUILDFLAG(ENABLE_WIDEVINE) + #include "third_party/widevine/cdm/widevine_cdm_common.h" // nogncheck + #endif +@@ -537,6 +541,14 @@ bool PathProvider(int key, base::FilePath* result) { + break; + #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_MAC) && !BUILDFLAG(IS_OPENBSD) + case chrome::DIR_POLICY_FILES: { ++#if defined(OS_LINUX) ++ if (sandbox::FlatpakSandbox::GetInstance()->GetSandboxLevel() > ++ sandbox::FlatpakSandbox::SandboxLevel::kNone) { ++ cur = base::FilePath( ++ FILE_PATH_LITERAL("/app/chromium/extensions/policies")); ++ break; ++ } ++#endif + cur = base::FilePath(policy::kPolicyPath); + break; + } +@@ -557,7 +569,13 @@ bool PathProvider(int key, base::FilePath* result) { + #endif + #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + case chrome::DIR_STANDALONE_EXTERNAL_EXTENSIONS: { +- cur = base::FilePath(kFilepathSinglePrefExtensions); ++ if (sandbox::FlatpakSandbox::GetInstance()->GetSandboxLevel() > ++ sandbox::FlatpakSandbox::SandboxLevel::kNone) { ++ cur = base::FilePath( ++ FILE_PATH_LITERAL("/app/chromium/extensions/extensions")); ++ } else { ++ cur = base::FilePath(kFilepathSinglePrefExtensions); ++ } + break; + } + #endif +@@ -604,6 +622,12 @@ bool PathProvider(int key, base::FilePath* result) { + "/Library/Application Support/Chromium/NativeMessagingHosts")); + #endif + #else // BUILDFLAG(IS_MAC) ++ if (sandbox::FlatpakSandbox::GetInstance()->GetSandboxLevel() > ++ sandbox::FlatpakSandbox::SandboxLevel::kNone) { ++ cur = base::FilePath(FILE_PATH_LITERAL( ++ "/app/chromium/extensions/native-messaging-hosts")); ++ break; ++ } + #if BUILDFLAG(GOOGLE_CHROME_BRANDING) + cur = base::FilePath( + FILE_PATH_LITERAL("/etc/opt/chrome/native-messaging-hosts")); +-- +2.46.1 + diff --git a/SOURCES/flatpak-Expose-Widevine-into-the-sandbox.patch b/SOURCES/flatpak-Expose-Widevine-into-the-sandbox.patch new file mode 100644 index 00000000..b6f953dc --- /dev/null +++ b/SOURCES/flatpak-Expose-Widevine-into-the-sandbox.patch @@ -0,0 +1,326 @@ +From 0446b3432c90bbab893c7bde22a5e664e14753af Mon Sep 17 00:00:00 2001 +From: Ryan Gonzalez +Date: Tue, 17 Nov 2020 13:00:39 -0600 +Subject: [PATCH] flatpak: Expose Widevine into the sandbox + +--- + .../zygote_host/zygote_host_impl_linux.cc | 54 +++++++++++++- + sandbox/linux/services/flatpak_sandbox.cc | 74 ++++++++++++++----- + sandbox/linux/services/flatpak_sandbox.h | 27 ++++++- + 3 files changed, 131 insertions(+), 24 deletions(-) + +diff --git a/content/browser/zygote_host/zygote_host_impl_linux.cc b/content/browser/zygote_host/zygote_host_impl_linux.cc +index 1703fb6ade044..3e8eb87981230 100644 +--- a/content/browser/zygote_host/zygote_host_impl_linux.cc ++++ b/content/browser/zygote_host/zygote_host_impl_linux.cc +@@ -9,7 +9,10 @@ + #include + + #include "base/files/file_enumerator.h" ++#include "base/files/file_util.h" + #include "base/logging.h" ++#include "base/nix/xdg_util.h" ++#include "base/path_service.h" + #include "base/posix/unix_domain_socket.h" + #include "base/process/kill.h" + #include "base/process/launch.h" +@@ -18,9 +21,12 @@ + #include "base/types/fixed_array.h" + #include "build/build_config.h" + #include "build/chromeos_buildflags.h" ++#include "chrome/common/chrome_paths.h" // nogncheck + #include "content/common/zygote/zygote_commands_linux.h" + #include "content/common/zygote/zygote_communication_linux.h" + #include "content/common/zygote/zygote_handle_impl_linux.h" ++#include "content/public/common/cdm_info.h" ++#include "content/public/common/content_client.h" + #include "content/public/common/zygote/zygote_handle.h" + #include "sandbox/linux/services/credentials.h" + #include "sandbox/linux/services/flatpak_sandbox.h" +@@ -29,6 +35,7 @@ + #include "sandbox/linux/suid/common/sandbox.h" + #include "sandbox/policy/linux/sandbox_linux.h" + #include "sandbox/policy/switches.h" ++#include "third_party/widevine/cdm/buildflags.h" // nogncheck + + #if BUILDFLAG(IS_CHROMEOS) + #include "content/common/zygote/zygote_communication_linux.h" +@@ -193,8 +200,51 @@ pid_t ZygoteHostImpl::LaunchZygote( + if (is_sandboxed_zygote && use_namespace_sandbox_) { + process = sandbox::NamespaceSandbox::LaunchProcess(*cmd_line, options); + } else if (is_sandboxed_zygote && use_flatpak_sandbox_) { +- process = sandbox::FlatpakSandbox::GetInstance()->LaunchProcess(*cmd_line, +- options); ++ sandbox::FlatpakSandbox::SpawnOptions spawn_options; ++ ++#if BUILDFLAG(ENABLE_LIBRARY_CDMS) ++ // Expose the CDM paths into the sandbox. This is similar to PreSandboxInit ++ // in content_main_runner_impl.cc. ++ std::vector cdms; ++ GetContentClient()->AddContentDecryptionModules(&cdms, nullptr); ++ for (const auto& cdm : cdms) { ++ if (!spawn_options.ExposePathRo(cdm.path)) { ++ LOG(ERROR) << "Failed to expose CDM module"; ++ } ++ } ++#endif ++ ++#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT) ++ // Make sure we also expose the full Widevine CDM folder so it can be ++ // detected. ++ // TODO: Remove the explicit dependencies on chrome::. ++ base::FilePath widevine_cdm_path; ++ if (!base::PathService::Get(chrome::DIR_COMPONENT_UPDATED_WIDEVINE_CDM, ++ &widevine_cdm_path)) { ++ LOG(ERROR) << "Failed to get Widevine CDM folder for sandbox forwarding"; ++ } ++ ++ LOG(INFO) << "Widevine CDM path IS: " << widevine_cdm_path; ++ ++ if (!widevine_cdm_path.empty() && base::PathExists(widevine_cdm_path)) { ++ if (!spawn_options.ExposePathRo(widevine_cdm_path)) { ++ LOG(ERROR) << "Failed to expose updated Widevine CDM path"; ++ } ++ } ++ ++ // The Widevine data is found relative to $XDG_CONFIG_HOME, which is not set ++ // by default when running a sandboxed process. ++ auto env = base::Environment::Create(); ++ base::FilePath xdgConfigHome = base::nix::GetXDGDirectory( ++ env.get(), base::nix::kXdgConfigHomeEnvVar, nullptr); ++ if (!xdgConfigHome.empty()) { ++ options.environment[base::nix::kXdgConfigHomeEnvVar] = ++ xdgConfigHome.value(); ++ } ++#endif ++ ++ process = sandbox::FlatpakSandbox::GetInstance()->LaunchProcess( ++ *cmd_line, options, spawn_options); + } else { + process = base::LaunchProcess(*cmd_line, options); + } +diff --git a/sandbox/linux/services/flatpak_sandbox.cc b/sandbox/linux/services/flatpak_sandbox.cc +index 2a915a5b9fa11..ed8d4c0556f63 100644 +--- a/sandbox/linux/services/flatpak_sandbox.cc ++++ b/sandbox/linux/services/flatpak_sandbox.cc +@@ -4,6 +4,7 @@ + + #include "sandbox/linux/services/flatpak_sandbox.h" + ++#include + #include + #include + #include +@@ -92,6 +93,18 @@ enum FlatpakSpawnSandboxFlags { + kFlatpakSpawnSandbox_ShareA11yBus = 1 << 4, + }; + ++bool FlatpakSandbox::SpawnOptions::ExposePathRo(base::FilePath path) { ++ base::ScopedFD fd( ++ HANDLE_EINTR(open(path.value().c_str(), O_PATH | O_NOFOLLOW))); ++ if (!fd.is_valid()) { ++ PLOG(ERROR) << "Failed to expose path " << path; ++ return false; ++ } ++ ++ sandbox_expose_ro.push_back(std::move(fd)); ++ return true; ++} ++ + FlatpakSandbox::FlatpakSandbox() + : bus_thread_("FlatpakPortalBus"), process_info_cv_(&process_info_lock_) {} + +@@ -168,8 +181,9 @@ bool FlatpakSandbox::IsPidSandboxed(base::ProcessId relative_pid) { + + base::Process FlatpakSandbox::LaunchProcess( + const base::CommandLine& cmdline, +- const base::LaunchOptions& launch_options) { +- base::ProcessId external_pid = Spawn(cmdline, launch_options); ++ const base::LaunchOptions& launch_options, ++ const SpawnOptions& spawn_options /*= {}*/) { ++ base::ProcessId external_pid = Spawn(cmdline, launch_options, spawn_options); + if (external_pid == base::kNullProcessId) { + return base::Process(); + } +@@ -363,9 +377,9 @@ void FlatpakSandbox::OnSpawnExitedSignal(dbus::Signal* signal) { + process_info_cv_.Broadcast(); + } + +-base::ProcessId FlatpakSandbox::Spawn( +- const base::CommandLine& cmdline, +- const base::LaunchOptions& launch_options) { ++base::ProcessId FlatpakSandbox::Spawn(const base::CommandLine& cmdline, ++ const base::LaunchOptions& launch_options, ++ const SpawnOptions& spawn_options) { + base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, + base::BlockingType::MAY_BLOCK); + base::ScopedAllowBaseSyncPrimitives allow_wait; +@@ -391,24 +405,26 @@ base::ProcessId FlatpakSandbox::Spawn( + FROM_HERE, + base::BindOnce(&FlatpakSandbox::SpawnOnBusThread, base::Unretained(this), + base::Unretained(&external_pid), base::Unretained(&event), +- cmdline, launch_options)); ++ base::Unretained(&cmdline), ++ base::Unretained(&launch_options), ++ base::Unretained(&spawn_options))); + event.Wait(); + + return external_pid; + } + +-void FlatpakSandbox::SpawnOnBusThread( +- base::ProcessId* out_external_pid, +- base::WaitableEvent* event, +- const base::CommandLine& cmdline, +- const base::LaunchOptions& launch_options) { ++void FlatpakSandbox::SpawnOnBusThread(base::ProcessId* out_external_pid, ++ base::WaitableEvent* event, ++ const base::CommandLine* cmdline, ++ const base::LaunchOptions* launch_options, ++ const SpawnOptions* spawn_options) { + dbus::ObjectProxy* object_proxy = GetPortalObjectProxy(); + dbus::MethodCall method_call(kFlatpakPortalInterfaceName, "Spawn"); + dbus::MessageWriter writer(&method_call); + + const base::FilePath& current_directory = +- !launch_options.current_directory.empty() +- ? launch_options.current_directory ++ !launch_options->current_directory.empty() ++ ? launch_options->current_directory + // Change to /app since it's guaranteed to always be present in + // the sandbox. + : kFlatpakAppPath; +@@ -417,7 +433,7 @@ void FlatpakSandbox::SpawnOnBusThread( + dbus::MessageWriter argv_writer(nullptr); + writer.OpenArray("ay", &argv_writer); + +- for (const std::string& arg : cmdline.argv()) { ++ for (const std::string& arg : cmdline->argv()) { + WriteStringAsByteArray(&argv_writer, arg); + } + +@@ -439,7 +455,7 @@ void FlatpakSandbox::SpawnOnBusThread( + WriteFdPairMap(&fds_writer, STDOUT_FILENO, STDOUT_FILENO); + WriteFdPairMap(&fds_writer, STDERR_FILENO, STDERR_FILENO); + +- for (const auto& pair : launch_options.fds_to_remap) { ++ for (const auto& pair : launch_options->fds_to_remap) { + WriteFdPairMap(&fds_writer, pair.first, pair.second); + } + +@@ -448,7 +464,7 @@ void FlatpakSandbox::SpawnOnBusThread( + dbus::MessageWriter env_writer(nullptr); + writer.OpenArray("{ss}", &env_writer); + +- for (const auto& pair : launch_options.environment) { ++ for (const auto& pair : launch_options->environment) { + dbus::MessageWriter entry_writer(nullptr); + env_writer.OpenDictEntry(&entry_writer); + +@@ -472,11 +488,11 @@ void FlatpakSandbox::SpawnOnBusThread( + #else + #endif + +- if (launch_options.clear_environment) { ++ if (launch_options->clear_environment) { + spawn_flags |= kFlatpakSpawn_ClearEnvironment; + } + +- if (launch_options.kill_on_parent_death) { ++ if (launch_options->kill_on_parent_death) { + spawn_flags |= kFlatpakSpawn_WatchBus; + } + +@@ -485,6 +501,28 @@ void FlatpakSandbox::SpawnOnBusThread( + dbus::MessageWriter options_writer(nullptr); + writer.OpenArray("{sv}", &options_writer); + ++ if (!spawn_options->sandbox_expose_ro.empty()) { ++ dbus::MessageWriter entry_writer(nullptr); ++ options_writer.OpenDictEntry(&entry_writer); ++ ++ entry_writer.AppendString("sandbox-expose-fd-ro"); ++ ++ dbus::MessageWriter variant_writer(nullptr); ++ entry_writer.OpenVariant("ah", &variant_writer); ++ ++ dbus::MessageWriter fds_writer(nullptr); ++ variant_writer.OpenArray("h", &fds_writer); ++ ++ for (const base::ScopedFD& fd : spawn_options->sandbox_expose_ro) { ++ CHECK(fd.is_valid()) << "Invalid spawn expose fd"; ++ fds_writer.AppendFileDescriptor(fd.get()); ++ } ++ ++ variant_writer.CloseContainer(&fds_writer); ++ entry_writer.CloseContainer(&variant_writer); ++ options_writer.CloseContainer(&entry_writer); ++ } ++ + if (sandbox_flags != 0) { + dbus::MessageWriter entry_writer(nullptr); + options_writer.OpenDictEntry(&entry_writer); +diff --git a/sandbox/linux/services/flatpak_sandbox.h b/sandbox/linux/services/flatpak_sandbox.h +index 167bbc85945ad..de8e7165b4573 100644 +--- a/sandbox/linux/services/flatpak_sandbox.h ++++ b/sandbox/linux/services/flatpak_sandbox.h +@@ -9,6 +9,8 @@ + #include "base/compiler_specific.h" + #include "base/containers/flat_map.h" + #include "base/containers/flat_set.h" ++#include "base/files/file_path.h" ++#include "base/files/scoped_file.h" + #include "base/no_destructor.h" + #include "base/process/kill.h" + #include "base/process/launch.h" +@@ -34,6 +36,20 @@ namespace sandbox { + // it is known inside the sandbox's PID namespace. + class SANDBOX_EXPORT FlatpakSandbox { + public: ++ class SpawnOptions { ++ public: ++ SpawnOptions() = default; ++ SpawnOptions(const SpawnOptions& other) = delete; ++ SpawnOptions(SpawnOptions&& other) = delete; ++ ++ bool ExposePathRo(base::FilePath path); ++ ++ private: ++ friend class FlatpakSandbox; ++ ++ std::vector sandbox_expose_ro; ++ }; ++ + static FlatpakSandbox* GetInstance(); + + // Represents the level of sandboxing inside a Flatpak. kNone means this is +@@ -55,7 +71,8 @@ class SANDBOX_EXPORT FlatpakSandbox { + // GetRelativePid. This is the reason why a vanilla ProcessId is returned + // rather than a base::Process instance. + base::Process LaunchProcess(const base::CommandLine& cmdline, +- const base::LaunchOptions& launch_options); ++ const base::LaunchOptions& launch_options, ++ const SpawnOptions& spawn_options = {}); + + // Indefinitely waits for the given process and fills the exit code pointer + // if given and non-null. Returns false on wait failure. +@@ -84,11 +101,13 @@ class SANDBOX_EXPORT FlatpakSandbox { + void OnSpawnExitedSignal(dbus::Signal* signal); + + base::ProcessId Spawn(const base::CommandLine& cmdline, +- const base::LaunchOptions& launch_options); ++ const base::LaunchOptions& launch_options, ++ const SpawnOptions& spawn_options); + void SpawnOnBusThread(base::ProcessId* out_external_pid, + base::WaitableEvent* event, +- const base::CommandLine& cmdline, +- const base::LaunchOptions& launch_options); ++ const base::CommandLine* cmdline, ++ const base::LaunchOptions* launch_options, ++ const SpawnOptions* spawn_options); + void OnSpawnResponse(base::ProcessId* out_external_pid, + base::WaitableEvent* event, + dbus::Response* response, +-- +2.46.1 + diff --git a/SPECS/chromium.spec b/SPECS/chromium.spec index fd62813a..b52e55d4 100644 --- a/SPECS/chromium.spec +++ b/SPECS/chromium.spec @@ -40,7 +40,7 @@ ninja -j %{numjobs} -C '%1' '%2' # enable|disable headless client build -%if %{with gost} +%if %{with gost} || 0%{?flatpak} %global build_headless 0 %else %global build_headless 1 @@ -55,7 +55,7 @@ %endif # enable|disable headless client build -%if %{with gost} +%if %{with gost} || 0%{?flatpak} %global build_chromedriver 0 %else %global build_chromedriver 1 @@ -478,6 +478,12 @@ Patch412: add-ppc64-architecture-to-extensions.diff Patch413: fix-unknown-warning-option-messages.diff Patch414: cargo-add-ppc64.diff +# flatpak sandbox patches from +# https://github.com/flathub/org.chromium.Chromium/tree/master/patches/chromium +Patch416: flatpak-Add-initial-sandbox-support.patch +Patch417: flatpak-Adjust-paths-for-the-sandbox.patch +Patch418: flatpak-Expose-Widevine-into-the-sandbox.patch + # Old Yandex patch Patch600: 0001-Yandex-as-default-search-engine.patch # MSVSphere @@ -739,7 +745,7 @@ BuildRequires: libXNVCtrl-devel %endif # One of the python scripts invokes git to look for a hash. So helpful. -BuildRequires: /usr/bin/git +BuildRequires: git-core BuildRequires: hwdata BuildRequires: kernel-headers %if ! %{bundlelibevent} @@ -858,7 +864,7 @@ Requires: nss-mdns%{_isa} # GTK modules it expects to find for some reason. Requires: libcanberra-gtk3%{_isa} -%if 0%{?fedora} +%if 0%{?fedora} && %{undefined flatpak} # This enables support for u2f tokens Requires: u2f-hidraw-policy %endif @@ -999,9 +1005,11 @@ Provides: bundled(xdg-mime) Provides: bundled(xdg-user-dirs) # Provides: bundled(zlib) = 1.2.11 +%if %{undefined flatpak} # For selinux scriptlet Requires(post): /usr/sbin/semanage Requires(post): /usr/sbin/restorecon +%endif # Package with libffmpeg.so Requires: %{name}-ffmpeg%{_isa} = %{version}-%{release} @@ -1281,6 +1289,12 @@ sed -i 's/std::string data_dir_basename = "chromium"/std::string data_dir_basena %patch -P414 -p1 -b .rust-add-ppc64-case %endif +%if 0%{?flatpak} +%patch -P416 -p1 -b .flatpak-initial-sandbox +%patch -P417 -p1 -b .flatpak-sandbox-paths +%patch -P418 -p1 -b .flatpak-widevine +%endif + %patch -P601 -p1 -b .Added-Russian-description-and-summary-for-gnome-soft %if ! %{with gost} %patch -P602 -p1 -b .Yandex-as-default-search-engine @@ -1333,12 +1347,12 @@ popd popd %else mkdir -p third_party/node/linux/node-linux-x64/bin - ln -sf %{_bindir}/node third_party/node/linux/node-linux-x64/bin/node + ln -s $(which node) third_party/node/linux/node-linux-x64/bin/node %endif # Get rid of the bundled esbuild %if 0%{?fedora} - ln -sf %{_bindir}/esbuild third_party/devtools-frontend/src/third_party/esbuild/esbuild + ln -sf $(which esbuild) third_party/devtools-frontend/src/third_party/esbuild/esbuild %else %ifarch x86_64 tar -zxf %{SOURCE14} --directory %{_tmppath} @@ -1353,7 +1367,7 @@ popd rm -rf buildtools/third_party/eu-strip/bin/eu-strip # Replace it with a symlink to the Fedora copy -ln -s %{_bindir}/eu-strip buildtools/third_party/eu-strip/bin/eu-strip +ln -s $(which eu-strip) buildtools/third_party/eu-strip/bin/eu-strip %if %{bundlelibusbx} # no hackity hack hack @@ -1361,7 +1375,7 @@ ln -s %{_bindir}/eu-strip buildtools/third_party/eu-strip/bin/eu-strip # hackity hack hack rm -rf third_party/libusb/src/libusb/libusb.h # we _shouldn't need to do this, but it looks like we do. -cp -a %{_includedir}/libusb-1.0/libusb.h third_party/libusb/src/libusb/libusb.h +cp -a $(pkg-config --variable=includedir libusb-1.0)/libusb-1.0/libusb.h third_party/libusb/src/libusb/libusb.h %endif # Hard code extra version @@ -1428,11 +1442,8 @@ export RUSTC_BOOTSTRAP=1 # set rustc version rustc_version="$(rustc --version)" # set rust bindgen root -%if 0%{?rhel} == 8 -rust_bindgen_root="$PWD%{_prefix}" -%else -rust_bindgen_root="%{_prefix}" -%endif +rust_bindgen_root="$(which bindgen | sed 's#/bin/.*##')" +rust_sysroot_absolute="$(rustc --print sysroot)" # set clang version clang_version="$(clang --version | sed -n 's/clang version //p' | cut -d. -f1)" @@ -1476,7 +1487,7 @@ CHROMIUM_CORE_GN_DEFINES+=' clang_use_chrome_plugins=false' CHROMIUM_CORE_GN_DEFINES+=' use_lld=true' # enable system rust -CHROMIUM_CORE_GN_DEFINES+=' rust_sysroot_absolute="%{_prefix}"' +CHROMIUM_CORE_GN_DEFINES+=" rust_sysroot_absolute=\"$rust_sysroot_absolute\"" CHROMIUM_CORE_GN_DEFINES+=" rust_bindgen_root=\"$rust_bindgen_root\"" CHROMIUM_CORE_GN_DEFINES+=" rustc_version=\"$rustc_version\"" @@ -1523,13 +1534,13 @@ CHROMIUM_BROWSER_GN_DEFINES+=' rtc_use_h264=true' CHROMIUM_BROWSER_GN_DEFINES+=' use_kerberos=true' %if %{use_qt} -CHROMIUM_BROWSER_GN_DEFINES+=' use_qt=true moc_qt5_path="%{_libdir}/qt5/bin/"' -%else +CHROMIUM_BROWSER_GN_DEFINES+=" use_qt=true moc_qt5_path=\"$(%{_qt5_qmake} -query QT_HOST_BINS)\"" +HROMIUM_BROWSER_GN_DEFINES+=" CHROMIUM_BROWSER_GN_DEFINES+=' use_qt=false' %endif %if %{use_qt6} -CHROMIUM_BROWSER_GN_DEFINES+=' use_qt6=true moc_qt6_path="%{_libdir}/qt6/libexec/"' +CHROMIUM_BROWSER_GN_DEFINES+=" use_qt6=true moc_qt6_path=\"$(%{_qt6_qmake} -query QT_HOST_LIBEXECS)\"" %else CHROMIUM_BROWSER_GN_DEFINES+=' use_qt6=false' %endif @@ -1695,7 +1706,7 @@ fi %if %{bootstrap} tools/gn/bootstrap/bootstrap.py --gn-gen-args="$CHROMIUM_CORE_GN_DEFINES $CHROMIUM_BROWSER_GN_DEFINES" %else -mkdir -p %{chromebuilddir} && cp -a %{_bindir}/gn %{chromebuilddir}/ +mkdir -p %{chromebuilddir} && cp -a $(which gn) %{chromebuilddir}/ %endif %{chromebuilddir}/gn --script-executable=%{chromium_pybin} gen --args="$CHROMIUM_CORE_GN_DEFINES $CHROMIUM_BROWSER_GN_DEFINES" %{chromebuilddir} @@ -1933,6 +1944,7 @@ cp -a %{SOURCE9} %{buildroot}%{_datadir}/gnome-control-center/default-apps/ # README.fedora cp %{SOURCE1} . +%if %{undefined flatpak} %post # Set SELinux labels - semanage itself will adjust the lib directory naming # But only do it when selinux is enabled, otherwise, it gets noisy. @@ -1942,6 +1954,7 @@ if selinuxenabled; then semanage fcontext -a -t chrome_sandbox_exec_t /usr/lib/chrome-sandbox &>/dev/null || : restorecon -R -v %{chromium_path}/%{chromium_browser_channel} &>/dev/null || : fi +%endif %if %{build_remoting} %pretrans -n chrome-remote-desktop -p