Merge "libstore/local-derivation-goal: prohibit creating setuid/setgid binaries" into main
This commit is contained in:
commit
79d0ae6670
9 changed files with 121 additions and 7 deletions
8
doc/manual/rl-next/fchmodat2-sandbox.md
Normal file
8
doc/manual/rl-next/fchmodat2-sandbox.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
synopsis: Creating setuid/setgid binaries with fchmodat2 is now prohibited by the build sandbox
|
||||
prs: 10501
|
||||
---
|
||||
|
||||
The build sandbox blocks any attempt to create setuid/setgid binaries, but didn't check
|
||||
for the use of the `fchmodat2` syscall which was introduced in Linux 6.6 and is used by
|
||||
glibc >=2.39. This is fixed now.
|
|
@ -163,9 +163,10 @@
|
|||
busybox-sandbox-shell = final.busybox-sandbox-shell or final.default-busybox-sandbox-shell;
|
||||
};
|
||||
|
||||
# Export the patched version of boehmgc that Lix uses into the overlay
|
||||
# Export the patched version of boehmgc & libseccomp that Lix uses into the overlay
|
||||
# for consumers of this flake.
|
||||
boehmgc-nix = final.nix.boehmgc-nix;
|
||||
libseccomp-nix = final.nix.libseccomp-nix;
|
||||
};
|
||||
in
|
||||
{
|
||||
|
|
|
@ -181,7 +181,7 @@ deps += cpuid
|
|||
|
||||
# seccomp only makes sense on Linux
|
||||
seccomp_required = is_linux ? get_option('seccomp-sandboxing') : false
|
||||
seccomp = dependency('libseccomp', 'seccomp', required : seccomp_required)
|
||||
seccomp = dependency('libseccomp', 'seccomp', required : seccomp_required, version : '>=2.5.5')
|
||||
configdata += {
|
||||
'HAVE_SECCOMP': seccomp.found().to_int(),
|
||||
}
|
||||
|
|
22
package.nix
22
package.nix
|
@ -21,12 +21,14 @@
|
|||
curl,
|
||||
doxygen,
|
||||
editline,
|
||||
fetchurl,
|
||||
flex,
|
||||
git,
|
||||
gtest,
|
||||
jq,
|
||||
libarchive,
|
||||
libcpuid,
|
||||
libseccomp-nix ? __forDefaults.libseccomp-nix,
|
||||
libseccomp,
|
||||
libsodium,
|
||||
lsof,
|
||||
|
@ -82,6 +84,18 @@
|
|||
};
|
||||
|
||||
lix-doc = pkgs.callPackage ./lix-doc/package.nix { };
|
||||
|
||||
# remove when we drop 23.11 support (which includes a version too old to know about fchmodat2)
|
||||
# see src/libstore/linux/fchmodat2-compat.hh
|
||||
libseccomp-nix =
|
||||
assert lib.versionOlder (lib.getVersion libseccomp) "2.5.5";
|
||||
libseccomp.overrideAttrs (_: rec {
|
||||
version = "2.5.5";
|
||||
src = fetchurl {
|
||||
url = "https://github.com/seccomp/libseccomp/releases/download/v${version}/libseccomp-${version}.tar.gz";
|
||||
hash = "sha256-JIosik2bmFiqa69ScSw0r+/PnJ6Ut23OAsHJqiX7M3U=";
|
||||
};
|
||||
});
|
||||
},
|
||||
}:
|
||||
let
|
||||
|
@ -273,7 +287,7 @@ stdenv.mkDerivation (finalAttrs: {
|
|||
lix-doc
|
||||
]
|
||||
++ lib.optionals stdenv.hostPlatform.isLinux [
|
||||
libseccomp
|
||||
libseccomp-nix
|
||||
busybox-sandbox-shell
|
||||
]
|
||||
++ lib.optional internalApiDocs rapidcheck
|
||||
|
@ -411,7 +425,9 @@ stdenv.mkDerivation (finalAttrs: {
|
|||
|
||||
passthru.perl-bindings = pkgs.callPackage ./perl { inherit fileset stdenv buildWithMeson; };
|
||||
|
||||
# Export the patched version of boehmgc.
|
||||
# Export the patched version of boehmgc & libseccomp.
|
||||
# flake.nix exports that into its overlay.
|
||||
passthru.boehmgc-nix = __forDefaults.boehmgc-nix;
|
||||
passthru = {
|
||||
inherit (__forDefaults) boehmgc-nix libseccomp-nix;
|
||||
};
|
||||
})
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
/* Includes required for chroot support. */
|
||||
#if __linux__
|
||||
#include <sys/ioctl.h>
|
||||
#include "linux/fchmodat2-compat.hh"
|
||||
#include <net/if.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <sys/mman.h>
|
||||
|
@ -1664,6 +1665,10 @@ void setupSeccomp()
|
|||
if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(fchmodat), 1,
|
||||
SCMP_A2(SCMP_CMP_MASKED_EQ, (scmp_datum_t) perm, (scmp_datum_t) perm)) != 0)
|
||||
throw SysError("unable to add seccomp rule");
|
||||
|
||||
if (seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), NIX_SYSCALL_FCHMODAT2, 1,
|
||||
SCMP_A2(SCMP_CMP_MASKED_EQ, (scmp_datum_t) perm, (scmp_datum_t) perm)) != 0)
|
||||
throw SysError("unable to add seccomp rule");
|
||||
}
|
||||
|
||||
/* Prevent builders from creating EAs or ACLs. Not all filesystems
|
||||
|
|
37
src/libstore/linux/fchmodat2-compat.hh
Normal file
37
src/libstore/linux/fchmodat2-compat.hh
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Determine the syscall number for `fchmodat2`.
|
||||
*
|
||||
* On most platforms this is 452. Exceptions can be found on
|
||||
* a glibc git checkout via `rg --pcre2 'define __NR_fchmodat2 (?!452)'`.
|
||||
*
|
||||
* The problem is that glibc 2.39 and libseccomp 2.5.5 are needed to
|
||||
* get the syscall number. However, a Nix built against nixpkgs 23.11
|
||||
* (glibc 2.38) should still have the issue fixed without depending
|
||||
* on the build environment.
|
||||
*
|
||||
* To achieve that, the macros below try to determine the platform and
|
||||
* set the syscall number which is platform-specific, but
|
||||
* in most cases 452.
|
||||
*
|
||||
* TODO: remove this when 23.11 is EOL and the entire (supported) ecosystem
|
||||
* is on glibc 2.39.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#if HAVE_SECCOMP
|
||||
# if defined(__alpha__)
|
||||
# define NIX_SYSCALL_FCHMODAT2 562
|
||||
# elif defined(__x86_64__) && SIZE_MAX == 0xFFFFFFFF // x32
|
||||
# define NIX_SYSCALL_FCHMODAT2 1073742276
|
||||
# elif defined(__mips__) && defined(__mips64) && defined(_ABIN64) // mips64/n64
|
||||
# define NIX_SYSCALL_FCHMODAT2 5452
|
||||
# elif defined(__mips__) && defined(__mips64) && defined(_ABIN32) // mips64/n32
|
||||
# define NIX_SYSCALL_FCHMODAT2 6452
|
||||
# elif defined(__mips__) && defined(_ABIO32) // mips32
|
||||
# define NIX_SYSCALL_FCHMODAT2 4452
|
||||
# else
|
||||
# define NIX_SYSCALL_FCHMODAT2 452
|
||||
# endif
|
||||
#endif // HAVE_SECCOMP
|
|
@ -155,7 +155,7 @@ in
|
|||
|
||||
setuid = lib.genAttrs
|
||||
["i686-linux" "x86_64-linux"]
|
||||
(system: runNixOSTestFor system ./setuid.nix);
|
||||
(system: runNixOSTestFor system ./setuid/setuid.nix);
|
||||
|
||||
ca-fd-leak = runNixOSTestFor "x86_64-linux" ./ca-fd-leak;
|
||||
|
||||
|
|
21
tests/nixos/setuid/fchmodat2-suid.c
Normal file
21
tests/nixos/setuid/fchmodat2-suid.c
Normal file
|
@ -0,0 +1,21 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
int main(void) {
|
||||
char *name = getenv("out");
|
||||
FILE *fd = fopen(name, "w");
|
||||
fprintf(fd, "henlo :3");
|
||||
fclose(fd);
|
||||
|
||||
// FIXME use something nicer here that's less
|
||||
// platform-dependent as soon as we go to 24.05
|
||||
// and the glibc is new enough to support fchmodat2
|
||||
long rs = syscall(452, NULL, name, S_ISUID, 0);
|
||||
assert(rs == -1);
|
||||
assert(errno == EPERM);
|
||||
}
|
|
@ -5,6 +5,16 @@
|
|||
let
|
||||
pkgs = config.nodes.machine.nixpkgs.pkgs;
|
||||
|
||||
fchmodat2-builder = pkgs.runCommandCC "fchmodat2-suid" {
|
||||
passAsFile = [ "code" ];
|
||||
code = builtins.readFile ./fchmodat2-suid.c;
|
||||
# Doesn't work with -O0, shuts up the warning about that.
|
||||
hardeningDisable = [ "fortify" ];
|
||||
} ''
|
||||
mkdir -p $out/bin/
|
||||
$CC -x c "$codePath" -O0 -g -o $out/bin/fchmodat2-suid
|
||||
'';
|
||||
|
||||
in
|
||||
{
|
||||
name = "setuid";
|
||||
|
@ -14,13 +24,29 @@ in
|
|||
{ virtualisation.writableStore = true;
|
||||
nix.settings.substituters = lib.mkForce [ ];
|
||||
nix.nixPath = [ "nixpkgs=${lib.cleanSource pkgs.path}" ];
|
||||
virtualisation.additionalPaths = [ pkgs.stdenvNoCC pkgs.pkgsi686Linux.stdenvNoCC ];
|
||||
virtualisation.additionalPaths = [
|
||||
pkgs.stdenvNoCC
|
||||
pkgs.pkgsi686Linux.stdenvNoCC
|
||||
fchmodat2-builder
|
||||
];
|
||||
# need at least 6.6 to test for fchmodat2
|
||||
boot.kernelPackages = pkgs.linuxKernel.packages.linux_6_6;
|
||||
|
||||
};
|
||||
|
||||
testScript = { nodes }: ''
|
||||
# fmt: off
|
||||
start_all()
|
||||
|
||||
with subtest("fchmodat2 suid regression test"):
|
||||
machine.succeed("""
|
||||
nix-build -E '(with import <nixpkgs> {}; runCommand "fchmodat2-suid" {
|
||||
BUILDER = builtins.storePath ${fchmodat2-builder};
|
||||
} "
|
||||
exec \\"$BUILDER\\"/bin/fchmodat2-suid
|
||||
")'
|
||||
""")
|
||||
|
||||
# Copying to /tmp should succeed.
|
||||
machine.succeed(r"""
|
||||
nix-build --no-sandbox -E '(with import <nixpkgs> {}; runCommand "foo" {} "
|
Loading…
Reference in a new issue