diff options
author | Eelco Dolstra <e.dolstra@tudelft.nl> | 2008-12-11 18:57:10 +0000 |
---|---|---|
committer | Eelco Dolstra <e.dolstra@tudelft.nl> | 2008-12-11 18:57:10 +0000 |
commit | ac5478eb525f85e560d822ab3b5cb6ccb664d56e (patch) | |
tree | 261a1751ef2cc93cb53cf8009796953779f51f36 /src | |
parent | 652817046b2f999e29de8109fce9c61b5b18d22c (diff) |
* Don't provide the whole Nix store in the chroot, but only the
closure of the inputs. This really enforces that there can't be any
undeclared dependencies on paths in the store. This is done by
creating a fake Nix store and creating bind-mounts or hard-links in
the fake store for all paths in the closure. After the build, the
build output is moved from the fake store to the real store. TODO:
the chroot has to be on the same filesystem as the Nix store for
this to work, but this isn't enforced yet. (I.e. it only works
currently if /tmp is on the same FS as /nix/store.)
Diffstat (limited to 'src')
-rw-r--r-- | src/libstore/build.cc | 54 |
1 files changed, 44 insertions, 10 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc index cbbd3aa39..6992d02c4 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -648,6 +648,8 @@ private: /* Whether we're currently doing a chroot build. */ bool useChroot; + + Path chrootRootDir; /* RAII object to delete the chroot directory. */ boost::shared_ptr<AutoDelete> autoDelChroot; @@ -804,9 +806,7 @@ void DerivationGoal::haveDerivation() trace("loading derivation"); if (nrFailed != 0) { - printMsg(lvlError, - format("cannot build missing derivation `%1%'") - % drvPath); + printMsg(lvlError, format("cannot build missing derivation `%1%'") % drvPath); amDone(ecFailed); return; } @@ -1062,6 +1062,12 @@ void DerivationGoal::buildDone() i != drv.outputs.end(); ++i) { Path path = i->second.path; + + if (useChroot && pathExists(chrootRootDir + path)) { + if (rename((chrootRootDir + path).c_str(), path.c_str()) == -1) + throw SysError(format("moving build output `%1%' from the chroot to the Nix store") % path); + } + if (!pathExists(path)) continue; struct stat st; @@ -1449,6 +1455,14 @@ DerivationGoal::PrepareBuildReply DerivationGoal::prepareBuild() } +void chmod(const Path & path, mode_t mode) +{ + if (::chmod(path.c_str(), 01777) == -1) + throw SysError(format("setting permissions on `%1%'") % path); + +} + + void DerivationGoal::startBuilder() { startNest(nest, lvlInfo, @@ -1648,7 +1662,6 @@ void DerivationGoal::startBuilder() work properly. Purity checking for fixed-output derivations is somewhat pointless anyway. */ useChroot = queryBoolSetting("build-use-chroot", false); - Path chrootRootDir; Paths dirsInChroot; if (fixedOutput) useChroot = false; @@ -1669,9 +1682,7 @@ void DerivationGoal::startBuilder() instead.) */ Path chrootTmpDir = chrootRootDir + "/tmp"; createDirs(chrootTmpDir); - - if (chmod(chrootTmpDir.c_str(), 01777) == -1) - throw SysError("creating /tmp in the chroot"); + chmod(chrootTmpDir, 01777); /* Create a /etc/passwd with entries for the build user and the nobody account. The latter is kind of a hack to @@ -1695,8 +1706,31 @@ void DerivationGoal::startBuilder() dirsInChroot = querySetting("build-chroot-dirs", defaultDirs); - dirsInChroot.push_front(nixStore); dirsInChroot.push_front(tmpDir); + + /* Make the closure of the inputs available in the chroot, + rather than the whole Nix store. This prevents any access + to undeclared dependencies. Directories are bind-mounted, + while other inputs are hard-linked (since only directories + can be bind-mounted). !!! As an extra security + precaution, make the fake Nix store only writable by the + build user. */ + createDirs(chrootRootDir + nixStore); + chmod(chrootRootDir + nixStore, 01777); + + foreach (PathSet::iterator, i, inputPaths) { + struct stat st; + if (lstat(i->c_str(), &st)) + throw SysError(format("getting attributes of path `%1%'") % *i); + if (S_ISDIR(st.st_mode)) + dirsInChroot.push_back(*i); + else { + Path p = chrootRootDir + *i; + if (link(i->c_str(), p.c_str()) == -1) + throw SysError(format("linking `%1%' to `%2%'") % p % *i); + } + } + #else throw Error("chroot builds are not supported on this platform"); #endif @@ -1742,7 +1776,7 @@ void DerivationGoal::startBuilder() foreach (Paths::iterator, i, dirsInChroot) { Path source = *i; Path target = chrootRootDir + source; - printMsg(lvlError, format("bind mounting `%1%' to `%2%'") % source % target); + debug(format("bind mounting `%1%' to `%2%'") % source % target); createDirs(target); @@ -1781,7 +1815,7 @@ void DerivationGoal::startBuilder() safe. Also note that setuid() when run as root sets the real, effective and saved UIDs. */ if (buildUser.enabled()) { - debug(format("switching to user `%1%'") % buildUser.getUser()); + printMsg(lvlChatty, format("switching to user `%1%'") % buildUser.getUser()); if (amPrivileged()) { |