Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: NixOS/nix
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: d3f780996c05
Choose a base ref
...
head repository: NixOS/nix
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: c8cc50d46e78
Choose a head ref
  • 2 commits
  • 5 files changed
  • 1 contributor

Commits on Jun 6, 2017

  1. Always use the Darwin sandbox

    Even with "build-use-sandbox = false", we now use sandboxing with a
    permissive profile that allows everything except the creation of
    setuid/setgid binaries.
    edolstra committed Jun 6, 2017
    1

    Verified

    This commit was signed with the committer’s verified signature.
    edolstra Eelco Dolstra
    Copy the full SHA
    85e93d7 View commit details
  2. Verified

    This commit was signed with the committer’s verified signature.
    edolstra Eelco Dolstra
    Copy the full SHA
    c8cc50d View commit details
Showing with 105 additions and 86 deletions.
  1. +1 −3 .gitignore
  2. +93 −81 src/libstore/build.cc
  3. +4 −2 src/libstore/local.mk
  4. +2 −0 src/libstore/sandbox-defaults.sb
  5. +5 −0 src/libstore/sandbox-minimal.sb
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -48,9 +48,7 @@ perl/Makefile.config
/src/libexpr/nix.tbl

# /src/libstore/
/src/libstore/schema.sql.gen.hh
/src/libstore/sandbox-defaults.sb.gen.hh
/src/libstore/sandbox-network.sb.gen.hh
/src/libstore/*.gen.hh

/src/nix/nix

174 changes: 93 additions & 81 deletions src/libstore/build.cc
Original file line number Diff line number Diff line change
@@ -1716,11 +1716,17 @@ void DerivationGoal::startBuilder()
/* If `build-users-group' is not empty, then we have to build as
one of the members of that group. */
if (settings.buildUsersGroup != "" && getuid() == 0) {
#if defined(__linux__) || defined(__APPLE__)
buildUser = std::make_unique<UserLock>();

/* Make sure that no other processes are executing under this
uid. */
buildUser->kill();
#else
/* Don't know how to block the creation of setuid/setgid
binaries on this platform. */
throw Error("build users are not supported on this platform for security reasons");
#endif
}

/* Create a temporary directory where the build will take
@@ -2611,104 +2617,102 @@ void DerivationGoal::runChild()

const char *builder = "invalid";

string sandboxProfile;
if (drv->isBuiltin()) {
;
}
#if __APPLE__
else if (useChroot) {
/* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
PathSet ancestry;
else {
/* This has to appear before import statements. */
std::string sandboxProfile = "(version 1)\n";

