diff options
Diffstat (limited to 'nix/nix-daemon/nix-daemon.cc')
-rw-r--r-- | nix/nix-daemon/nix-daemon.cc | 159 |
1 files changed, 99 insertions, 60 deletions
diff --git a/nix/nix-daemon/nix-daemon.cc b/nix/nix-daemon/nix-daemon.cc index 8814fe3155..10159db62e 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; @@ -44,7 +48,6 @@ static FdSource from(STDIN_FILENO); static FdSink to(STDOUT_FILENO); bool canSendStderr; -pid_t myPid; @@ -54,11 +57,7 @@ pid_t myPid; socket. */ static void tunnelStderr(const unsigned char * buf, size_t count) { - /* Don't send the message to the client if we're a child of the - process handling the connection. Otherwise we could screw up - the protocol. It's up to the parent to redirect stderr and - send it to the client somehow (e.g., as in build.cc). */ - if (canSendStderr && myPid == getpid()) { + if (canSendStderr) { try { writeInt(STDERR_NEXT, to); writeString(buf, count, to); @@ -284,15 +283,6 @@ static void performOp(bool trusted, unsigned int clientVersion, { switch (op) { -#if 0 - case wopQuit: { - /* Close the database. */ - store.reset((StoreAPI *) 0); - writeInt(1, to); - break; - } -#endif - case wopIsValidPath: { /* 'readStorePath' could raise an error leading to the connection being closed. To be able to recover from an invalid path error, @@ -450,6 +440,9 @@ static void performOp(bool trusted, unsigned int clientVersion, case wopImportPaths: { startWork(); TunnelSource source(from); + + /* Unlike Nix, always require a signature, even for "trusted" + users. */ Paths paths = store->importPaths(true, source); stopWork(); writeStrings(paths, to); @@ -650,6 +643,25 @@ static void performOp(bool trusted, unsigned int clientVersion, break; } + case wopOptimiseStore: + startWork(); + store->optimiseStore(); + stopWork(); + writeInt(1, to); + break; + + case wopVerifyStore: { + bool checkContents = readInt(from) != 0; + bool repair = readInt(from) != 0; + startWork(); + if (repair && !trusted) + throw Error("you are not privileged to repair paths"); + bool errors = store->verifyStore(checkContents, repair); + stopWork(); + writeInt(errors, to); + break; + } + default: throw Error(format("invalid operation %1%") % op); } @@ -659,7 +671,6 @@ static void performOp(bool trusted, unsigned int clientVersion, static void processConnection(bool trusted) { canSendStderr = false; - myPid = getpid(); _writeToStderr = tunnelStderr; #ifdef HAVE_HUP_NOTIFICATION @@ -708,7 +719,7 @@ static void processConnection(bool trusted) to.flush(); } catch (Error & e) { - stopWork(false, e.msg()); + stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0); to.flush(); return; } @@ -735,12 +746,10 @@ static void processConnection(bool trusted) during addTextToStore() / importPath(). If that happens, just send the error message and exit. */ bool errorAllowed = canSendStderr; - if (!errorAllowed) printMsg(lvlError, format("error processing client input: %1%") % e.msg()); stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0); - if (!errorAllowed) break; + if (!errorAllowed) throw; } catch (std::bad_alloc & e) { - if (canSendStderr) - stopWork(false, "Nix daemon out of memory", GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0); + stopWork(false, "Nix daemon out of memory", GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0); throw; } @@ -749,7 +758,9 @@ static void processConnection(bool trusted) assert(!canSendStderr); }; - printMsg(lvlError, format("%1% operations") % opCount); + canSendStderr = false; + _isInterrupted = false; + printMsg(lvlDebug, format("%1% operations") % opCount); } @@ -771,11 +782,35 @@ 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 static void daemonLoop() { + if (chdir("/") == -1) + throw SysError("cannot change current directory"); + /* Get rid of children automatically; don't let them become zombies. */ setSigChldAction(true); @@ -804,7 +839,8 @@ static void daemonLoop() /* Urgh, sockaddr_un allows path names of only 108 characters. So chdir to the socket directory so that we can pass a relative path name. */ - chdir(dirOf(socketPath).c_str()); + if (chdir(dirOf(socketPath).c_str()) == -1) + throw SysError("cannot change current directory"); Path socketPathRel = "./" + baseNameOf(socketPath); struct sockaddr_un addr; @@ -824,7 +860,8 @@ static void daemonLoop() if (res == -1) throw SysError(format("cannot bind to socket `%1%'") % socketPath); - chdir("/"); /* back to the root */ + if (chdir("/") == -1) /* back to the root */ + throw SysError("cannot change current directory"); if (listen(fdSocket, 5) == -1) throw SysError(format("cannot listen on socket `%1%'") % socketPath); @@ -856,58 +893,61 @@ 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"); - printMsg(lvlInfo, format("accepted connection from pid %1%, uid %2%") % clientPid % clientUid); + clientPid = cred.pid; - /* Fork a child to handle the connection. */ - pid_t child; - child = fork(); + struct passwd * pw = getpwuid(cred.uid); + string user = pw ? pw->pw_name : int2String(cred.uid); - switch (child) { + struct group * gr = getgrgid(cred.gid); + string group = gr ? gr->gr_name : int2String(cred.gid); - case -1: - throw SysError("unable to fork"); + Strings trustedUsers = settings.get("trusted-users", Strings({"root"})); + Strings allowedUsers = settings.get("allowed-users", Strings({"*"})); - case 0: - try { /* child */ + if (matchUser(user, group, trustedUsers)) + trusted = true; - /* Background the daemon. */ - if (setsid() == -1) - throw SysError(format("creating a new session")); + if (!trusted && !matchUser(user, group, allowedUsers)) + throw Error(format("user `%1%' is not allowed to connect to the Nix daemon") % user); - /* Restore normal handling of SIGCHLD. */ - setSigChldAction(false); + printMsg(lvlInfo, format((string) "accepted connection from pid %1%, user %2%" + + (trusted ? " (trusted)" : "")) % clientPid % user); +#endif - /* For debugging, stuff the pid into argv[1]. */ - if (clientPid != -1 && argvSaved[1]) { - string processName = int2String(clientPid); - strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1])); - } + /* Fork a child to handle the connection. */ + startProcess([&]() { + fdSocket.close(); - /* Handle the connection. */ - from.fd = remote; - to.fd = remote; - processConnection(trusted); + /* Background the daemon. */ + if (setsid() == -1) + throw SysError(format("creating a new session")); - } catch (std::exception & e) { - writeToStderr("unexpected Nix daemon error: " + string(e.what()) + "\n"); + /* Restore normal handling of SIGCHLD. */ + setSigChldAction(false); + + /* For debugging, stuff the pid into argv[1]. */ + if (clientPid != -1 && argvSaved[1]) { + string processName = int2String(clientPid); + strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1])); } + + /* Handle the connection. */ + from.fd = remote; + to.fd = remote; + processConnection(trusted); + exit(0); - } + }, false, "unexpected Nix daemon error: ", true); } catch (Interrupted & e) { throw; @@ -925,7 +965,6 @@ void run(Strings args) if (arg == "--daemon") /* ignored for backwards compatibility */; } - chdir("/"); daemonLoop(); } |