From 2608e40988ba8cf51723fe0d21bdedf6b3997c9c Mon Sep 17 00:00:00 2001 From: Hartmut Goebel Date: Mon, 21 Nov 2016 19:49:12 +0100 Subject: [PATCH] daemon: Set ownership of kept build directories to the calling user. Fixes . * nix/libstore/globals.hh (Settings) Add clientUid and clientGid. * nix/nix-daemon/nix-daemon.cc (daemonLoop] Store UID and GID of the caller in settings. * nix/libstore/build.cc (_chown): New function. (DerivationGoal::deleteTmpDir): Use it, change ownership of build directory if it is kept and the new owner is not root. --- nix/libstore/build.cc | 21 +++++++++++++++++++++ nix/libstore/globals.hh | 6 ++++++ nix/nix-daemon/nix-daemon.cc | 12 ++++++++++++ 3 files changed, 39 insertions(+) diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc index 889ee3d2bd..e823001788 100644 --- a/nix/libstore/build.cc +++ b/nix/libstore/build.cc @@ -2631,6 +2631,21 @@ void DerivationGoal::closeLogFile() } +static void _chown(const Path & path, uid_t uid, gid_t gid) +{ + checkInterrupt(); + + if (lchown(path.c_str(), uid, gid) == -1) { + throw SysError(format("change owner and group of `%1%'") % path); + } + struct stat st = lstat(path); + if (S_ISDIR(st.st_mode)) { + for (auto & i : readDirectory(path)) + _chown(path + "/" + i.name, uid, gid); + } +} + + void DerivationGoal::deleteTmpDir(bool force) { if (tmpDir != "") { @@ -2639,6 +2654,12 @@ void DerivationGoal::deleteTmpDir(bool force) format("note: keeping build directory `%2%'") % drvPath % tmpDir); chmod(tmpDir.c_str(), 0755); + // Change the ownership if clientUid is set. Never change the + // ownership or the group to "root" for security reasons. + if (settings.clientUid != (uid_t) -1 && settings.clientUid != 0) { + _chown(tmpDir, settings.clientUid, + settings.clientGid != 0 ? settings.clientGid : -1); + } } else deletePath(tmpDir); diff --git a/nix/libstore/globals.hh b/nix/libstore/globals.hh index 8c07e360f2..7beb1a55ca 100644 --- a/nix/libstore/globals.hh +++ b/nix/libstore/globals.hh @@ -70,6 +70,12 @@ struct Settings { subgoal of the same goal) fails. */ bool keepGoing; + /* User and groud id of the client issuing the build request. Used to set + the owner and group of the kept temporary directories of failed + builds. */ + uid_t clientUid; + gid_t clientGid; + /* Whether, if we cannot realise the known closure corresponding to a derivation, we should try to normalise the derivation instead. */ diff --git a/nix/nix-daemon/nix-daemon.cc b/nix/nix-daemon/nix-daemon.cc index 682f9a2b5b..47b67d5863 100644 --- a/nix/nix-daemon/nix-daemon.cc +++ b/nix/nix-daemon/nix-daemon.cc @@ -960,6 +960,18 @@ static void daemonLoop() strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1])); } +#if defined(SO_PEERCRED) + /* Store the client's user and group for this connection. This + has to be done in the forked process since it is per + connection. */ + settings.clientUid = cred.uid; + settings.clientGid = cred.gid; +#else + /* Setting these to -1 means: do not change */ + settings.clientUid = (uid_t) -1; + settings.clientGid = (gid_t) -1; +#endif + /* Handle the connection. */ from.fd = remote; to.fd = remote;