diff --git a/Makefile.am b/Makefile.am index 2213a81dd..9f9f9e0a9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,16 +14,19 @@ relname: install-data-local: init-state if INIT_STATE +if SETUID_HACK +INIT_FLAGS = -g @NIX_GROUP@ -o @NIX_USER@ +endif init-state: - $(INSTALL) -d $(DESTDIR)$(localstatedir)/nix - $(INSTALL) -d $(DESTDIR)$(localstatedir)/nix/db - $(INSTALL) -d $(DESTDIR)$(localstatedir)/log/nix - $(INSTALL) -d $(DESTDIR)$(localstatedir)/nix/profiles - $(INSTALL) -d $(DESTDIR)$(localstatedir)/nix/gcroots - $(INSTALL) -d $(DESTDIR)$(localstatedir)/nix/gcroots/tmp + $(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix + $(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/db + $(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/log/nix + $(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/profiles + $(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/gcroots + $(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/gcroots/tmp rm -f $(DESTDIR)$(localstatedir)/nix/gcroots/profiles ln -s $(localstatedir)/nix/profiles $(DESTDIR)$(localstatedir)/nix/gcroots/profiles - $(INSTALL) -d $(DESTDIR)$(prefix)/store + $(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(prefix)/store # $(bindir)/nix-store --init else init-state: diff --git a/configure.ac b/configure.ac index 0d7045de1..5dab9847a 100644 --- a/configure.ac +++ b/configure.ac @@ -142,6 +142,28 @@ AC_ARG_ENABLE(init-state, AC_HELP_STRING([--disable-init-state], init_state=$enableval, init_state=yes) AM_CONDITIONAL(INIT_STATE, test "$init_state" = "yes") + +AC_ARG_ENABLE(setuid, AC_HELP_STRING([--enable-setuid], + [install Nix setuid]), + setuid_hack=$enableval, setuid_hack=no) +AM_CONDITIONAL(SETUID_HACK, test "$setuid_hack" = "yes") +if test "$setuid_hack" = "yes"; then + AC_DEFINE(SETUID_HACK, 1, [whether to install Nix setuid]) +fi + +AC_ARG_WITH(nix-user, AC_HELP_STRING([--with-nix-user=USER], + [user for Nix setuid binaries]), + NIX_USER=$withval, NIX_USER=nix) +AC_SUBST(NIX_USER) +AC_DEFINE_UNQUOTED(NIX_USER, ["$NIX_USER"], [Nix user]) + +AC_ARG_WITH(nix-group, AC_HELP_STRING([--with-nix-group=USER], + [group for Nix setuid binaries]), + NIX_GROUP=$withval, NIX_GROUP=nix) +AC_SUBST(NIX_GROUP) +AC_DEFINE_UNQUOTED(NIX_GROUP, ["$NIX_GROUP"], [Nix group]) + + AM_CONFIG_HEADER([config.h]) AC_CONFIG_FILES([Makefile externals/Makefile diff --git a/scripts/nix-push.in b/scripts/nix-push.in index 84330016f..fc44d02c6 100644 --- a/scripts/nix-push.in +++ b/scripts/nix-push.in @@ -92,7 +92,7 @@ while (scalar @tmp > 0) { my @tmp2 = @tmp[0..$n - 1]; @tmp = @tmp[$n..scalar @tmp - 1]; - system "@bindir@/nix-store --realise -B @tmp2 > /dev/null"; + system "@bindir@/nix-store --realise @tmp2 > /dev/null"; if ($?) { die "`nix-store --realise' failed"; } open NARPATHS, "@bindir@/nix-store --query --list @tmp2 |" or die "cannot run nix"; diff --git a/src/Makefile.am b/src/Makefile.am index 29bd535f6..6c3e5ee20 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,2 +1,8 @@ SUBDIRS = bin2c boost libutil libstore libmain nix-store nix-hash \ libexpr nix-instantiate nix-env log2xml + +SETUID_PROGS = nix-store nix-instantiate nix-env +install-exec-hook: +if SETUID_HACK + cd $(DESTDIR)$(bindir) && chown root $(SETUID_PROGS) && chmod u+s $(SETUID_PROGS) +endif diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 3f9b2a10d..068c12659 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -5,6 +5,9 @@ #include #include +#include +#include + extern "C" { #include } @@ -160,10 +163,89 @@ static void initAndRun(int argc, char * * argv) } +void switchToNixUser() +{ +#if SETUID_HACK + + /* Here we set the uid and gid to the Nix user and group, + respectively, IF the current (real) user is a member of the Nix + group. Otherwise we just drop all privileges. */ + + /* Lookup the Nix gid. */ + struct group * gr = getgrnam(NIX_GROUP); + if (!gr) { + cerr << format("missing group `%1%'\n") % NIX_GROUP; + exit(1); + } + + /* Get the supplementary group IDs for the current user. */ + int maxGids = 512, nrGids; + gid_t gids[maxGids]; + if ((nrGids = getgroups(maxGids, gids)) == -1) { + cerr << format("unable to query gids\n"); + exit(1); + } + + /* Check that the current user is a member of the Nix group. */ + bool found = false; + for (int i = 0; i < nrGids; ++i) + if (gids[i] == gr->gr_gid) { + found = true; + break; + } + + if (!found) { + /* Not in the Nix group - drop all root/Nix privileges. */ + setgid(getgid()); + setuid(getuid()); + return; + } + + /* Set the real, effective and saved gids to gr->gr_gid. Also + make very sure that this succeeded. We switch the gid first + because we cannot do it after we have dropped root uid. */ + if (setgid(gr->gr_gid) != 0 || + getgid() != gr->gr_gid || + getegid() != gr->gr_gid) + { + cerr << format("unable to set gid to `%1%'\n") % NIX_GROUP; + exit(1); + } + + /* Lookup the Nix uid. */ + struct passwd * pw = getpwnam(NIX_USER); + if (!pw) { + cerr << format("missing user `%1%'\n") % NIX_USER; + exit(1); + } + + /* This will drop all root privileges, setting the real, effective + and saved uids to pw->pw_uid. Also make very sure that this + succeeded.*/ + if (setuid(pw->pw_uid) != 0 || + getuid() != pw->pw_uid || + geteuid() != pw->pw_uid) + { + cerr << format("unable to set uid to `%1%'\n") % NIX_USER; + exit(1); + } + +#endif +} + + static char buf[1024]; int main(int argc, char * * argv) { + /* If we are setuid root, we have to get rid of the excess + privileges ASAP. */ + printMsg(lvlError, format("%1% %2% %3% %4%\n") % getuid() % geteuid() + % getgid() % getegid()); + switchToNixUser(); + printMsg(lvlError, format("%1% %2% %3% %4%\n") % getuid() % geteuid() + % getgid() % getegid()); + /* ATerm setup. */ ATerm bottomOfStack; ATinit(argc, argv, &bottomOfStack);