summaryrefslogtreecommitdiff
path: root/nix
diff options
context:
space:
mode:
Diffstat (limited to 'nix')
-rw-r--r--nix/libstore/build.cc43
1 files changed, 41 insertions, 2 deletions
diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc
index 20d83fea4a8..4f486f08220 100644
--- a/nix/libstore/build.cc
+++ b/nix/libstore/build.cc
@@ -1621,6 +1621,24 @@ void DerivationGoal::startBuilder()
1621 auto drvName = storePathToName(drvPath); 1621 auto drvName = storePathToName(drvPath);
1622 tmpDir = createTempDir("", "guix-build-" + drvName, false, false, 0700); 1622 tmpDir = createTempDir("", "guix-build-" + drvName, false, false, 0700);
1623 1623
1624 if (useChroot) {
1625 /* Make the build directory seen by the build process a sub-directory.
1626 That way, "/tmp/guix-build-foo.drv-0" is root-owned, and thus its
1627 permissions cannot be changed by the build process, while
1628 "/tmp/guix-build-foo.drv-0/top" is owned by the build user. This
1629 cannot be done when !useChroot because then $NIX_BUILD_TOP would
1630 be inaccessible to the build user by its full file name.
1631
1632 If the build user could make the build directory world-writable,
1633 then an attacker could create in it a hardlink to a root-owned file
1634 such as /etc/shadow. If 'keepFailed' is true, the daemon would
1635 then chown that hardlink to the user, giving them write access to
1636 that file. */
1637 tmpDir += "/top";
1638 if (mkdir(tmpDir.c_str(), 0700) == 1)
1639 throw SysError("creating top-level build directory");
1640 }
1641
1624 /* In a sandbox, for determinism, always use the same temporary 1642 /* In a sandbox, for determinism, always use the same temporary
1625 directory. */ 1643 directory. */
1626 tmpDirInSandbox = useChroot ? canonPath("/tmp", true) + "/guix-build-" + drvName + "-0" : tmpDir; 1644 tmpDirInSandbox = useChroot ? canonPath("/tmp", true) + "/guix-build-" + drvName + "-0" : tmpDir;
@@ -2626,20 +2644,41 @@ static void _chown(const Path & path, uid_t uid, gid_t gid)
2626void DerivationGoal::deleteTmpDir(bool force) 2644void DerivationGoal::deleteTmpDir(bool force)
2627{ 2645{
2628 if (tmpDir != "") { 2646 if (tmpDir != "") {
2647 // When useChroot is true, tmpDir looks like
2648 // "/tmp/guix-build-foo.drv-0/top". Its parent is root-owned.
2649 string top;
2650 if (useChroot) {
2651 if (baseNameOf(tmpDir) != "top") abort();
2652 top = dirOf(tmpDir);
2653 } else top = tmpDir;
2654
2629 if (settings.keepFailed && !force) { 2655 if (settings.keepFailed && !force) {
2630 printMsg(lvlError, 2656 printMsg(lvlError,
2631 format("note: keeping build directory `%2%'") 2657 format("note: keeping build directory `%2%'")
2632 % drvPath % tmpDir); 2658 % drvPath % top);
2633 chmod(tmpDir.c_str(), 0755); 2659 chmod(tmpDir.c_str(), 0755);
2660
2634 // Change the ownership if clientUid is set. Never change the 2661 // Change the ownership if clientUid is set. Never change the
2635 // ownership or the group to "root" for security reasons. 2662 // ownership or the group to "root" for security reasons.
2636 if (settings.clientUid != (uid_t) -1 && settings.clientUid != 0) { 2663 if (settings.clientUid != (uid_t) -1 && settings.clientUid != 0) {
2637 _chown(tmpDir, settings.clientUid, 2664 _chown(tmpDir, settings.clientUid,
2638 settings.clientGid != 0 ? settings.clientGid : -1); 2665 settings.clientGid != 0 ? settings.clientGid : -1);
2666
2667 if (top != tmpDir) {
2668 // Rename tmpDir to its parent, with an intermediate step.
2669 string pivot = top + ".pivot";
2670 if (rename(top.c_str(), pivot.c_str()) == -1)
2671 throw SysError("pivoting failed build tree");
2672 if (rename((pivot + "/top").c_str(), top.c_str()) == -1)
2673 throw SysError("renaming failed build tree");
2674 rmdir(pivot.c_str());
2675 }
2639 } 2676 }
2640 } 2677 }
2641 else 2678 else {
2642 deletePath(tmpDir); 2679 deletePath(tmpDir);
2680 if (top != tmpDir) rmdir(dirOf(tmpDir).c_str());
2681 }
2643 tmpDir = ""; 2682 tmpDir = "";
2644 } 2683 }
2645} 2684}