* Removed `build-allow-root'.

* Added `build-users-group', the group under which builds are to be
  performed.
* Check that /nix/store has 1775 permission and is owner by the
  build-users-group.
This commit is contained in:
Eelco Dolstra 2006-12-03 15:32:38 +00:00
parent 84d6459bd5
commit 35247c4c9f
5 changed files with 61 additions and 39 deletions

View file

@ -78,37 +78,46 @@
#build-max-jobs = 1 #build-max-jobs = 1
### Option `build-allow-root'
#
# This option controls Nix's behaviour when it is invoked under the
# `root' user (or setuid-root). If `true' (default), builds are
# performed under the `root' user. If `false', builds are performed
# under one of the users listed in the `build-users' option (see
# below).
#build-allow-root = true
### Option `build-users' ### Option `build-users'
# #
# This option is only applicable if `build-allow-root' is `false' and # This option contains a list of user names under which Nix can
# Nix is invoked under the `root' user (or setuid-root). It contains # execute builds. In multi-user Nix installations, builds should not
# a list of user names under which Nix can execute builds. Builds # be performed by the Nix account since that would allow users to
# cannot be performed by root since that would allow users to take # arbitrarily modify the Nix store and database by supplying specially
# over the system by supplying specially crafted builders; and they # crafted builders; and they cannot be performed by the calling user
# cannot be performed by the calling user since that would allow # since that would allow him/her to influence the build result.
# him/her to influence the build result.
# #
# Thus this list should contain a number of `special' user accounts # Thus this list should contain a number of `special' user accounts
# created specifically for Nix, e.g., `nix-builder-1', # created specifically for Nix, e.g., `nix-builder-1',
# `nix-builder-2', and so on. The more users the better, since at # `nix-builder-2', and so on. The more users the better, since at
# most a number of builds equal to the number of build users can be # most a number of builds equal to the number of build users can be
# started. # running simultaneously.
#
# If this list is empty, builds will be performed under the Nix
# account (that is, the uid under which the Nix daemon runs, or that
# owns the setuid nix-worker program).
# #
# Example: # Example:
# build-users = nix-builder-1 nix-builder-2 nix-builder-3 # build-users = nix-builder-1 nix-builder-2 nix-builder-3
#build-users = #build-users =
### Option `build-users-group'
#
# If `build-users' is used, then this option specifies the group ID
# (gid) under which each build is to be performed. This group should
# have permission to create files in the Nix store, but not delete
# them. I.e., /nix/store should be owned by the Nix account, its
# group should be the group specified here, and its mode should be
# 1775.
#
# The default is `nix'.
#
# Example:
# build-users-group = nix
#build-users-group =
### Option `system' ### Option `system'
# #
# This option specifies the canonical Nix system name of the current # This option specifies the canonical Nix system name of the current

View file

@ -258,6 +258,8 @@ static void setuidInit()
if (setuid(nixUid)) abort(); if (setuid(nixUid)) abort();
if (setgid(nixGid)) abort(); if (setgid(nixGid)) abort();
#endif #endif
setuidMode = true;
} }

View file

@ -24,6 +24,9 @@ namespace nix {
Path makeRootName(const Path & gcRoot, int & counter); Path makeRootName(const Path & gcRoot, int & counter);
void printGCWarning(); void printGCWarning();
/* Whether we're running setuid. */
bool setuidMode = false;
} }

View file