if (useChroot) {

/* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
PathSet ancestry;

/* We build the ancestry before adding all inputPaths to the store because we know they'll
all have the same parents (the store), and there might be lots of inputs. This isn't
particularly efficient... I doubt it'll be a bottleneck in practice */
for (auto & i : dirsInChroot) {
Path cur = i.first;
while (cur.compare("/") != 0) {
cur = dirOf(cur);
ancestry.insert(cur);
}
}

/* We build the ancestry before adding all inputPaths to the store because we know they'll
all have the same parents (the store), and there might be lots of inputs. This isn't
particularly efficient... I doubt it'll be a bottleneck in practice */
for (auto & i : dirsInChroot) {
Path cur = i.first;
/* And we want the store in there regardless of how empty dirsInChroot. We include the innermost
path component this time, since it's typically /nix/store and we care about that. */
Path cur = worker.store.storeDir;
while (cur.compare("/") != 0) {
cur = dirOf(cur);
ancestry.insert(cur);
cur = dirOf(cur);
}
}

/* And we want the store in there regardless of how empty dirsInChroot. We include the innermost
path component this time, since it's typically /nix/store and we care about that. */
Path cur = worker.store.storeDir;
while (cur.compare("/") != 0) {
ancestry.insert(cur);
cur = dirOf(cur);
}

/* Add all our input paths to the chroot */
for (auto & i : inputPaths)
dirsInChroot[i] = i;

/* This has to appear before import statements */
sandboxProfile += "(version 1)\n";
/* Add all our input paths to the chroot */
for (auto & i : inputPaths)
dirsInChroot[i] = i;

/* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
if (settings.darwinLogSandboxViolations) {
sandboxProfile += "(deny default)\n";
} else {
sandboxProfile += "(deny default (with no-log))\n";
}

sandboxProfile +=
#include "sandbox-defaults.sb.gen.hh"
;
/* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
if (settings.darwinLogSandboxViolations) {
sandboxProfile += "(deny default)\n";
} else {
sandboxProfile += "(deny default (with no-log))\n";
}

if (fixedOutput)
sandboxProfile +=
#include "sandbox-network.sb.gen.hh"
#include "sandbox-defaults.sb.gen.hh"
;

/* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
to find temporary directories, so we want to open up a broader place for them to dump their files, if needed. */
Path globalTmpDir = canonPath(getEnv("TMPDIR", "/tmp"), true);

/* They don't like trailing slashes on subpath directives */
if (globalTmpDir.back() == '/') globalTmpDir.pop_back();

/* Our rwx outputs */
sandboxProfile += "(allow file-read* file-write* process-exec\n";
for (auto & i : missingPaths) {
sandboxProfile += (format("\t(subpath \"%1%\")\n") % i.c_str()).str();
}
sandboxProfile += ")\n";

/* Our inputs (transitive dependencies and any impurities computed above)
without file-write* allowed, access() incorrectly returns EPERM
*/
sandboxProfile += "(allow file-read* file-write* process-exec\n";
for (auto & i : dirsInChroot) {
if (i.first != i.second.source)
throw Error(format(
"can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin")
% i.first % i.second.source);
if (fixedOutput)
sandboxProfile +=
#include "sandbox-network.sb.gen.hh"
;

string path = i.first;
struct stat st;
if (lstat(path.c_str(), &st)) {
if (i.second.optional && errno == ENOENT)
continue;
throw SysError(format("getting attributes of path ‘%1%’") % path);
/* Our rwx outputs */
sandboxProfile += "(allow file-read* file-write* process-exec\n";
for (auto & i : missingPaths) {
sandboxProfile += (format("\t(subpath \"%1%\")\n") % i.c_str()).str();
}
if (S_ISDIR(st.st_mode))
sandboxProfile += (format("\t(subpath \"%1%\")\n") % path).str();
else
sandboxProfile += (format("\t(literal \"%1%\")\n") % path).str();
}
sandboxProfile += ")\n";
sandboxProfile += ")\n";

/* Our inputs (transitive dependencies and any impurities computed above)
without file-write* allowed, access() incorrectly returns EPERM
*/
sandboxProfile += "(allow file-read* file-write* process-exec\n";
for (auto & i : dirsInChroot) {
if (i.first != i.second.source)
throw Error(format(
"can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin")
% i.first % i.second.source);

string path = i.first;
struct stat st;
if (lstat(path.c_str(), &st)) {
if (i.second.optional && errno == ENOENT)
continue;
throw SysError(format("getting attributes of path ‘%1%’") % path);
}
if (S_ISDIR(st.st_mode))
sandboxProfile += (format("\t(subpath \"%1%\")\n") % path).str();
else
sandboxProfile += (format("\t(literal \"%1%\")\n") % path).str();
}
sandboxProfile += ")\n";

/* Allow file-read* on full directory hierarchy to self. Allows realpath() */
sandboxProfile += "(allow file-read*\n";
for (auto & i : ancestry) {
sandboxProfile += (format("\t(literal \"%1%\")\n") % i.c_str()).str();
}
sandboxProfile += ")\n";
/* Allow file-read* on full directory hierarchy to self. Allows realpath() */
sandboxProfile += "(allow file-read*\n";
for (auto & i : ancestry) {
sandboxProfile += (format("\t(literal \"%1%\")\n") % i.c_str()).str();
}
sandboxProfile += ")\n";

sandboxProfile += additionalSandboxProfile;
sandboxProfile += additionalSandboxProfile;
} else
sandboxProfile +=
#include "sandbox-minimal.sb.gen.hh"
;

debug("Generated sandbox profile:");
debug(sandboxProfile);
@@ -2717,6 +2721,13 @@ void DerivationGoal::runChild()

writeFile(sandboxFile, sandboxProfile);

/* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
to find temporary directories, so we want to open up a broader place for them to dump their files, if needed. */
Path globalTmpDir = canonPath(getEnv("TMPDIR", "/tmp"), true);

/* They don't like trailing slashes on subpath directives */
if (globalTmpDir.back() == '/') globalTmpDir.pop_back();

builder = "/usr/bin/sandbox-exec";
args.push_back("sandbox-exec");
args.push_back("-f");
@@ -2725,12 +2736,13 @@ void DerivationGoal::runChild()
args.push_back("_GLOBAL_TMP_DIR=" + globalTmpDir);
args.push_back(drv->builder);
}
#endif
#else
else {
builder = drv->builder.c_str();
string builderBasename = baseNameOf(drv->builder);
args.push_back(builderBasename);
}
#endif

for (auto & i : drv->args)
args.push_back(rewriteStrings(i, inputRewrites));
6 changes: 4 additions & 2 deletions src/libstore/local.mk
Original file line number Diff line number Diff line change
@@ -36,14 +36,16 @@ libstore_CXXFLAGS = \

$(d)/local-store.cc: $(d)/schema.sql.gen.hh

$(d)/build.cc: $(d)/sandbox-defaults.sb.gen.hh $(d)/sandbox-network.sb.gen.hh
sandbox-headers = $(d)/sandbox-defaults.sb.gen.hh $(d)/sandbox-network.sb.gen.hh $(d)/sandbox-minimal.sb.gen.hh

$(d)/build.cc: $(sandbox-headers)

%.gen.hh: %
@echo 'R"foo(' >> $@.tmp
$(trace-gen) cat $< >> $@.tmp
@echo ')foo"' >> $@.tmp
@mv $@.tmp $@

clean-files += $(d)/schema.sql.gen.hh $(d)/sandbox-defaults.sb.gen.hh $(d)/sandbox-network.sb.gen.hh
clean-files += $(d)/schema.sql.gen.hh $(sandbox-headers)

$(eval $(call install-file-in, $(d)/nix-store.pc, $(prefix)/lib/pkgconfig, 0644))
2 changes: 2 additions & 0 deletions src/libstore/sandbox-defaults.sb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
(define TMPDIR (param "_GLOBAL_TMP_DIR"))

(deny default)

; Disallow creating setuid/setgid binaries, since that
; would allow breaking build user isolation.
(deny file-write-setugid)
5 changes: 5 additions & 0 deletions src/libstore/sandbox-minimal.sb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
(allow default)

; Disallow creating setuid/setgid binaries, since that
; would allow breaking build user isolation.
(deny file-write-setugid)