From b73d675e713417452432988480f63d5dc441b3a0 Mon Sep 17 00:00:00 2001 From: Yaakov Selkowitz Date: Sun, 22 Dec 2024 18:20:01 -0500 Subject: [PATCH] Fix flatpak build This adds patches from Flathub to use the Flatpak sandbox (as the GUID sandbox is not compatible with Flatpak's own), removes dependencies on host services, and uses automatic detection of dependency locations (since dependencies are not necessarily found in /app even when building therein, as the buildroot is still in /usr). --- chromium.spec | 45 +- flatpak-Add-initial-sandbox-support.patch | 1325 +++++++++++++++++ flatpak-Adjust-paths-for-the-sandbox.patch | 86 ++ ...pak-Expose-Widevine-into-the-sandbox.patch | 326 ++++ 4 files changed, 1771 insertions(+), 11 deletions(-) create mode 100644 flatpak-Add-initial-sandbox-support.patch create mode 100644 flatpak-Adjust-paths-for-the-sandbox.patch create mode 100644 flatpak-Expose-Widevine-into-the-sandbox.patch diff --git a/chromium.spec b/chromium.spec index cb186e4b..b03f7868 100644 --- a/chromium.spec +++ b/chromium.spec @@ -37,12 +37,18 @@ # enable|disable chromedriver %global build_chromedriver 1 +%if 0%{?flatpak} +%global build_chromedriver 0 +%endif # enable|disable headless client build %global build_headless 1 %ifarch ppc64le %global build_headless 0 %endif +%if 0%{?flatpak} +%global build_headless 0 +%endif # enable|disable chrome-remote-desktop build %global build_remoting 0 @@ -442,6 +448,12 @@ Patch413: fix-unknown-warning-option-messages.diff Patch414: cargo-add-ppc64.diff Patch415: add-ppc64-pthread-stack-size.patch +# 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 + # upstream patches # Use chromium-latest.py to generate clean tarball from released build tarballs, found here: @@ -666,7 +678,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} @@ -782,7 +794,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 @@ -923,9 +935,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 %description Chromium is an open-source web browser, powered by WebKit (Blink). @@ -1135,6 +1149,12 @@ Qt6 UI for chromium. %patch -P415 -p1 -b .add-ppc64-pthread-stack-size %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 + # Change shebang in all relevant files in this directory and all subdirectories # See `man find` for how the `-exec command {} +` syntax works find -type f \( -iname "*.py" \) -exec sed -i '1s=^#! */usr/bin/\(python\|env python\)[23]\?=#!%{chromium_pybin}=' {} + @@ -1155,12 +1175,12 @@ find -type f \( -iname "*.py" \) -exec sed -i '1s=^#! */usr/bin/\(python\|env py popd %else mkdir -p third_party/node/linux/node-linux-x64/bin - ln -s %{_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} @@ -1175,7 +1195,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 @@ -1183,7 +1203,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 @@ -1247,7 +1267,8 @@ export RUSTC_BOOTSTRAP=1 # set rustc version rustc_version="$(rustc --version)" # set rust bindgen root -rust_bindgen_root="%{_prefix}" +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)" @@ -1291,7 +1312,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\"" @@ -1343,13 +1364,13 @@ CHROMIUM_BROWSER_GN_DEFINES+=' rtc_use_h264=false' CHROMIUM_BROWSER_GN_DEFINES+=' use_kerberos=true' %if %{use_qt} -CHROMIUM_BROWSER_GN_DEFINES+=' use_qt=true moc_qt5_path="%{_libdir}/qt5/bin/"' +CHROMIUM_BROWSER_GN_DEFINES+=" use_qt=true moc_qt5_path=\"$(%{_qt5_qmake} -query QT_HOST_BINS)\"" %else 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 @@ -1518,7 +1539,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} @@ -1742,6 +1763,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. @@ -1751,6 +1773,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 diff --git a/flatpak-Add-initial-sandbox-support.patch b/flatpak-Add-initial-sandbox-support.patch new file mode 100644 index 00000000..dae2d943 --- /dev/null +++ b/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/flatpak-Adjust-paths-for-the-sandbox.patch b/flatpak-Adjust-paths-for-the-sandbox.patch new file mode 100644 index 00000000..3b1863de --- /dev/null +++ b/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/flatpak-Expose-Widevine-into-the-sandbox.patch b/flatpak-Expose-Widevine-into-the-sandbox.patch new file mode 100644 index 00000000..b6f953dc --- /dev/null +++ b/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 +