summary refs log tree commit diff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2015-05-11 17:04:26 +0200
committerLudovic Courtès <ludo@gnu.org>2015-05-11 17:04:26 +0200
commit766481d606e4b1860307692d6a44723983662d45 (patch)
tree7e6d112047bb03f7761ca8a41c02ac2a84b73c84
parentc69944c511b89d3fdbffe00353e27d1e1c5f670c (diff)
parentfdee1ced43fb495d612a29e955141cdf6b9a95ba (diff)
downloadguix-766481d606e4b1860307692d6a44723983662d45.tar.gz
Merge commit 'fdee1ced43fb495d612a29e955141cdf6b9a95ba' into nix
-rw-r--r--nix/libstore/build.cc10
-rw-r--r--nix/libstore/globals.cc6
-rw-r--r--nix/libstore/globals.hh9
-rw-r--r--nix/libstore/local-store.cc4
-rw-r--r--nix/libstore/remote-store.cc2
-rw-r--r--nix/libstore/remote-store.hh1
-rw-r--r--nix/libutil/archive.cc172
-rw-r--r--nix/libutil/archive.hh12
-rw-r--r--nix/libutil/util.cc13
-rw-r--r--nix/nix-daemon/nix-daemon.cc58
10 files changed, 184 insertions, 103 deletions
diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc
index d3184507ce..4376a8322c 100644
--- a/nix/libstore/build.cc
+++ b/nix/libstore/build.cc
@@ -590,7 +590,9 @@ HookInstance::HookInstance()
 {
     debug("starting build hook");
 
-    Path buildHook = absPath(getEnv("NIX_BUILD_HOOK"));
+    Path buildHook = getEnv("NIX_BUILD_HOOK");
+    if (string(buildHook, 0, 1) != "/") buildHook = settings.nixLibexecDir + "/nix/" + buildHook;
+    buildHook = canonPath(buildHook);
 
     /* Create a pipe to get the output of the child. */
     fromHook.create();
@@ -2075,9 +2077,9 @@ void DerivationGoal::initChild()
                     throw SysError("mounting /dev/pts");
                 createSymlink("/dev/pts/ptmx", chrootRootDir + "/dev/ptmx");
 
-		/* Make sure /dev/pts/ptmx is world-writable.  With some
-		   Linux versions, it is created with permissions 0.  */
-		chmod_(chrootRootDir + "/dev/pts/ptmx", 0666);
+                /* Make sure /dev/pts/ptmx is world-writable.  With some
+                   Linux versions, it is created with permissions 0.  */
+                chmod_(chrootRootDir + "/dev/pts/ptmx", 0666);
             }
 
             /* Do the chroot().  Below we do a chdir() to the
diff --git a/nix/libstore/globals.cc b/nix/libstore/globals.cc
index 62c2fd6290..21fbfba68b 100644
--- a/nix/libstore/globals.cc
+++ b/nix/libstore/globals.cc
@@ -2,6 +2,7 @@
 
 #include "globals.hh"
 #include "util.hh"
+#include "archive.hh"
 
 #include <map>
 #include <algorithm>
@@ -56,6 +57,8 @@ Settings::Settings()
     lockCPU = getEnv("NIX_AFFINITY_HACK", "1") == "1";
     showTrace = false;
     enableImportNative = false;
+    trustedUsers = Strings({"root"});
+    allowedUsers = Strings({"*"});
 }
 
 
@@ -144,6 +147,9 @@ void Settings::update()
     get(useSshSubstituter, "use-ssh-substituter");
     get(logServers, "log-servers");
     get(enableImportNative, "allow-unsafe-native-code-during-evaluation");
+    get(useCaseHack, "use-case-hack");
+    get(trustedUsers, "trusted-users");
+    get(allowedUsers, "allowed-users");
 
     string subs = getEnv("NIX_SUBSTITUTERS", "default");
     if (subs == "default") {
diff --git a/nix/libstore/globals.hh b/nix/libstore/globals.hh
index 8dd59a9c79..f1748336fd 100644
--- a/nix/libstore/globals.hh
+++ b/nix/libstore/globals.hh
@@ -203,6 +203,15 @@ struct Settings {
     /* Whether the importNative primop should be enabled */
     bool enableImportNative;
 
