about summary refs log tree commit diff
path: root/src/libstore
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstore')
-rw-r--r--src/libstore/build.cc76
-rw-r--r--src/libstore/builtins.cc12
-rw-r--r--src/libstore/download.cc30
-rw-r--r--src/libstore/download.hh9
-rw-r--r--src/libstore/globals.cc5
-rw-r--r--src/libstore/local.mk1
-rw-r--r--src/libstore/sandbox-defaults.sb.in55
7 files changed, 131 insertions, 57 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 5674e83c9e66..93bb2144425f 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -74,6 +74,7 @@
 
 #if __linux__
 #include <sys/personality.h>
+#include <sys/mman.h>
 #endif
 
 #if HAVE_STATVFS
@@ -1245,12 +1246,19 @@ void DerivationGoal::inputsRealised()
 }
 
 
-static bool canBuildLocally(const string & platform)
+static bool isBuiltin(const BasicDerivation & drv)
+{
+    return string(drv.builder, 0, 8) == "builtin:";
+}
+
+
+static bool canBuildLocally(const BasicDerivation & drv)
 {
-    return platform == settings.thisSystem
+    return drv.platform == settings.thisSystem
+        || isBuiltin(drv)
 #if __linux__
-        || (platform == "i686-linux" && settings.thisSystem == "x86_64-linux")
-        || (platform == "armv6l-linux" && settings.thisSystem == "armv7l-linux")
+        || (drv.platform == "i686-linux" && settings.thisSystem == "x86_64-linux")
+        || (drv.platform == "armv6l-linux" && settings.thisSystem == "armv7l-linux")
 #endif
         ;
 }
@@ -1265,7 +1273,7 @@ static string get(const StringPairs & map, const string & key, const string & de
 
 bool willBuildLocally(const BasicDerivation & drv)
 {
-    return get(drv.env, "preferLocalBuild") == "1" && canBuildLocally(drv.platform);
+    return get(drv.env, "preferLocalBuild") == "1" && canBuildLocally(drv);
 }
 
 
@@ -1275,12 +1283,6 @@ bool substitutesAllowed(const BasicDerivation & drv)
 }
 
 
-static bool isBuiltin(const BasicDerivation & drv)
-{
-    return string(drv.builder, 0, 8) == "builtin:";
-}
-
-
 void DerivationGoal::tryToBuild()
 {
     trace("trying to build");
@@ -1682,7 +1684,7 @@ void DerivationGoal::startBuilder()
             "building path(s) %1%") % showPaths(missingPaths));
 
     /* Right platform? */
