diff options
author | Ludovic Courtès <ludo@gnu.org> | 2025-04-15 14:46:45 +0200 |
---|---|---|
committer | John Kehayias <john.kehayias@protonmail.com> | 2025-06-24 10:07:59 -0400 |
commit | 0e79d5b6550729e6ce3bac1e979638ac054ba5a5 (patch) | |
tree | 3b142f79018a174e386d9f4a890ba71abcdf6c05 | |
parent | c659f977bb09de6d5615e6aa9efddedc1d9ff458 (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>
-rw-r--r-- | nix/libutil/util.cc | 8 |
1 files changed, 5 insertions, 3 deletions
diff --git a/nix/libutil/util.cc b/nix/libutil/util.cc index 327edf471f..8938a213f6 100644 --- a/nix/libutil/util.cc +++ b/nix/libutil/util.cc @@ -473,7 +473,8 @@ static void copyFileRecursively(int sourceroot, const Path &source, if (sourceFd == -1) throw SysError(format("opening `%1%'") % source); AutoCloseFD destinationFd = openat(destinationroot, destination.c_str(), - O_CLOEXEC | O_CREAT | O_WRONLY | O_TRUNC, + O_CLOEXEC | O_CREAT | O_WRONLY | O_TRUNC + | O_NOFOLLOW | O_EXCL, st.st_mode); if (destinationFd == -1) throw SysError(format("opening `%1%'") % source); @@ -495,7 +496,8 @@ static void copyFileRecursively(int sourceroot, const Path &source, throw SysError(format("creating directory `%1%'") % destination); AutoCloseFD destinationFd = openat(destinationroot, destination.c_str(), - O_CLOEXEC | O_RDONLY | O_DIRECTORY); + O_CLOEXEC | O_RDONLY | O_DIRECTORY + | O_NOFOLLOW); if (err != 0) throw SysError(format("opening directory `%1%'") % destination); @@ -505,7 +507,7 @@ static void copyFileRecursively(int sourceroot, const Path &source, throw SysError(format("opening `%1%'") % source); if (deleteSource && !(st.st_mode & S_IWUSR)) { - /* Ensure the directory writable so files within it can be + /* Ensure the directory is writable so files within it can be deleted. */ if (fchmod(sourceFd, st.st_mode | S_IWUSR) == -1) throw SysError(format("making `%1%' directory writable") % source); |