+    /* List of users that have elevated rights in the Nix daemon, such
+       as the ability to specify additional binary caches, or to
+       import unsigned NARs. */
+    Strings trustedUsers;
+
+    /* List of users that are allowed to connect to the daemon, in
+       addition to the trusted users. These have normal rights. */
+    Strings allowedUsers;
+
 private:
     SettingsMap settings, overrides;
 
diff --git a/nix/libstore/local-store.cc b/nix/libstore/local-store.cc
index e66042c57f..7d78ab7ecf 100644
--- a/nix/libstore/local-store.cc
+++ b/nix/libstore/local-store.cc
@@ -1486,6 +1486,8 @@ void LocalStore::exportPath(const Path & path, bool sign,
 {
     assertStorePath(path);
 
+    printMsg(lvlInfo, format("exporting path `%1%'") % path);
+
     addTempRoot(path);
     if (!isValidPath(path))
         throw Error(format("path `%1%' is not valid") % path);
@@ -1596,8 +1598,6 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
 
     Path dstPath = readStorePath(hashAndReadSource);
 
-    printMsg(lvlInfo, format("importing path `%1%'") % dstPath);
-
     PathSet references = readStorePaths<PathSet>(hashAndReadSource);
 
     Path deriver = readString(hashAndReadSource);
diff --git a/nix/libstore/remote-store.cc b/nix/libstore/remote-store.cc
index 3b021bb2a5..b3967bb241 100644
--- a/nix/libstore/remote-store.cc
+++ b/nix/libstore/remote-store.cc
@@ -133,8 +133,6 @@ RemoteStore::~RemoteStore()
     try {
         to.flush();
         fdSocket.close();
-        if (child != -1)
-            child.wait(true);
     } catch (...) {
         ignoreException();
     }
diff --git a/nix/libstore/remote-store.hh b/nix/libstore/remote-store.hh
index 04b60fce4b..b010147643 100644
--- a/nix/libstore/remote-store.hh
+++ b/nix/libstore/remote-store.hh
@@ -87,7 +87,6 @@ private:
     AutoCloseFD fdSocket;
     FdSink to;
     FdSource from;
-    Pid child;
     unsigned int daemonVersion;
     bool initialised;
 
diff --git a/nix/libutil/archive.cc b/nix/libutil/archive.cc
index 70a1c580dd..5450fd2f71 100644
--- a/nix/libutil/archive.cc
+++ b/nix/libutil/archive.cc
@@ -1,10 +1,14 @@
+#define _XOPEN_SOURCE 600
+
 #include "config.h"
 
 #include <cerrno>
 #include <algorithm>
 #include <vector>
+#include <map>
+
+#include <strings.h> // for strcasecmp
 
-#define _XOPEN_SOURCE 600
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -18,39 +22,21 @@
 namespace nix {
 
 
+bool useCaseHack =
+#if __APPLE__
+    true;
+#else
+    false;
+#endif
+
 static string archiveVersion1 = "nix-archive-1";
 
+static string caseHackSuffix = "~nix~case~hack~";
 
 PathFilter defaultPathFilter;
 
 
-static void dump(const string & path, Sink & sink, PathFilter & filter);
-
-
-static void dumpEntries(const Path & path, Sink & sink, PathFilter & filter)
-{
-    Strings names = readDirectory(path);
-    vector<string> names2(names.begin(), names.end());
-    sort(names2.begin(), names2.end());
-
-    for (vector<string>::iterator i = names2.begin();
-         i != names2.end(); ++i)
-    {
-        Path entry = path + "/" + *i;
-        if (filter(entry)) {
-            writeString("entry", sink);
-            writeString("(", sink);
-            writeString("name", sink);
-            writeString(*i, sink);
-            writeString("node", sink);
-            dump(entry, sink, filter);
-            writeString(")", sink);
-        }
-    }
-}
-
-
-static void dumpContents(const Path & path, size_t size, 
+static void dumpContents(const Path & path, size_t size,
     Sink & sink)
 {
     writeString("contents", sink);
@@ -58,7 +44,7 @@ static void dumpContents(const Path & path, size_t size,
 
     AutoCloseFD fd = open(path.c_str(), O_RDONLY);
     if (fd == -1) throw SysError(format("opening file `%1%'") % path);
-    
+
     unsigned char buf[65536];
     size_t left = size;
 
@@ -89,12 +75,41 @@ static void dump(const Path & path, Sink & sink, PathFilter & filter)
             writeString("", sink);
         }
         dumpContents(path, (size_t) st.st_size, sink);
-    } 
+    }
 
     else if (S_ISDIR(st.st_mode)) {
         writeString("type", sink);
         writeString("directory", sink);
-        dumpEntries(path, sink, filter);
+
+        /* If we're on a case-insensitive system like Mac OS X, undo
+           the case hack applied by restorePath(). */
+        Strings names = readDirectory(path);
+        std::map<string, string> unhacked;
+        for (auto & i : names)
+            if (useCaseHack) {
+                string name(i);
+                size_t pos = i.find(caseHackSuffix);
+                if (pos != string::npos) {
+                    printMsg(lvlDebug, format("removing case hack suffix from `%1%'") % (path + "/" + i));
+                    name.erase(pos);
+                }
+                if (unhacked.find(name) != unhacked.end())
+                    throw Error(format("file name collision in between `%1%' and `%2%'")
+                        % (path + "/" + unhacked[name]) % (path + "/" + i));
+                unhacked[name] = i;
+            } else
+                unhacked[i] = i;
+
+        for (auto & i : unhacked)
+            if (filter(path + "/" + i.first)) {
+                writeString("entry", sink);
+                writeString("(", sink);
+                writeString("name", sink);
+                writeString(i.first, sink);
+                writeString("node", sink);
+                dump(path + "/" + i.second, sink, filter);
+                writeString(")", sink);
+            }
     }
 
     else if (S_ISLNK(st.st_mode)) {
@@ -123,6 +138,7 @@ static SerialisationError badArchive(string s)
 }
 
 
+#if 0
 static void skipGeneric(Source & source)
 {
     if (readString(source) == "(") {
@@ -130,43 +146,13 @@ static void skipGeneric(Source & source)
             skipGeneric(source);
     }
 }
-
-
-static void parse(ParseSink & sink, Source & source, const Path & path);
-
-
-
-static void parseEntry(ParseSink & sink, Source & source, const Path & path)
-{
-    string s, name;
-
-    s = readString(source);
-    if (s != "(") throw badArchive("expected open tag");
-
-    while (1) {
-        checkInterrupt();
-
-        s = readString(source);
-
-        if (s == ")") {
-            break;
-        } else if (s == "name") {
-            name = readString(source);
-        } else if (s == "node") {
-            if (s == "") throw badArchive("entry name missing");
-            parse(sink, source, path + "/" + name);
-        } else {
-            throw badArchive("unknown field " + s);
-            skipGeneric(source);
-        }
-    }
-}
+#endif
 
 
 static void parseContents(ParseSink & sink, Source & source, const Path & path)
 {
     unsigned long long size = readLongLong(source);
-    
+
     sink.preallocateContents(size);
 
     unsigned long long left = size;
@@ -185,6 +171,15 @@ static void parseContents(ParseSink & sink, Source & source, const Path & path)
 }
 
 
+struct CaseInsensitiveCompare
+{
+    bool operator() (const string & a, const string & b) const
+    {
+        return strcasecmp(a.c_str(), b.c_str()) < 0;
+    }
+};
+
+
 static void parse(ParseSink & sink, Source & source, const Path & path)
 {
     string s;
@@ -194,6 +189,8 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
 
     enum { tpUnknown, tpRegular, tpDirectory, tpSymlink } type = tpUnknown;
 
+    std::map<Path, int, CaseInsensitiveCompare> names;
+
     while (1) {
         checkInterrupt();
 
@@ -221,9 +218,9 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
             else if (t == "symlink") {
                 type = tpSymlink;
             }
-            
+
             else throw badArchive("unknown file type " + t);
-            
+
         }
 
         else if (s == "contents" && type == tpRegular) {
@@ -236,7 +233,40 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
         }
 
         else if (s == "entry" && type == tpDirectory) {
-            parseEntry(sink, source, path);
+            string name, prevName;
+
+            s = readString(source);
+            if (s != "(") throw badArchive("expected open tag");
+
+            while (1) {
+                checkInterrupt();
+
+                s = readString(source);
+
+                if (s == ")") {
+                    break;
+                } else if (s == "name") {
+                    name = readString(source);
+                    if (name.empty() || name == "." || name == ".." || name.find('/') != string::npos || name.find((char) 0) != string::npos)
+                        throw Error(format("NAR contains invalid file name `%1%'") % name);
+                    if (name <= prevName)
+                        throw Error("NAR directory is not sorted");
+                    prevName = name;
+                    if (useCaseHack) {
+                        auto i = names.find(name);
+                        if (i != names.end()) {
+                            printMsg(lvlDebug, format("case collision between `%1%' and `%2%'") % i->first % name);
+                            name += caseHackSuffix;
+                            name += int2String(++i->second);
+                        } else
+                            names[name] = 0;
+                    }
+                } else if (s == "node") {
+                    if (s.empty()) throw badArchive("entry name missing");
+                    parse(sink, source, path + "/" + name);
+                } else
+                    throw badArchive("unknown field " + s);
+            }
         }
 
         else if (s == "target" && type == tpSymlink) {
@@ -244,17 +274,15 @@ static void parse(ParseSink & sink, Source & source, const Path & path)
             sink.createSymlink(path, target);
         }
 
-        else {
+        else
             throw badArchive("unknown field " + s);
-            skipGeneric(source);
-        }
     }
 }
 
 
 void parseDump(ParseSink & sink, Source & source)
 {
-    string version;    
+    string version;
     try {
         version = readString(source);
     } catch (SerialisationError & e) {
@@ -323,7 +351,7 @@ struct RestoreSink : ParseSink
     }
 };
 
- 
+
 void restorePath(const Path & path, Source & source)
 {
     RestoreSink sink;
@@ -331,5 +359,5 @@ void restorePath(const Path & path, Source & source)
     parseDump(sink, source);
 }
 
- 
+
 }
diff --git a/nix/libutil/archive.hh b/nix/libutil/archive.hh
index ccac92074d..c216e9768f 100644
--- a/nix/libutil/archive.hh
+++ b/nix/libutil/archive.hh
@@ -28,7 +28,7 @@ namespace nix {
 
    where:
 
-     attrs(as) = concat(map(attr, as)) + encN(0) 
+     attrs(as) = concat(map(attr, as)) + encN(0)
      attrs((a, b)) = encS(a) + encS(b)
 
      encS(s) = encN(len(s)) + s + (padding until next 64-bit boundary)
@@ -58,7 +58,7 @@ void dumpPath(const Path & path, Sink & sink,
 struct ParseSink
 {
     virtual void createDirectory(const Path & path) { };
-    
+
     virtual void createRegularFile(const Path & path) { };
     virtual void isExecutable() { };
     virtual void preallocateContents(unsigned long long size) { };
@@ -66,10 +66,14 @@ struct ParseSink
 
     virtual void createSymlink(const Path & path, const string & target) { };
 };
-    
+
 void parseDump(ParseSink & sink, Source & source);
 
 void restorePath(const Path & path, Source & source);
 
- 
+
+// FIXME: global variables are bad m'kay.
+extern bool useCaseHack;
+
+
 }
diff --git a/nix/libutil/util.cc b/nix/libutil/util.cc
index faa2b83c37..32244b2185 100644
--- a/nix/libutil/util.cc
+++ b/nix/libutil/util.cc
@@ -708,17 +708,14 @@ void AutoCloseDir::close()
 
 
 Pid::Pid()
+    : pid(-1), separatePG(false), killSignal(SIGKILL)
 {
-    pid = -1;
-    separatePG = false;
-    killSignal = SIGKILL;
 }
 
 
 Pid::Pid(pid_t pid)
+    : pid(pid), separatePG(false), killSignal(SIGKILL)
 {
-    Pid();
-    *this = pid;
 }
 
 
@@ -857,8 +854,10 @@ pid_t startProcess(std::function<void()> fun, const string & errorPrefix)
             restoreAffinity();
             fun();
         } catch (std::exception & e) {
-            writeToStderr(errorPrefix + string(e.what()) + "\n");
-        }
+            try {
+                std::cerr << errorPrefix << e.what() << "\n";
+            } catch (...) { }
+        } catch (...) { }
         _exit(1);
     }
 
