diff --git a/configure.ac b/configure.ac index 5dab9847ad..2ba8e92384 100644 --- a/configure.ac +++ b/configure.ac @@ -151,6 +151,12 @@ if test "$setuid_hack" = "yes"; then AC_DEFINE(SETUID_HACK, 1, [whether to install Nix setuid]) fi +AC_CHECK_FUNC(setresuid, [HAVE_SETRESUID=1], [HAVE_SETRESUID=]) +AM_CONDITIONAL(HAVE_SETRESUID, test "$HAVE_SETRESUID" = "1") +if test "$HAVE_SETRESUID" = "1"; then + AC_DEFINE(HAVE_SETRESUID, 1, [whether we have setresuid()]) +fi + AC_ARG_WITH(nix-user, AC_HELP_STRING([--with-nix-user=USER], [user for Nix setuid binaries]), NIX_USER=$withval, NIX_USER=nix) diff --git a/src/Makefile.am b/src/Makefile.am index 6c3e5ee209..5637382172 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,5 +4,10 @@ SUBDIRS = bin2c boost libutil libstore libmain nix-store nix-hash \ SETUID_PROGS = nix-store nix-instantiate nix-env install-exec-hook: if SETUID_HACK +if HAVE_SETRESUID + cd $(DESTDIR)$(bindir) && chown @NIX_USER@ $(SETUID_PROGS) \ + && chgrp @NIX_GROUP@ $(SETUID_PROGS) && chmod ug+s $(SETUID_PROGS) +else cd $(DESTDIR)$(bindir) && chown root $(SETUID_PROGS) && chmod u+s $(SETUID_PROGS) endif +endif diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 068c126596..df56a77cf8 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -163,6 +163,16 @@ static void initAndRun(int argc, char * * argv) } +#if HAVE_SETRESUID +#define _setuid(uid) setresuid(uid, uid, uid) +#define _setgid(gid) setresgid(gid, gid, gid) +#else +/* Only works properly when run by root. */ +#define _setuid(uid) setuid(uid) +#define _setgid(gid) setgid(gid) +#endif + + void switchToNixUser() { #if SETUID_HACK @@ -186,6 +196,13 @@ void switchToNixUser() exit(1); } + /* !!! Apparently it is unspecified whether getgroups() includes + the effective gid. In that case the following test is always + true *if* the program is installed setgid (which we do when we + have setresuid()). On Linux this doesn't appear to be the + case, but we should switch to the real gid before doing this + test, and then switch back to the saved gid. */ + /* Check that the current user is a member of the Nix group. */ bool found = false; for (int i = 0; i < nrGids; ++i) @@ -196,15 +213,15 @@ void switchToNixUser() if (!found) { /* Not in the Nix group - drop all root/Nix privileges. */ - setgid(getgid()); - setuid(getuid()); + _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 || + if (_setgid(gr->gr_gid) != 0 || getgid() != gr->gr_gid || getegid() != gr->gr_gid) { @@ -222,7 +239,7 @@ void switchToNixUser() /* 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 || + if (_setuid(pw->pw_uid) != 0 || getuid() != pw->pw_uid || geteuid() != pw->pw_uid) { @@ -240,11 +257,7 @@ 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;