diff options
author | Eelco Dolstra <eelco.dolstra@logicblox.com> | 2013-11-14T10·57+0100 |
---|---|---|
committer | Eelco Dolstra <eelco.dolstra@logicblox.com> | 2013-11-14T10·57+0100 |
commit | a478e8a7bb8c24da0ac91b7100bd0e422035c62f (patch) | |
tree | 238363db5630470775389033e88559bce83cb66c /src | |
parent | 89e6781cc5885cbf6284a51c0403dded62ce8bc0 (diff) |
Remove nix-setuid-helper
AFAIK, nobody uses it, it's not maintained, and it has no tests.
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/libstore/build.cc | 167 | ||||
-rw-r--r-- | src/libstore/gc.cc | 2 | ||||
-rw-r--r-- | src/libstore/local-store.cc | 6 | ||||
-rw-r--r-- | src/libstore/local-store.hh | 14 | ||||
-rw-r--r-- | src/libutil/util.cc | 13 | ||||
-rw-r--r-- | src/libutil/util.hh | 4 | ||||
-rw-r--r-- | src/nix-setuid-helper/Makefile.am | 7 | ||||
-rw-r--r-- | src/nix-setuid-helper/nix-setuid-helper.cc | 263 |
9 files changed, 32 insertions, 446 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 0ae407c573d1..a5e411b9ed6e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,3 +1,3 @@ SUBDIRS = boost libutil libstore libmain nix-store nix-hash \ - libexpr nix-instantiate nix-env nix-daemon nix-setuid-helper \ + libexpr nix-instantiate nix-env nix-daemon \ nix-log2xml bsdiff-4.3 diff --git a/src/libstore/build.cc b/src/libstore/build.cc index 51314f7368f0..63e34d256057 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -549,93 +549,10 @@ void UserLock::release() } -static void runSetuidHelper(const string & command, - const string & arg) -{ - Path program = getEnv("NIX_SETUID_HELPER", - settings.nixLibexecDir + "/nix-setuid-helper"); - - /* Fork. */ - Pid pid; - pid = fork(); - switch (pid) { - - case -1: - throw SysError("unable to fork"); - - case 0: /* child */ - try { - std::vector<const char *> args; /* careful with c_str()! */ - args.push_back(program.c_str()); - args.push_back(command.c_str()); - args.push_back(arg.c_str()); - args.push_back(0); - - restoreSIGPIPE(); - restoreAffinity(); - - execve(program.c_str(), (char * *) &args[0], 0); - throw SysError(format("executing `%1%'") % program); - } - catch (std::exception & e) { - writeToStderr("error: " + string(e.what()) + "\n"); - } - _exit(1); - } - - /* Parent. */ - - /* Wait for the child to finish. */ - int status = pid.wait(true); - if (!statusOk(status)) - throw Error(format("program `%1%' %2%") - % program % statusToString(status)); -} - - void UserLock::kill() { assert(enabled()); - if (amPrivileged()) - killUser(uid); - else - runSetuidHelper("kill", user); -} - - -bool amPrivileged() -{ - return geteuid() == 0; -} - - -void getOwnership(const Path & path) -{ - runSetuidHelper("get-ownership", path); -} - - -void deletePathWrapped(const Path & path, unsigned long long & bytesFreed) -{ - try { - /* First try to delete it ourselves. */ - deletePath(path, bytesFreed); - } catch (SysError & e) { - /* If this failed due to a permission error, then try it with - the setuid helper. */ - if (settings.buildUsersGroup != "" && !amPrivileged()) { - getOwnership(path); - deletePath(path, bytesFreed); - } else - throw; - } -} - - -void deletePathWrapped(const Path & path) -{ - unsigned long long dummy1; - deletePathWrapped(path, dummy1); + killUser(uid); } @@ -971,15 +888,11 @@ void DerivationGoal::killChild() worker.childTerminated(pid); if (buildUser.enabled()) { - /* We can't use pid.kill(), since we may not have the - appropriate privilege. I.e., if we're not root, then - setuid helper should do it). - - Also, if we're using a build user, then there is a - tricky race condition: if we kill the build user before - the child has done its setuid() to the build user uid, - then it won't be killed, and we'll potentially lock up - in pid.wait(). So also send a conventional kill to the + /* If we're using a build user, then there is a tricky + race condition: if we kill the build user before the + child has done its setuid() to the build user uid, then + it won't be killed, and we'll potentially lock up in + pid.wait(). So also send a conventional kill to the child. */ ::kill(-pid, SIGKILL); /* ignore the result */ buildUser.kill(); @@ -1349,7 +1262,7 @@ void DerivationGoal::tryToBuild() if (worker.store.isValidPath(path)) continue; if (!pathExists(path)) continue; debug(format("removing unregistered path `%1%'") % path); - deletePathWrapped(path); + deletePath(path); } /* Check again whether any output previously failed to build, @@ -1427,7 +1340,7 @@ void replaceValidPath(const Path & storePath, const Path tmpPath) if (rename(tmpPath.c_str(), storePath.c_str()) == -1) throw SysError(format("moving `%1%' to `%2%'") % tmpPath % storePath); if (pathExists(oldPath)) - deletePathWrapped(oldPath); + deletePath(oldPath); } @@ -1532,13 +1445,6 @@ void DerivationGoal::buildDone() rewrittenPaths.insert(path); } - - /* Gain ownership of the build result using the setuid - wrapper if we're not root. If we *are* root, then - canonicalisePathMetaData() will take care of this later - on. */ - if (buildUser.enabled() && !amPrivileged()) - getOwnership(path); } /* Check the exit status. */ @@ -1846,13 +1752,9 @@ void DerivationGoal::startBuilder() uid. */ buildUser.kill(); - /* Change ownership of the temporary build directory, if we're - root. If we're not root, then the setuid helper will do it - just before it starts the builder. */ - if (amPrivileged()) { - if (chown(tmpDir.c_str(), buildUser.getUID(), buildUser.getGID()) == -1) - throw SysError(format("cannot change ownership of `%1%'") % tmpDir); - } + /* Change ownership of the temporary build directory. */ + if (chown(tmpDir.c_str(), buildUser.getUID(), buildUser.getGID()) == -1) + throw SysError(format("cannot change ownership of `%1%'") % tmpDir); /* Check that the Nix store has the appropriate permissions, i.e., owned by root and mode 1775 (sticky bit on so that @@ -2212,30 +2114,18 @@ void DerivationGoal::initChild() if (buildUser.enabled()) { printMsg(lvlChatty, format("switching to user `%1%'") % buildUser.getUser()); - if (amPrivileged()) { - - if (setgroups(0, 0) == -1) - throw SysError("cannot clear the set of supplementary groups"); - - if (setgid(buildUser.getGID()) == -1 || - getgid() != buildUser.getGID() || - getegid() != buildUser.getGID()) - throw SysError("setgid failed"); - - if (setuid(buildUser.getUID()) == -1 || - getuid() != buildUser.getUID() || - geteuid() != buildUser.getUID()) - throw SysError("setuid failed"); - - } else { - /* Let the setuid helper take care of it. */ - program = settings.nixLibexecDir + "/nix-setuid-helper"; - args.push_back(program.c_str()); - args.push_back("run-builder"); - user = buildUser.getUser().c_str(); - args.push_back(user.c_str()); - args.push_back(drv.builder.c_str()); - } + if (setgroups(0, 0) == -1) + throw SysError("cannot clear the set of supplementary groups"); + + if (setgid(buildUser.getGID()) == -1 || + getgid() != buildUser.getGID() || + getegid() != buildUser.getGID()) + throw SysError("setgid failed"); + + if (setuid(buildUser.getUID()) == -1 || + getuid() != buildUser.getUID() || + geteuid() != buildUser.getUID()) + throw SysError("setuid failed"); } /* Fill in the arguments. */ @@ -2466,12 +2356,10 @@ void DerivationGoal::deleteTmpDir(bool force) printMsg(lvlError, format("note: keeping build directory `%2%'") % drvPath % tmpDir); - if (buildUser.enabled() && !amPrivileged()) - getOwnership(tmpDir); chmod(tmpDir.c_str(), 0755); } else - deletePathWrapped(tmpDir); + deletePath(tmpDir); tmpDir = ""; } } @@ -2548,7 +2436,7 @@ Path DerivationGoal::addHashRewrite(const Path & path) string h1 = string(path, settings.nixStore.size() + 1, 32); string h2 = string(printHash32(hashString(htSHA256, "rewrite:" + drvPath + ":" + path)), 0, 32); Path p = settings.nixStore + "/" + h2 + string(path, settings.nixStore.size() + 33); - if (pathExists(p)) deletePathWrapped(p); + if (pathExists(p)) deletePath(p); assert(path.size() == p.size()); rewritesToTmp[h1] = h2; rewritesFromTmp[h2] = h1; @@ -2639,9 +2527,6 @@ SubstitutionGoal::SubstitutionGoal(const Path & storePath, Worker & worker, bool SubstitutionGoal::~SubstitutionGoal() { - /* !!! Once we let substitution goals run under a build user, we - need to use the setuid helper just as in ~DerivationGoal(). - Idem for cancel. */ if (pid != -1) worker.childTerminated(pid); } @@ -2792,7 +2677,7 @@ void SubstitutionGoal::tryToRun() /* Remove the (stale) output path if it exists. */ if (pathExists(destPath)) - deletePathWrapped(destPath); + deletePath(destPath); worker.store.setSubstituterEnv(); diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index f32329960c66..ce9b10f0538b 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -415,7 +415,7 @@ bool LocalStore::isActiveTempFile(const GCState & state, void LocalStore::deleteGarbage(GCState & state, const Path & path) { unsigned long long bytesFreed; - deletePathWrapped(path, bytesFreed); + deletePath(path, bytesFreed); state.results.bytesFreed += bytesFreed; } diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 24d6acc2f403..8aa113796da1 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1328,7 +1328,7 @@ Path LocalStore::addToStoreFromDump(const string & dump, const string & name, if (repair || !isValidPath(dstPath)) { - if (pathExists(dstPath)) deletePathWrapped(dstPath); + if (pathExists(dstPath)) deletePath(dstPath); if (recursive) { StringSource source(dump); @@ -1397,7 +1397,7 @@ Path LocalStore::addTextToStore(const string & name, const string & s, if (repair || !isValidPath(dstPath)) { - if (pathExists(dstPath)) deletePathWrapped(dstPath); + if (pathExists(dstPath)) deletePath(dstPath); writeFile(dstPath, s); @@ -1630,7 +1630,7 @@ Path LocalStore::importPath(bool requireSignature, Source & source) if (!isValidPath(dstPath)) { - if (pathExists(dstPath)) deletePathWrapped(dstPath); + if (pathExists(dstPath)) deletePath(dstPath); if (rename(unpacked.c_str(), dstPath.c_str()) == -1) throw SysError(format("cannot move `%1%' to `%2%'") diff --git a/src/libstore/local-store.hh b/src/libstore/local-store.hh index adba52fd46a9..1ace7cec996b 100644 --- a/src/libstore/local-store.hh +++ b/src/libstore/local-store.hh @@ -322,7 +322,7 @@ typedef set<Inode> InodesSeen; - the permissions are set of 444 or 555 (i.e., read-only with or without execute permission; setuid bits etc. are cleared) - the owner and group are set to the Nix user and group, if we're - in a setuid Nix installation. */ + running as root. */ void canonicalisePathMetaData(const Path & path, uid_t fromUid, InodesSeen & inodesSeen); void canonicalisePathMetaData(const Path & path, uid_t fromUid); @@ -330,16 +330,4 @@ void canonicaliseTimestampAndPermissions(const Path & path); MakeError(PathInUse, Error); -/* Whether we are root. */ -bool amPrivileged(); - -/* Recursively change the ownership of `path' to the current uid. */ -void getOwnership(const Path & path); - -/* Like deletePath(), but changes the ownership of `path' using the - setuid wrapper if necessary (and possible). */ -void deletePathWrapped(const Path & path, unsigned long long & bytesFreed); - -void deletePathWrapped(const Path & path); - } diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 8fd61826fa28..9437a2f99a3c 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -968,19 +968,6 @@ void closeOnExec(int fd) } -void setuidCleanup() -{ - /* Don't trust the environment. */ - environ = 0; - - /* Make sure that file descriptors 0, 1, 2 are open. */ - for (int fd = 0; fd <= 2; ++fd) { - struct stat st; - if (fstat(fd, &st) == -1) abort(); - } -} - - #if HAVE_VFORK pid_t (*maybeVfork)() = vfork; #else diff --git a/src/libutil/util.hh b/src/libutil/util.hh index 86c65b763d00..2335cbf98820 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -268,10 +268,6 @@ void closeMostFDs(const set<int> & exceptions); /* Set the close-on-exec flag for the given file descriptor. */ void closeOnExec(int fd); -/* Common initialisation for setuid programs: clear the environment, - sanitize file handles 0, 1 and 2. */ -void setuidCleanup(); - /* Call vfork() if available, otherwise fork(). */ extern pid_t (*maybeVfork)(); diff --git a/src/nix-setuid-helper/Makefile.am b/src/nix-setuid-helper/Makefile.am deleted file mode 100644 index a04701636d84..000000000000 --- a/src/nix-setuid-helper/Makefile.am +++ /dev/null @@ -1,7 +0,0 @@ -libexec_PROGRAMS = nix-setuid-helper - -nix_setuid_helper_SOURCES = nix-setuid-helper.cc -nix_setuid_helper_LDADD = ../libutil/libutil.la \ - ../boost/format/libformat.la - -AM_CXXFLAGS = -I$(srcdir)/.. -I$(srcdir)/../libutil diff --git a/src/nix-setuid-helper/nix-setuid-helper.cc b/src/nix-setuid-helper/nix-setuid-helper.cc deleted file mode 100644 index 697964088c2a..000000000000 --- a/src/nix-setuid-helper/nix-setuid-helper.cc +++ /dev/null @@ -1,263 +0,0 @@ -#include "config.h" - -#include <sys/types.h> -#include <sys/stat.h> -#include <unistd.h> -#include <fcntl.h> -#include <stdlib.h> - -#include <pwd.h> -#include <grp.h> - -#include <iostream> -#include <vector> - -#include "util.hh" - -using namespace nix; - - -extern char * * environ; - - -/* Recursively change the ownership of `path' to user `uidTo' and - group `gidTo'. `path' must currently be owned by user `uidFrom', - or, if `uidFrom' is -1, by group `gidFrom'. */ -static void secureChown(uid_t uidFrom, gid_t gidFrom, - uid_t uidTo, gid_t gidTo, const Path & path) -{ - format error = format("cannot change ownership of `%1%'") % path; - - struct stat st; - if (lstat(path.c_str(), &st) == -1) - /* Important: don't give any detailed error messages here. - Otherwise, the Nix account can discover information about - the existence of paths that it doesn't normally have access - to. */ - throw Error(error); - - if (uidFrom != (uid_t) -1) { - assert(uidFrom != 0); - if (st.st_uid != uidFrom) - throw Error(error); - } else { - assert(gidFrom != 0); - if (st.st_gid != gidFrom) - throw Error(error); - } - - assert(uidTo != 0 && gidTo != 0); - -#if HAVE_LCHOWN - if (lchown(path.c_str(), uidTo, gidTo) == -1) - throw Error(error); -#else - if (!S_ISLNK(st.st_mode) && - chown(path.c_str(), uidTo, gidTo) == -1) - throw Error(error); -#endif - - if (S_ISDIR(st.st_mode)) { - Strings names = readDirectory(path); - for (Strings::iterator i = names.begin(); i != names.end(); ++i) - /* !!! recursion; check stack depth */ - secureChown(uidFrom, gidFrom, uidTo, gidTo, path + "/" + *i); - } -} - - -static uid_t nameToUid(const string & userName) -{ - struct passwd * pw = getpwnam(userName.c_str()); - if (!pw) - throw Error(format("user `%1%' does not exist") % userName); - return pw->pw_uid; -} - - -static void checkIfBuildUser(const StringSet & buildUsers, - const string & userName) -{ - if (buildUsers.find(userName) == buildUsers.end()) - throw Error(format("user `%1%' is not a member of the build users group") - % userName); -} - - -/* Run `program' under user account `targetUser'. `targetUser' should - be a member of `buildUsersGroup'. The ownership of the current - directory is changed from the Nix user (uidNix) to the target - user. */ -static void runBuilder(uid_t uidNix, gid_t gidBuildUsers, - const StringSet & buildUsers, const string & targetUser, - string program, int argc, char * * argv, char * * env) -{ - uid_t uidTargetUser = nameToUid(targetUser); - - /* Sanity check. */ - if (uidTargetUser == 0) - throw Error("won't setuid to root"); - - /* Verify that the target user is a member of the build users - group. */ - checkIfBuildUser(buildUsers, targetUser); - - /* Chown the current directory, *if* it is owned by the Nix - account. The idea is that the current directory is the - temporary build directory in /tmp or somewhere else, and we - don't want to create that directory here. */ - secureChown(uidNix, (gid_t) -1, uidTargetUser, gidBuildUsers, "."); - - /* Set the real, effective and saved gid. Must be done before - setuid(), otherwise it won't set the real and saved gids. */ - if (setgroups(0, 0) == -1) - throw SysError("cannot clear the set of supplementary groups"); - - if (setgid(gidBuildUsers) == -1 || - getgid() != gidBuildUsers || - getegid() != gidBuildUsers) - throw SysError("setgid failed"); - - /* Set the real, effective and saved uid. */ - if (setuid(uidTargetUser) == -1 || - getuid() != uidTargetUser || - geteuid() != uidTargetUser) - throw SysError("setuid failed"); - - /* Execute the program. */ - std::vector<const char *> args; - for (int i = 0; i < argc; ++i) - args.push_back(argv[i]); - args.push_back(0); - - environ = env; - - /* Glibc clears TMPDIR in setuid programs (see - sysdeps/generic/unsecvars.h in the Glibc sources), so bring it - back. */ - setenv("TMPDIR", getenv("NIX_BUILD_TOP"), 1); - - if (execv(program.c_str(), (char * *) &args[0]) == -1) - throw SysError(format("cannot execute `%1%'") % program); -} - - -void killBuildUser(gid_t gidBuildUsers, - const StringSet & buildUsers, const string & userName) -{ - uid_t uid = nameToUid(userName); - - /* Verify that the user whose processes we are to kill is a member - of the build users group. */ - checkIfBuildUser(buildUsers, userName); - - assert(uid != 0); - - killUser(uid); -} - - -#ifndef NIX_SETUID_CONFIG_FILE -#define NIX_SETUID_CONFIG_FILE "/etc/nix-setuid.conf" -#endif - - -static void run(int argc, char * * argv) -{ - char * * oldEnviron = environ; - - setuidCleanup(); - - if (geteuid() != 0) - throw Error("nix-setuid-wrapper must be setuid root"); - - - /* Read the configuration file. It should consist of two words: - - <nix-user-name> <nix-builders-group> - - The first is the privileged account under which the main Nix - processes run (i.e., the supposed caller). It should match our - real uid. The second is the Unix group to which the Nix - builders belong (and nothing else!). */ - string configFile = NIX_SETUID_CONFIG_FILE; - AutoCloseFD fdConfig = open(configFile.c_str(), O_RDONLY); - if (fdConfig == -1) - throw SysError(format("opening `%1%'") % configFile); - - /* Config file should be owned by root. */ - struct stat st; - if (fstat(fdConfig, &st) == -1) throw SysError("statting file"); - if (st.st_uid != 0) - throw Error(format("`%1%' not owned by root") % configFile); - if (st.st_mode & (S_IWGRP | S_IWOTH)) - throw Error(format("`%1%' should not be group or world-writable") % configFile); - - vector<string> tokens = tokenizeString<vector<string> >(readFile(fdConfig)); - - fdConfig.close(); - - if (tokens.size() != 2) - throw Error(format("parse error in `%1%'") % configFile); - - string nixUser = tokens[0]; - string buildUsersGroup = tokens[1]; - - - /* Check that the caller (real uid) is the one allowed to call - this program. */ - uid_t uidNix = nameToUid(nixUser); - if (uidNix != getuid()) - throw Error("you are not allowed to call this program, go away"); - - - /* Get the gid and members of buildUsersGroup. */ - struct group * gr = getgrnam(buildUsersGroup.c_str()); - if (!gr) - throw Error(format("group `%1%' does not exist") % buildUsersGroup); - gid_t gidBuildUsers = gr->gr_gid; - - StringSet buildUsers; - for (char * * p = gr->gr_mem; *p; ++p) - buildUsers.insert(*p); - - - /* Perform the desired command. */ - if (argc < 2) - throw Error("invalid arguments"); - - string command(argv[1]); - - if (command == "run-builder") { - /* Syntax: nix-setuid-helper run-builder <username> <program> - <arg0 arg1...> */ - if (argc < 4) throw Error("missing user name / program name"); - runBuilder(uidNix, gidBuildUsers, buildUsers, - argv[2], argv[3], argc - 4, argv + 4, oldEnviron); - } - - else if (command == "get-ownership") { - /* Syntax: nix-setuid-helper get-ownership <path> */ - if (argc != 3) throw Error("missing path"); - secureChown((uid_t) -1, gidBuildUsers, uidNix, gidBuildUsers, argv[2]); - } - - else if (command == "kill") { - /* Syntax: nix-setuid-helper kill <username> */ - if (argc != 3) throw Error("missing user name"); - killBuildUser(gidBuildUsers, buildUsers, argv[2]); - } - - else throw Error ("invalid command"); -} - - -int main(int argc, char * * argv) -{ - try { - run(argc, argv); - } catch (Error & e) { - std::cerr << e.msg() << std::endl; - return 1; - } -} |