diff --git a/nix/nix-daemon/nix-daemon.cc b/nix/nix-daemon/nix-daemon.cc
index 265131c613..f2141ee536 100644
--- a/nix/nix-daemon/nix-daemon.cc
+++ b/nix/nix-daemon/nix-daemon.cc
@@ -7,6 +7,8 @@
 #include "affinity.hh"
 #include "globals.hh"
 
+#include <algorithm>
+
 #include <cstring>
 #include <unistd.h>
 #include <signal.h>
@@ -17,6 +19,8 @@
 #include <sys/un.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <pwd.h>
+#include <grp.h>
 
 using namespace nix;
 
@@ -450,7 +454,7 @@ static void performOp(bool trusted, unsigned int clientVersion,
     case wopImportPaths: {
         startWork();
         TunnelSource source(from);
-        Paths paths = store->importPaths(true, source);
+        Paths paths = store->importPaths(!trusted, source);
         stopWork();
         writeStrings(paths, to);
         break;
@@ -769,6 +773,27 @@ static void setSigChldAction(bool autoReap)
 }
 
 
+bool matchUser(const string & user, const string & group, const Strings & users)
+{
+    if (find(users.begin(), users.end(), "*") != users.end())
+        return true;
+
+    if (find(users.begin(), users.end(), user) != users.end())
+        return true;
+
+    for (auto & i : users)
+        if (string(i, 0, 1) == "@") {
+            if (group == string(i, 1)) return true;
+            struct group * gr = getgrnam(i.c_str() + 1);
+            if (!gr) continue;
+            for (char * * mem = gr->gr_mem; *mem; mem++)
+                if (user == string(*mem)) return true;
+        }
+
+    return false;
+}
+
+
 #define SD_LISTEN_FDS_START 3
 
 
@@ -854,22 +879,33 @@ static void daemonLoop()
 
             closeOnExec(remote);
 
-            /* Get the identity of the caller, if possible. */
-            uid_t clientUid = -1;
-            pid_t clientPid = -1;
             bool trusted = false;
+            pid_t clientPid = -1;
 
 #if defined(SO_PEERCRED)
+            /* Get the identity of the caller, if possible. */
             ucred cred;
             socklen_t credLen = sizeof(cred);
-            if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) != -1) {
-                clientPid = cred.pid;
-                clientUid = cred.uid;
-                if (clientUid == 0) trusted = true;
-            }
-#endif
+            if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) == -1)
+                throw SysError("getting peer credentials");
+
+            clientPid = cred.pid;
+
+            struct passwd * pw = getpwuid(cred.uid);
+            string user = pw ? pw->pw_name : int2String(cred.uid);
 
-            printMsg(lvlInfo, format("accepted connection from pid %1%, uid %2%") % clientPid % clientUid);
+            struct group * gr = getgrgid(cred.gid);
+            string group = gr ? gr->gr_name : int2String(cred.gid);
+
+            if (matchUser(user, group, settings.trustedUsers))
+                trusted = true;
+
+            if (!trusted && !matchUser(user, group, settings.allowedUsers))
+                throw Error(format("user `%1%' is not allowed to connect to the Nix daemon") % user);
+
+            printMsg(lvlInfo, format((string) "accepted connection from pid %1%, user %2%"
+                    + (trusted ? " (trusted)" : "")) % clientPid % user);
+#endif
 
             /* Fork a child to handle the connection. */
             startProcess([&]() {