summaryrefslogtreecommitdiff
path: root/nix
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2025-04-15 14:46:45 +0200
committerJohn Kehayias <john.kehayias@protonmail.com>2025-06-24 10:07:59 -0400
commit0e79d5b6550729e6ce3bac1e979638ac054ba5a5 (patch)
tree3b142f79018a174e386d9f4a890ba71abcdf6c05 /nix
parentc659f977bb09de6d5615e6aa9efddedc1d9ff458 (diff)
daemon: Protect ‘copyFileRecursively’ from race conditions.
Previously, if an attacker managed to introduce a hard link or a symlink on one of the destination file names before it is opened, ‘copyFileRecursively’ would overwrite the symlink’s target or the hard link’s content. This kind of attack could be carried out while guix-daemon is copying the output or the chroot directory of a failed fixed-output derivation build, possibly allowing the attacker to escalate to the privileges of the build user. * nix/libutil/util.cc (copyFileRecursively): In the ‘S_ISREG’ case, open ‘destination’ with O_NOFOLLOW | O_EXCL. In the ‘S_ISDIR’ case, open ‘destination’ with O_NOFOLLOW. Reported-by: Reepca Russelstein <reepca@russelstein.xyz> Change-Id: I94273efe4e92c1a4270a98c5ec47bd098e9227c9 Signed-off-by: John Kehayias <john.kehayias@protonmail.com>
Diffstat (limited to 'nix')
-rw-r--r--nix/libutil/util.cc8
1 files changed, 5 insertions, 3 deletions
diff --git a/nix/libutil/util.cc b/nix/libutil/util.cc
index 327edf471f0..8938a213f6d 100644
--- a/nix/libutil/util.cc
+++ b/nix/libutil/util.cc
@@ -473,7 +473,8 @@ static void copyFileRecursively(int sourceroot, const Path &source,
473 if (sourceFd == -1) throw SysError(format("opening `%1%'") % source); 473 if (sourceFd == -1) throw SysError(format("opening `%1%'") % source);
474 474
475 AutoCloseFD destinationFd = openat(destinationroot, destination.c_str(), 475 AutoCloseFD destinationFd = openat(destinationroot, destination.c_str(),
476 O_CLOEXEC | O_CREAT | O_WRONLY | O_TRUNC, 476 O_CLOEXEC | O_CREAT | O_WRONLY | O_TRUNC
477 | O_NOFOLLOW | O_EXCL,
477 st.st_mode); 478 st.st_mode);
478 if (destinationFd == -1) throw SysError(format("opening `%1%'") % source); 479 if (destinationFd == -1) throw SysError(format("opening `%1%'") % source);
479 480
@@ -495,7 +496,8 @@ static void copyFileRecursively(int sourceroot, const Path &source,
495 throw SysError(format("creating directory `%1%'") % destination); 496 throw SysError(format("creating directory `%1%'") % destination);
496 497
497 AutoCloseFD destinationFd = openat(destinationroot, destination.c_str(), 498 AutoCloseFD destinationFd = openat(destinationroot, destination.c_str(),
498 O_CLOEXEC | O_RDONLY | O_DIRECTORY); 499 O_CLOEXEC | O_RDONLY | O_DIRECTORY
500 | O_NOFOLLOW);
499 if (err != 0) 501 if (err != 0)
500 throw SysError(format("opening directory `%1%'") % destination); 502 throw SysError(format("opening directory `%1%'") % destination);
501 503
@@ -505,7 +507,7 @@ static void copyFileRecursively(int sourceroot, const Path &source,
505 throw SysError(format("opening `%1%'") % source); 507 throw SysError(format("opening `%1%'") % source);
506 508
507 if (deleteSource && !(st.st_mode & S_IWUSR)) { 509 if (deleteSource && !(st.st_mode & S_IWUSR)) {
508 /* Ensure the directory writable so files within it can be 510 /* Ensure the directory is writable so files within it can be
509 deleted. */ 511 deleted. */
510 if (fchmod(sourceFd, st.st_mode | S_IWUSR) == -1) 512 if (fchmod(sourceFd, st.st_mode | S_IWUSR) == -1)
511 throw SysError(format("making `%1%' directory writable") % source); 513 throw SysError(format("making `%1%' directory writable") % source);