* 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
### 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'
#
# This option is only applicable if `build-allow-root' is `false' and
# Nix is invoked under the `root' user (or setuid-root). It contains
# a list of user names under which Nix can execute builds. Builds
# cannot be performed by root since that would allow users to take
# over the system by supplying specially crafted builders; and they
# cannot be performed by the calling user since that would allow
# him/her to influence the build result.
# This option contains a list of user names under which Nix can
# execute builds. In multi-user Nix installations, builds should not
# be performed by the Nix account since that would allow users to
# arbitrarily modify the Nix store and database by supplying specially
# crafted builders; and they cannot be performed by the calling user
# since that would allow him/her to influence the build result.
#
# Thus this list should contain a number of `special' user accounts
# created specifically for Nix, e.g., `nix-builder-1',
# `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
# 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:
# build-users = nix-builder-1 nix-builder-2 nix-builder-3
#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'
#
# 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 (setgid(nixGid)) abort();
#endif
setuidMode = true;
}

View file

@ -24,6 +24,9 @@ namespace nix {
Path makeRootName(const Path & gcRoot, int & counter);
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
is `false', then we have to build as one of the users listed in
`build-users'. */
if (!queryBoolSetting("build-allow-root", true) &&
getuid() == rootUserId)
{
/* If `build-users' is not empty, then we have to build as one of
the users listed in `build-users'. */
gid_t gidBuildGroup = -1;
if (querySetting("build-users", Strings()).size() > 0) {
buildUser.acquire();
assert(buildUser.getUID() != 0);
@ -1288,6 +1286,14 @@ void DerivationGoal::startBuilder()
if (chown(tmpDir.c_str(), buildUser.getUID(), (gid_t) -1) == -1)
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,
i.e., owned by root and mode 1777 (sticky bit on so that
the builder can create its output but not mess with the
@ -1295,13 +1301,13 @@ void DerivationGoal::startBuilder()
struct stat st;
if (stat(nixStore.c_str(), &st) == -1)
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) ||
((st.st_mode & S_IRWXO) != S_IRWXO))
((st.st_mode & S_IRWXG) != S_IRWXG) ||
(st.st_gid != gidBuildGroup))
throw Error(format(
"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);
const char * * envArr = strings2CharPtrs(envStrs);
/* If we are running as root and `build-allow-root' is
`false', then switch to the user we allocated above.
Make sure that we drop all root privileges. Note that
initChild() above has closed all file descriptors
except std*, so that's safe. Also note that setuid()
when run as root sets the real, effective and saved
UIDs. */
/* If we are running in `build-users' mode, then switch to
the user we allocated above. Make sure that we drop
all root privileges. Note that initChild() above has
closed all file descriptors except std*, so that's
safe. Also note that setuid() when run as root sets
the real, effective and saved UIDs. */
if (buildUser.getUID() != 0) {
printMsg(lvlError, format("switching to uid `%1%'") % buildUser.getUID());
/* !!! setgid also */
if (setgroups(0, 0) == -1)
throw SysError("cannot clear the set of supplementary groups");
setuid(buildUser.getUID());
assert(getuid() == buildUser.getUID());
assert(geteuid() == buildUser.getUID());
setgid(gidBuildGroup);
assert(getgid() == gidBuildGroup);
assert(getegid() == gidBuildGroup);
}
/* 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);
}
if (st.st_uid != getuid() || st.st_gid != getgid()) {
if (chown(path.c_str(), getuid(), getgid()) == -1)
if (st.st_uid != geteuid() || st.st_gid != getegid()) {
if (chown(path.c_str(), geteuid(), getegid()) == -1)
throw SysError(format("changing owner/group of `%1%' to %2%/%3%")
% path % getuid() % getgid());
% path % geteuid() % getegid());
}
if (st.st_mtime != 0) {