From 822997bd340c4f546e60ffab6700218db63686ab Mon Sep 17 00:00:00 2001 From: Jade Lovelace Date: Fri, 4 Oct 2024 23:33:46 -0700 Subject: [PATCH] libstore: ban unpacking case hacked filenames from NARs There is absolutely no good reason these should show up in NARs besides misconfigured systems and as long as the case hack exists, unpacking such a NAR will cause its repacking to be wrong on systems with case hack enabled. This should not have any security impact on Lix to fix, but it was one of the vectors for CVE-2024-45593: https://github.com/NixOS/nix/security/advisories/GHSA-h4vv-h3jq-v493 Change-Id: I85b6075aacc069ee7039240b0f525804a2d8edcb --- src/libutil/archive.cc | 8 +++++++- tests/functional2/store/test_evil_nars.py | 17 ++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/libutil/archive.cc b/src/libutil/archive.cc index 225483804..cca89d847 100644 --- a/src/libutil/archive.cc +++ b/src/libutil/archive.cc @@ -270,7 +270,13 @@ static Generator parseObject(Source & source, const Path & path) co_yield MetadataString{name}; if (name.empty() || name == "." || name == ".." || name.find('/') != std::string::npos - || name.find((char) 0) != std::string::npos) + || name.find((char) 0) != std::string::npos + // The case hack is a thing that only exists on the + // filesystem. + // Unpacking one appearing in a NAR is super + // sketchy because it will at minimum cause corruption at + // the time of repacking the NAR. + || name.find(caseHackSuffix) != std::string::npos) { throw Error("NAR contains invalid file name '%1%'", name); } diff --git a/tests/functional2/store/test_evil_nars.py b/tests/functional2/store/test_evil_nars.py index d658a15f3..937eed0ce 100644 --- a/tests/functional2/store/test_evil_nars.py +++ b/tests/functional2/store/test_evil_nars.py @@ -60,15 +60,14 @@ EVIL_NARS: list[tuple[str, NarItem]] = [ (b'meow', Regular(False, b'kbityy')) ])), ])), - # FIXME: ban casehacked filenames being extracted from NARs - # ('invalid-casehack-1', Directory([ - # (b'ZZZ~nix~case~hack~2', Regular(False, b'meow')), - # (b'zzz~nix~case~hack~1', Regular(False, b'eepy')), - # ])), - # ('invalid-casehack-2', Directory([ - # (b'ZZZ~nix~case~hack~1', Regular(False, b'meow')), - # (b'zzz~nix~case~hack~1', Regular(False, b'eepy')), - # ])), + ('invalid-casehack-1', Directory([ + (b'ZZZ~nix~case~hack~2', Regular(False, b'meow')), + (b'zzz~nix~case~hack~1', Regular(False, b'eepy')), + ])), + ('invalid-casehack-2', Directory([ + (b'ZZZ~nix~case~hack~1', Regular(False, b'meow')), + (b'zzz~nix~case~hack~1', Regular(False, b'eepy')), + ])), ] @pytest.mark.parametrize(['name', 'nar'], EVIL_NARS)