@ -1271,12 +1271,10 @@ void DerivationGoal::startBuilder()
} }
/* If we are running as root, and the `build-allow-root' setting /* If `build-users' is not empty, then we have to build as one of
is `false', then we have to build as one of the users listed in the users listed in `build-users'. */
`build-users'. */ gid_t gidBuildGroup = -1;
if (!queryBoolSetting("build-allow-root", true) && if (querySetting("build-users", Strings()).size() > 0) {
getuid() == rootUserId)
{
buildUser.acquire(); buildUser.acquire();
assert(buildUser.getUID() != 0); assert(buildUser.getUID() != 0);
@ -1288,6 +1286,14 @@ void DerivationGoal::startBuilder()
if (chown(tmpDir.c_str(), buildUser.getUID(), (gid_t) -1) == -1) if (chown(tmpDir.c_str(), buildUser.getUID(), (gid_t) -1) == -1)
throw SysError(format("cannot change ownership of `%1%'") % tmpDir); throw SysError(format("cannot change ownership of `%1%'") % tmpDir);
/* What group to execute the builder in? */
string buildGroup = querySetting("build-users-group", "nix");
struct group * gr = getgrnam(buildGroup.c_str());
if (!gr) throw Error(
format("the group `%1%' specified in `build-users-group' does not exist")
% buildGroup);
gidBuildGroup = gr->gr_gid;
/* Check that the Nix store has the appropriate permissions, /* Check that the Nix store has the appropriate permissions,
i.e., owned by root and mode 1777 (sticky bit on so that i.e., owned by root and mode 1777 (sticky bit on so that
the builder can create its output but not mess with the the builder can create its output but not mess with the
@ -1295,13 +1301,13 @@ void DerivationGoal::startBuilder()
struct stat st; struct stat st;
if (stat(nixStore.c_str(), &st) == -1) if (stat(nixStore.c_str(), &st) == -1)
throw SysError(format("cannot stat `%1%'") % nixStore); throw SysError(format("cannot stat `%1%'") % nixStore);
if (st.st_uid != rootUserId)
throw Error(format("`%1%' is not owned by root") % nixStore);
if (!(st.st_mode & S_ISVTX) || if (!(st.st_mode & S_ISVTX) ||
((st.st_mode & S_IRWXO) != S_IRWXO)) ((st.st_mode & S_IRWXG) != S_IRWXG) ||
(st.st_gid != gidBuildGroup))
throw Error(format( throw Error(format(
"builder does not have write permission to `%1%'; " "builder does not have write permission to `%1%'; "
"try `chmod 1777 %1%'") % nixStore); "try `chgrp %1% %2%; chmod 1775 %2%'")
% buildGroup % nixStore);
} }
@ -1343,23 +1349,25 @@ void DerivationGoal::startBuilder()
envStrs.push_back(i->first + "=" + i->second); envStrs.push_back(i->first + "=" + i->second);
const char * * envArr = strings2CharPtrs(envStrs); const char * * envArr = strings2CharPtrs(envStrs);
/* If we are running as root and `build-allow-root' is /* If we are running in `build-users' mode, then switch to
`false', then switch to the user we allocated above. the user we allocated above. Make sure that we drop
Make sure that we drop all root privileges. Note that all root privileges. Note that initChild() above has
initChild() above has closed all file descriptors closed all file descriptors except std*, so that's
except std*, so that's safe. Also note that setuid() safe. Also note that setuid() when run as root sets
when run as root sets the real, effective and saved the real, effective and saved UIDs. */
UIDs. */
if (buildUser.getUID() != 0) { if (buildUser.getUID() != 0) {
printMsg(lvlError, format("switching to uid `%1%'") % buildUser.getUID()); printMsg(lvlError, format("switching to uid `%1%'") % buildUser.getUID());
/* !!! setgid also */
if (setgroups(0, 0) == -1) if (setgroups(0, 0) == -1)
throw SysError("cannot clear the set of supplementary groups"); throw SysError("cannot clear the set of supplementary groups");
setuid(buildUser.getUID()); setuid(buildUser.getUID());
assert(getuid() == buildUser.getUID()); assert(getuid() == buildUser.getUID());
assert(geteuid() == buildUser.getUID()); assert(geteuid() == buildUser.getUID());
setgid(gidBuildGroup);
assert(getgid() == gidBuildGroup);
assert(getegid() == gidBuildGroup);
} }
/* Execute the program. This should not return. */ /* Execute the program. This should not return. */

View file

@ -235,10 +235,10 @@ void canonicalisePathMetaData(const Path & path)
throw SysError(format("changing mode of `%1%' to %2$o") % path % mode); throw SysError(format("changing mode of `%1%' to %2$o") % path % mode);
} }
if (st.st_uid != getuid() || st.st_gid != getgid()) { if (st.st_uid != geteuid() || st.st_gid != getegid()) {
if (chown(path.c_str(), getuid(), getgid()) == -1) if (chown(path.c_str(), geteuid(), getegid()) == -1)
throw SysError(format("changing owner/group of `%1%' to %2%/%3%") throw SysError(format("changing owner/group of `%1%' to %2%/%3%")
% path % getuid() % getgid()); % path % geteuid() % getegid());
} }
if (st.st_mtime != 0) { if (st.st_mtime != 0) {