-    if (!canBuildLocally(drv->platform)) {
+    if (!canBuildLocally(*drv)) {
         if (settings.printBuildTrace)
             printMsg(lvlError, format("@ unsupported-platform %1% %2%") % drvPath % drv->platform);
         throw Error(
@@ -2057,7 +2059,7 @@ void DerivationGoal::startBuilder()
         auto lastPos = std::string::size_type{0};
         for (auto nlPos = lines.find('\n'); nlPos != string::npos;
                 nlPos = lines.find('\n', lastPos)) {
-            auto line = std::string{lines, lastPos, nlPos};
+            auto line = std::string{lines, lastPos, nlPos - lastPos};
             lastPos = nlPos + 1;
             if (state == stBegin) {
                 if (line == "extra-chroot-dirs") {
@@ -2128,14 +2130,17 @@ void DerivationGoal::startBuilder()
         ProcessOptions options;
         options.allowVfork = false;
         Pid helper = startProcess([&]() {
-            char stack[32 * 1024];
+            size_t stackSize = 1 * 1024 * 1024;
+            char * stack = (char *) mmap(0, stackSize,
+                PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
+            if (!stack) throw SysError("allocating stack");
             int flags = CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWUTS | CLONE_PARENT | SIGCHLD;
             if (!fixedOutput) flags |= CLONE_NEWNET;
-            pid_t child = clone(childEntry, stack + sizeof(stack) - 8, flags, this);
+            pid_t child = clone(childEntry, stack + stackSize, flags, this);
             if (child == -1 && errno == EINVAL)
                 /* Fallback for Linux < 2.13 where CLONE_NEWPID and
                    CLONE_PARENT are not allowed together. */
-                child = clone(childEntry, stack + sizeof(stack) - 8, flags & ~CLONE_NEWPID, this);
+                child = clone(childEntry, stack + stackSize, flags & ~CLONE_NEWPID, this);
             if (child == -1) throw SysError("cloning builder process");
             writeFull(builderOut.writeSide, int2String(child) + "\n");
             _exit(0);
@@ -2434,9 +2439,11 @@ void DerivationGoal::runChild()
             for (auto & i : inputPaths)
                 dirsInChroot[i] = i;
 
-            /* TODO: we should factor out the policy cleanly, so we don't have to repeat the constants every time... */
+            /* This has to appear before import statements */
             sandboxProfile += "(version 1)\n";
 
+            sandboxProfile += (format("(import \"%1%/nix/sandbox-defaults.sb\")\n") % settings.nixDataDir).str();
+
             /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
             if (settings.get("darwin-log-sandbox-violations", false)) {
                 sandboxProfile += "(deny default)\n";
@@ -2444,15 +2451,6 @@ void DerivationGoal::runChild()
                 sandboxProfile += "(deny default (with no-log))\n";
             }
 
-            sandboxProfile += "(allow file-read* file-write-data (literal \"/dev/null\"))\n";
-
-            sandboxProfile += "(allow file-read-metadata\n"
-                "\t(literal \"/var\")\n"
-                "\t(literal \"/tmp\")\n"
-                "\t(literal \"/etc\")\n"
-                "\t(literal \"/etc/nix\")\n"
-                "\t(literal \"/etc/nix/nix.conf\"))\n";
-
             /* 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);
@@ -2460,20 +2458,6 @@ void DerivationGoal::runChild()
             /* They don't like trailing slashes on subpath directives */
             if (globalTmpDir.back() == '/') globalTmpDir.pop_back();
 
-            /* This is where our temp folders are and where most of the building will happen, so we want rwx on it. */
-            sandboxProfile += (format("(allow file-read* file-write* process-exec (subpath \"%1%\") (subpath \"/private/tmp\"))\n") % globalTmpDir).str();
-
-            sandboxProfile += "(allow process-fork)\n";
-            sandboxProfile += "(allow sysctl-read)\n";
-            sandboxProfile += "(allow signal (target same-sandbox))\n";
-
-            /* Enables getpwuid (used by git and others) */
-            sandboxProfile += "(allow mach-lookup (global-name \"com.apple.system.notification_center\") (global-name \"com.apple.system.opendirectoryd.libinfo\"))\n";
-
-            /* Allow local networking operations, mostly because lots of test suites use it and it seems mostly harmless */
-            sandboxProfile += "(allow network* (local ip) (remote unix-socket))";
-
-
             /* Our rwx outputs */
             sandboxProfile += "(allow file-read* file-write* process-exec\n";
             for (auto & i : missingPaths) {
@@ -2487,7 +2471,7 @@ void DerivationGoal::runChild()
                denying it means the `access` syscall will return EPERM instead of EACCESS, which confuses a few programs that assume (understandably, since
                it appears to be a violation of the POSIX spec) that `access` won't do that, and don't deal with it nicely if it does. The most notable of
                these is the entire GHC Haskell ecosystem. */
-            sandboxProfile += "(allow file-read* file-write* process-exec\n";
+            sandboxProfile += "(allow file-read* file-write* process-exec mach-priv-task-port\n";
             for (auto & i : dirsInChroot) {
                 if (i.first != i.second)
                     throw SysError(format("can't map '%1%' to '%2%': mismatched impure paths not supported on darwin"));
@@ -2504,8 +2488,12 @@ void DerivationGoal::runChild()
             sandboxProfile += ")\n";
 
             /* Our ancestry. N.B: this uses literal on folders, instead of subpath. Without that,
-               you open up the entire filesystem because you end up with (subpath "/") */
-            sandboxProfile += "(allow file-read-metadata\n";
+               you open up the entire filesystem because you end up with (subpath "/")
+               Note: file-read-metadata* is not sufficiently permissive for GHC. file-read* is but may
+               be a security hazard.
+               TODO: figure out a more appropriate directive.
+             */
+            sandboxProfile += "(allow file-read*\n";
             for (auto & i : ancestry) {
                 sandboxProfile += (format("\t(literal \"%1%\")\n") % i.c_str()).str();
             }
@@ -2518,6 +2506,8 @@ void DerivationGoal::runChild()
             args.push_back("sandbox-exec");
             args.push_back("-p");
             args.push_back(sandboxProfile);
+            args.push_back("-D");
+            args.push_back((format("_GLOBAL_TMP_DIR=%1%") % globalTmpDir).str());
             args.push_back(drv->builder);
         } else {
             builder = drv->builder.c_str();
diff --git a/src/libstore/builtins.cc b/src/libstore/builtins.cc
index 25e2e7df30e7..2a4396308399 100644
--- a/src/libstore/builtins.cc
+++ b/src/libstore/builtins.cc
@@ -7,8 +7,16 @@ void builtinFetchurl(const BasicDerivation & drv)
 {
     auto url = drv.env.find("url");
     if (url == drv.env.end()) throw Error("attribute ‘url’ missing");
-    printMsg(lvlInfo, format("downloading ‘%1%’...") % url->second);
-    auto data = downloadFile(url->second); // FIXME: show progress
+
+    /* No need to do TLS verification, because we check the hash of
+       the result anyway. */
+    DownloadOptions options;
+    options.verifyTLS = false;
+
+    /* Show a progress indicator, even though stderr is not a tty. */
+    options.forceProgress = true;
+
+    auto data = downloadFile(url->second, options);
 
     auto out = drv.env.find("out");
     if (out == drv.env.end()) throw Error("attribute ‘url’ missing");
diff --git a/src/libstore/download.cc b/src/libstore/download.cc
index c6c2f611573f..94c13249d494 100644
--- a/src/libstore/download.cc
+++ b/src/libstore/download.cc
@@ -102,7 +102,6 @@ struct Curl
         if (!curl) throw Error("unable to initialize curl");
 
         curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
-        curl_easy_setopt(curl, CURLOPT_CAINFO, getEnv("SSL_CERT_FILE", "/etc/ssl/certs/ca-certificates.crt").c_str());
         curl_easy_setopt(curl, CURLOPT_USERAGENT, ("Nix/" + nixVersion).c_str());
         curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
 
@@ -115,8 +114,6 @@ struct Curl
         curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progressCallback_);
         curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, (void *) &curl);
         curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
-
-        showProgress = isatty(STDERR_FILENO);
     }
 
     ~Curl()
@@ -125,10 +122,19 @@ struct Curl
         if (requestHeaders) curl_slist_free_all(requestHeaders);
     }
 
-    bool fetch(const string & url, const string & expectedETag = "")
+    bool fetch(const string & url, const DownloadOptions & options)
     {
+        showProgress = options.forceProgress || isatty(STDERR_FILENO);
+
         curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
 
+        if (options.verifyTLS)
+            curl_easy_setopt(curl, CURLOPT_CAINFO, getEnv("SSL_CERT_FILE", "/etc/ssl/certs/ca-certificates.crt").c_str());
+        else {
+            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
+            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
+        }
+
         data.clear();
 
         if (requestHeaders) {
@@ -136,9 +142,9 @@ struct Curl
             requestHeaders = 0;
         }
 
-        if (!expectedETag.empty()) {
-            this->expectedETag = expectedETag;
-            requestHeaders = curl_slist_append(requestHeaders, ("If-None-Match: " + expectedETag).c_str());
+        if (!options.expectedETag.empty()) {
+            this->expectedETag = options.expectedETag;
+            requestHeaders = curl_slist_append(requestHeaders, ("If-None-Match: " + options.expectedETag).c_str());
         }
 
         curl_easy_setopt(curl, CURLOPT_HTTPHEADER, requestHeaders);
@@ -154,7 +160,7 @@ struct Curl
             //std::cerr << "\e[" << moveBack << "D\e[K\n";
             std::cerr << "\n";
         checkInterrupt();
-        if (res == CURLE_WRITE_ERROR && etag == expectedETag) return false;
+        if (res == CURLE_WRITE_ERROR && etag == options.expectedETag) return false;
         if (res != CURLE_OK)
             throw DownloadError(format("unable to download ‘%1%’: %2% (%3%)")
                 % url % curl_easy_strerror(res) % res);
@@ -168,11 +174,11 @@ struct Curl
 };
 
 
-DownloadResult downloadFile(string url, string expectedETag)
+DownloadResult downloadFile(string url, const DownloadOptions & options)
 {
     DownloadResult res;
     Curl curl;
-    if (curl.fetch(url, expectedETag)) {
+    if (curl.fetch(url, options)) {
         res.cached = false;
         res.data = curl.data;
     } else
@@ -224,7 +230,9 @@ Path downloadFileCached(const string & url, bool unpack)
     if (!skip) {
 
         try {
-            auto res = downloadFile(url, expectedETag);
+            DownloadOptions options;
+            options.expectedETag = expectedETag;
+            auto res = downloadFile(url, options);
 
             if (!res.cached)
                 storePath = store->addTextToStore(name, res.data, PathSet(), false);
diff --git a/src/libstore/download.hh b/src/libstore/download.hh
index 28c9117e4227..c1cb25b90c32 100644
--- a/src/libstore/download.hh
+++ b/src/libstore/download.hh
@@ -5,13 +5,20 @@
 
 namespace nix {
 
+struct DownloadOptions
+{
+    string expectedETag;
+    bool verifyTLS{true};
+    bool forceProgress{false};
+};
+
 struct DownloadResult
 {
     bool cached;
     string data, etag;
 };
 
-DownloadResult downloadFile(string url, string expectedETag = "");
+DownloadResult downloadFile(string url, const DownloadOptions & options);
 
 Path downloadFileCached(const string & url, bool unpack);
 
diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc
index 73f8489438fc..e704837e8798 100644
--- a/src/libstore/globals.cc
+++ b/src/libstore/globals.cc
@@ -77,6 +77,11 @@ void Settings::processEnvironment()
     nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR));
     nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR));
     nixDaemonSocketFile = canonPath(nixStateDir + DEFAULT_SOCKET_PATH);
+
+    // should be set with the other config options, but depends on nixLibexecDir
+#ifdef __APPLE__
+    preBuildHook = nixLibexecDir + "/nix/resolve-system-dependencies.pl";
+#endif
 }
 
 
diff --git a/src/libstore/local.mk b/src/libstore/local.mk
index bf5c256c949e..f10981ad444c 100644
--- a/src/libstore/local.mk
+++ b/src/libstore/local.mk
@@ -33,3 +33,4 @@ $(d)/local-store.cc: $(d)/schema.sql.hh
 clean-files += $(d)/schema.sql.hh
 
 $(eval $(call install-file-in, $(d)/nix-store.pc, $(prefix)/lib/pkgconfig, 0644))
+$(eval $(call install-file-in, $(d)/sandbox-defaults.sb, $(datadir)/nix, 0644))
diff --git a/src/libstore/sandbox-defaults.sb.in b/src/libstore/sandbox-defaults.sb.in
new file mode 100644
index 000000000000..12c39fa7f217
--- /dev/null
+++ b/src/libstore/sandbox-defaults.sb.in
@@ -0,0 +1,55 @@
+(allow file-read* file-write-data (literal "/dev/null"))
+(allow ipc-posix*)
+(allow mach-lookup (global-name "com.apple.SecurityServer"))
+
+(allow file-read*
+       (literal "/dev/dtracehelper")
+       (literal "/dev/tty")
+       (literal "/dev/autofs_nowait")
+       (literal "/System/Library/CoreServices/SystemVersion.plist")
+       (literal "/private/var/run/systemkeychaincheck.done")
+       (literal "/private/etc/protocols")
+       (literal "/private/var/tmp")
+       (literal "/private/var/db")
+       (subpath "/private/var/db/mds"))
+
+(allow file-write*
+       (literal "/dev/tty")
+       (literal "/dev/dtracehelper")
+       (literal "/mds"))
+
+(allow file-ioctl (literal "/dev/dtracehelper"))
+
+(allow file-read-metadata
+       (literal "/var")
+       (literal "/tmp")
+       ; symlinks
+       (literal "@sysconfdir@")
+       (literal "@sysconfdir@/nix")
+       (literal "@sysconfdir@/nix/nix.conf")
+       (literal "/etc/resolv.conf")
+       (literal "/private/etc/resolv.conf"))
+
+(allow file-read*
+       (literal "/private@sysconfdir@/nix/nix.conf")
+       (literal "/private/var/run/resolv.conf"))
+
+; some builders use filehandles other than stdin/stdout
+(allow file* (subpath "/dev/fd"))
+
+; allow everything inside TMP
+(allow file* process-exec
+       (subpath (param "_GLOBAL_TMP_DIR"))
+       (subpath "/private/tmp"))
+
+(allow process-fork)
+(allow sysctl-read)
+(allow signal (target same-sandbox))
+
+; allow getpwuid (for git and other packages)
+(allow mach-lookup
+       (global-name "com.apple.system.notification_center")
+       (global-name "com.apple.system.opendirectoryd.libinfo"))
+
+; allow local networking
+(allow network* (local ip) (remote unix-socket))