aboutsummaryrefslogtreecommitdiff
path: root/tests/nixos/setuid
diff options
context:
space:
mode:
Diffstat (limited to 'tests/nixos/setuid')
-rw-r--r--tests/nixos/setuid/fchmodat2-suid.c21
-rw-r--r--tests/nixos/setuid/setuid.nix151
2 files changed, 172 insertions, 0 deletions
diff --git a/tests/nixos/setuid/fchmodat2-suid.c b/tests/nixos/setuid/fchmodat2-suid.c
new file mode 100644
index 000000000..931489ad7
--- /dev/null
+++ b/tests/nixos/setuid/fchmodat2-suid.c
@@ -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);
+}
diff --git a/tests/nixos/setuid/setuid.nix b/tests/nixos/setuid/setuid.nix
new file mode 100644
index 000000000..c4dc8dccb
--- /dev/null
+++ b/tests/nixos/setuid/setuid.nix
@@ -0,0 +1,151 @@
+# Verify that Linux builds cannot create setuid or setgid binaries.
+
+{ lib, config, nixpkgs, ... }:
+
+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";
+
+ nodes.machine =
+ { config, lib, pkgs, ... }:
+ { virtualisation.writableStore = true;
+ nix.settings.substituters = lib.mkForce [ ];
+ nix.nixPath = [ "nixpkgs=${lib.cleanSource pkgs.path}" ];
+ 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" {} "
+ mkdir -p $out
+ cp ${pkgs.coreutils}/bin/id /tmp/id
+ ")'
+ """.strip())
+
+ machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]')
+
+ machine.succeed("rm /tmp/id")
+
+ # Creating a setuid binary should fail.
+ machine.fail(r"""
+ nix-build --no-sandbox -E '(with import <nixpkgs> {}; runCommand "foo" {} "
+ mkdir -p $out
+ cp ${pkgs.coreutils}/bin/id /tmp/id
+ chmod 4755 /tmp/id
+ ")'
+ """.strip())
+
+ machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]')
+
+ machine.succeed("rm /tmp/id")
+
+ # Creating a setgid binary should fail.
+ machine.fail(r"""
+ nix-build --no-sandbox -E '(with import <nixpkgs> {}; runCommand "foo" {} "
+ mkdir -p $out
+ cp ${pkgs.coreutils}/bin/id /tmp/id
+ chmod 2755 /tmp/id
+ ")'
+ """.strip())
+
+ machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]')
+
+ machine.succeed("rm /tmp/id")
+
+ # The checks should also work on 32-bit binaries.
+ machine.fail(r"""
+ nix-build --no-sandbox -E '(with import <nixpkgs> { system = "i686-linux"; }; runCommand "foo" {} "
+ mkdir -p $out
+ cp ${pkgs.coreutils}/bin/id /tmp/id
+ chmod 2755 /tmp/id
+ ")'
+ """.strip())
+
+ machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]')
+
+ machine.succeed("rm /tmp/id")
+
+ # The tests above use fchmodat(). Test chmod() as well.
+ machine.succeed(r"""
+ nix-build --no-sandbox -E '(with import <nixpkgs> {}; runCommand "foo" { buildInputs = [ perl ]; } "
+ mkdir -p $out
+ cp ${pkgs.coreutils}/bin/id /tmp/id
+ perl -e \"chmod 0666, qw(/tmp/id) or die\"
+ ")'
+ """.strip())
+
+ machine.succeed('[[ $(stat -c %a /tmp/id) = 666 ]]')
+
+ machine.succeed("rm /tmp/id")
+
+ machine.fail(r"""
+ nix-build --no-sandbox -E '(with import <nixpkgs> {}; runCommand "foo" { buildInputs = [ perl ]; } "
+ mkdir -p $out
+ cp ${pkgs.coreutils}/bin/id /tmp/id
+ perl -e \"chmod 04755, qw(/tmp/id) or die\"
+ ")'
+ """.strip())
+
+ machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]')
+
+ machine.succeed("rm /tmp/id")
+
+ # And test fchmod().
+ machine.succeed(r"""
+ nix-build --no-sandbox -E '(with import <nixpkgs> {}; runCommand "foo" { buildInputs = [ perl ]; } "
+ mkdir -p $out
+ cp ${pkgs.coreutils}/bin/id /tmp/id
+ perl -e \"my \\\$x; open \\\$x, qw(/tmp/id); chmod 01750, \\\$x or die\"
+ ")'
+ """.strip())
+
+ machine.succeed('[[ $(stat -c %a /tmp/id) = 1750 ]]')
+
+ machine.succeed("rm /tmp/id")
+
+ machine.fail(r"""
+ nix-build --no-sandbox -E '(with import <nixpkgs> {}; runCommand "foo" { buildInputs = [ perl ]; } "
+ mkdir -p $out
+ cp ${pkgs.coreutils}/bin/id /tmp/id
+ perl -e \"my \\\$x; open \\\$x, qw(/tmp/id); chmod 04777, \\\$x or die\"
+ ")'
+ """.strip())
+
+ machine.succeed('[[ $(stat -c %a /tmp/id) = 555 ]]')
+
+ machine.succeed("rm /tmp/id")
+ '';
+}