From a552ed625bdd1cb7336606e8cbf7bd6ebc6442e2 Mon Sep 17 00:00:00 2001 From: Colin Date: Fri, 16 Aug 2024 02:15:46 +0000 Subject: [PATCH] make-sandboxed: fix several edge-cases for e.g. brave, firefox, especially around handling of wrapped binaries --- modules/programs/make-sandboxed.nix | 56 ++++++++++------------------- 1 file changed, 19 insertions(+), 37 deletions(-) diff --git a/modules/programs/make-sandboxed.nix b/modules/programs/make-sandboxed.nix index 891e0386f..157fa8b5b 100644 --- a/modules/programs/make-sandboxed.nix +++ b/modules/programs/make-sandboxed.nix @@ -156,7 +156,9 @@ let for item in "''${items[@]}"; do if [ "$item" != . ] && [ "$item" != .. ]; then local target="$_dir/$item" - if [ -x "$target" ] && ! [ -d "$target" ]; then + if [ -d "$target" ]; then + crawlAndWrap "$output" "$target" + elif [ -x "$target" ] && [[ "$item" != .* ]]; then # in the case of symlinks, deref until we find the real file, or the symlink points outside the package target=$(derefWhileInSameOutput "$output" "$target") target=$(findUnwrapped "$target") @@ -165,10 +167,8 @@ let local bin=$(basename "$target") sandboxWrap "$parent" "$bin" fi - elif [ -d "$target" ]; then - crawlAndWrap "$output" "$target" fi - # ignore all non-binaries + # ignore all non-binaries or "hidden" binaries (to avoid double-wrapping) fi done } @@ -196,15 +196,14 @@ let ; # helper used for `wrapperType == "wrappedDerivation"` which simply symlinks all a package's binaries into a new derivation - symlinkBinaries = pkgName: package: (runCommandLocal "${pkgName}-bin-only" { + symlinkDirs = suffix: symlinkRoots: pkgName: package: (runCommandLocal "${pkgName}-${suffix}" { + env.symlinkRoots = lib.concatStringsSep " " symlinkRoots; nativeBuildInputs = [ gnused ]; } '' set -e symlinkPath() { if [ -e "$out/$1" ]; then : # already linked. may happen when e.g. the package has bin/foo, and sbin -> bin. - elif ! [ -x "${package}/$1" ]; then - : # not a binary, nor a directory (-x) which could contain binaries elif [ -L "${package}/$1" ]; then local target=$(readlink "${package}/$1") if [[ "$target" =~ ^${package}/ ]]; then @@ -235,7 +234,7 @@ let mkdir -p "$out/$1" items=($(ls -a "${package}/$1")) for item in "''${items[@]}"; do - if [ "$item" != . ] && [ "$item" != .. ]; then + if [ "$item" != . ] && [ "$item" != .. ] && [[ "$item" != .* ]]; then symlinkPath "$1/$item" fi done @@ -244,16 +243,22 @@ let ln -s "${package}/$1" "$out/$1" fi } - symlinkPath bin - symlinkPath sbin - symlinkPath libexec - # allow downstream wrapping to hook this (and thereby actually wrap the binaries) + mkdir -p "$out" + _symlinkRoots=($symlinkRoots) + for root in "''${_symlinkRoots[@]}"; do + echo "crawling top-level directory for symlinking: $root" + symlinkPath "$root" + done + # allow downstream wrapping to hook this (and thereby actually wrap binaries, etc) + runHook postInstall runHook postFixup '').overrideAttrs (_: { # specifically, preserve meta.priority meta = extractMeta package; }); + symlinkBinaries = symlinkDirs "bin-only" [ "bin" "sbin" "libexec" ]; + # helper used for `wrapperType == "wrappedDerivation"` which ensures that any copied/symlinked share/ files (like .desktop) files # don't point to the unwrapped binaries. # other important files it preserves: @@ -281,12 +286,9 @@ let } # remove any files which exist in sandoxedBin (makes it possible to sandbox /opt-style packages) - # also remove any files which would be "hidden". mostly useful for /opt-style packages which contain nix-wrapped binaries. removeUnwanted() { local file_=$(basename "$1") - if [[ "$file_" == .* ]]; then - rm -r "$out/$1" - elif [ -f "$out/$1" ] || [ -L "$out/$1" ]; then + if [ -f "$out/$1" ] || [ -L "$out/$1" ]; then if [ -e "${sandboxedBin}/$1" ]; then rm "$out/$1" fi @@ -320,8 +322,6 @@ let passthru = (prevAttrs.passthru or {}) // { # check that sandboxedNonBin references only sandboxed binaries, and never the original unsandboxed binaries. # do this by dereferencing all sandboxedNonBin symlinks, and making `unsandboxed` a disallowedReference. - # further, since the sandboxed binaries intentionally reference the unsandboxed binaries, - # we have to patch those out as a way to whitelist them. checkSandboxed = let sandboxedNonBin = fixHardcodedRefs unsandboxed sandboxedBin unsandboxedNonBin; in runCommandLocal "${sandboxedNonBin.name}-check-sandboxed" @@ -342,25 +342,7 @@ let # patch them to use the sandboxed binaries, # and add some passthru metadata to enforce no lingering references to the unsandboxed binaries. sandboxNonBinaries = pkgName: unsandboxed: sandboxedBin: let - sandboxedWithoutFixedRefs = (runCommandLocal "${pkgName}-sandboxed-non-binary" { - nativeBuildInputs = [ xorg.lndir ]; - } '' - set -e - mkdir "$out" - # link in a limited subset of the directories. - # lib/ is the primary one to avoid, because of shared objects that would be unsandboxed if dlopen'd. - # all other directories are safe-ish, because they won't end up on PATH or LDPATH. - for dir in etc share; do - if [ -e "${unsandboxed}/$dir" ]; then - mkdir "$out/$dir" - lndir "${unsandboxed}/$dir" "$out/$dir" - fi - done - runHook postInstall - '').overrideAttrs (_: { - # specifically for meta.priority, though it shouldn't actually matter here. - meta = extractMeta unsandboxed; - }); + sandboxedWithoutFixedRefs = symlinkDirs "non-bin" [ "etc" "share" ] pkgName unsandboxed; in fixHardcodedRefs unsandboxed sandboxedBin sandboxedWithoutFixedRefs; # take the nearly-final sandboxed package, with binaries and all else, and