about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--configure.ac6
-rw-r--r--src/Makefile.am5
-rw-r--r--src/libmain/shared.cc29
3 files changed, 32 insertions, 8 deletions
diff --git a/configure.ac b/configure.ac
index 5dab9847ad8f..2ba8e92384a9 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 6c3e5ee209ae..5637382172a2 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 068c126596e7..df56a77cf832 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;