From 1b18ff36694c4d945ce45396262d2cf974079358 Mon Sep 17 00:00:00 2001 From: Morgan Jones Date: Fri, 16 May 2025 21:08:50 -0700 Subject: [PATCH] nixos/stage2-init: test nosuid and nodev mount options We can make some evil files in the store within stage1, and verify that they don't work. --- nixos/tests/boot-stage2.nix | 56 +++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/nixos/tests/boot-stage2.nix b/nixos/tests/boot-stage2.nix index c2bec97e4376..657e12116955 100644 --- a/nixos/tests/boot-stage2.nix +++ b/nixos/tests/boot-stage2.nix @@ -10,7 +10,24 @@ import ./make-test-python.nix ( lib, ... }: + let + # Prints the user's UID. Can't just do a shell script + # because setuid is ignored for interpreted programs. + uid = pkgs.writeCBin "uid" '' + #include + #include + int main(void) { + printf("%d\n", geteuid()); + return 0; + } + ''; + in { + users.users.alice = { + isNormalUser = true; + uid = 1000; + }; + virtualisation = { emptyDiskImages = [ 256 ]; @@ -32,6 +49,10 @@ import ./make-test-python.nix ( }; }; + environment.systemPackages = [ pkgs.xxd ]; + + system.extraDependencies = [ uid ]; + boot = { initrd = { # Format the upper Nix store. @@ -50,6 +71,19 @@ import ./make-test-python.nix ( mount -t overlay overlay \ -o lowerdir=/mnt-root/nix/store/ro,upperdir=/mnt-root/nix/store/rw,workdir=/mnt-root/nix/store/work \ /mnt-root/nix/store + + # Be very rude and try to put suid files and/or devices into the store. + evil=/mnt-root/nix/store/evil + mkdir -p $evil/bin $evil/dev + + echo "making evil suid..." >&2 + cp /mnt-root/${builtins.unsafeDiscardStringContext "${uid}"}/bin/uid $evil/bin/suid + chmod 4755 $evil/bin/suid + [ -u $evil/bin/suid ] || exit 1 + + echo "making evil devzero..." >&2 + mknod -m 666 $evil/dev/zero c 1 5 + [ -c $evil/dev/zero ] || exit 1 ''; kernelModules = [ "overlay" ]; @@ -70,6 +104,28 @@ import ./make-test-python.nix ( for opt in ["ro", "nosuid", "nodev"]: with subtest(f"testing store mount option: {opt}"): machine.succeed(f'[[ "$(findmnt --direction backward --first-only --noheadings --output OPTIONS /nix/store)" =~ (^|,){opt}(,|$) ]]') + + # should still be suid + machine.succeed('[ -u /nix/store/evil/bin/suid ]') + # runs as alice and is not root + machine.succeed('[ "$(sudo -u alice /nix/store/evil/bin/suid)" == 1000 ]') + # can be remounted and runs as root + machine.succeed('mount -o remount,suid,bind /nix/store && mount >&2') + machine.succeed('[ "$(sudo -u alice /nix/store/evil/bin/suid)" == 0 ]') + # double checking we can undo it + machine.succeed('mount -o remount,nosuid,bind /nix/store && mount >&2') + machine.succeed('[ "$(sudo -u alice /nix/store/evil/bin/suid)" == 1000 ]') + + # should still be a character device + machine.succeed('[ -c /nix/store/evil/dev/zero ]') + # should not work + machine.fail('[ "$(dd if=/nix/store/evil/dev/zero bs=1 count=1 | xxd -pl1)" == 00 ]') + # can be remounted and works + machine.succeed('mount -o remount,dev,bind /nix/store && mount >&2') + machine.succeed('[ "$(dd if=/nix/store/evil/dev/zero bs=1 count=1 | xxd -pl1)" == 00 ]') + # double checking we can undo it + machine.succeed('mount -o remount,nodev,bind /nix/store && mount >&2') + machine.fail('[ "$(dd if=/nix/store/evil/dev/zero bs=1 count=1 | xxd -pl1)" == 00 ]') ''; meta.maintainers = with pkgs.lib.maintainers; [ numinit ];