diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 33576d69c2..b4a0f774a0 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -15,6 +15,9 @@ #include +extern char * * environ; + + namespace nix { @@ -204,6 +207,54 @@ static void initAndRun(int argc, char * * argv) } +static void setuidInit() +{ + /* Don't do anything if this is not a setuid binary. */ + if (getuid() == geteuid() && getgid() == getegid()) return; + + uid_t nixUid = geteuid(); + gid_t nixGid = getegid(); + + fprintf(stderr, "<<< setuid mode >>>\n"); + + /* Don't trust the environment. */ + environ = 0; + + /* Don't trust the current directory. */ + if (chdir("/") == -1) abort(); + + /* Make sure that file descriptors 0, 1, 2 are open. */ + for (int fd = 0; fd <= 2; ++fd) { + struct stat st; + if (fstat(fd, &st) == -1) abort(); + } + + /* Set the real (and preferably also the save) uid/gid to the + effective uid/gid. This matters mostly when we're not using + build-users (bad!), since some builders (like Perl) complain + when real != effective. + + On systems where setresuid is unavailable, we can't drop the + saved uid/gid. This means that we could go back to the + original real uid (i.e., the uid of the caller). That's not + really a problem, except maybe when we execute a builder and + we're not using build-users. In that case, the builder may be + able to switch to the uid of the caller and possibly do bad + stuff. But note that when not using build-users, the builder + could also modify the Nix executables (say, replace them by a + Trojan horse), so the problem is already there. */ + +#if HAVE_SETRESUID + setresuid(nixUid, nixUid, nixUid); + setresgid(nixGid, nixGid, nixGid); +#else + /* Note: doesn't set saved uid/gid! */ + setuid(nixUid); + setgid(nixGid); +#endif +} + + } @@ -212,10 +263,11 @@ static char buf[1024]; int main(int argc, char * * argv) { using namespace nix; - - /* If we are setuid root, we have to get rid of the excess - privileges ASAP. */ - switchToNixUser(); + + /* If we're setuid, then we need to take some security precautions + right away. */ + if (argc == 0) abort(); + setuidInit(); /* ATerm setup. */ ATerm bottomOfStack; diff --git a/src/libutil/util.cc b/src/libutil/util.cc index 375e0e1df8..5623b01312 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -11,11 +11,8 @@ #include #include -#include - #include -#include -#include +#include #include "util.hh" @@ -891,130 +888,4 @@ bool string2Int(const string & s, int & n) } -////////////////////////////////////////////////////////////////////// - - -static bool haveSwitched; -static uid_t savedUid, nixUid; -static gid_t savedGid, nixGid; - - -#if HAVE_SETRESUID -#define _setuid(uid) setresuid(uid, uid, savedUid) -#define _setgid(gid) setresgid(gid, gid, savedGid) -#else -/* Only works properly when run by root. */ -#define _setuid(uid) setuid(uid) -#define _setgid(gid) setgid(gid) -#endif - - -void switchToNixUser() -{ -#if 0 - fprintf(stderr, "real = %d/%d, effective = %d/%d\n", - getuid(), getgid(), geteuid(), getegid()); -#endif - - /* Note: we require setresuid for now since I don't want to think - to deeply about whether this works on systems that don't have - setresuid. It's already hard enough. */ - -#if HAVE_SETRESUID - - /* Setuid Nix operation works as follows: - - - The Nix binaries are owned by a Nix user and group, e.g., - nix.nix, and must setuid and setgid, e.g., - - rwsrwsr-x nix.nix - - - Users (say alice.users) are only allowed to run (most) Nix - operations if they are in the Nix group. If they aren't, - some read-only operations (like nix-env -qa) may still work. - - - We run mostly under the Nix user/group, but we switch back to - the calling user/group for some work, like reading Nix - expressions. - - */ - - - /* Don't do anything if this is not a setuid binary. */ - if (getuid() == geteuid() && getgid() == getegid()) return; - - /* 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. (The Nix group is the group of the current executable, - i.e., the current effective gid.) Otherwise we just drop all - privileges. */ - - nixGid = geteuid(); - - /* Get the supplementary group IDs for the current user. */ - int maxGids = 512, nrGids; - gid_t gids[maxGids]; - if ((nrGids = getgroups(maxGids, gids)) == -1) { - std::cerr << format("unable to query gids\n"); - 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) - if (gids[i] == nixGid) { - found = true; - break; - } - - if (!found) { - /* Not in the Nix group - drop all root/Nix privileges. */ - _setgid(getgid()); - _setuid(getuid()); - return; - } - - /* Save the uid/gid of the caller so the we can switch back to - that uid/gid for temporary work, like accessing files, in - SwitchToOriginaluser. */ - savedUid = getuid(); - savedGid = getgid(); - - /* Set the real and effective gids to nixGid. 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(nixGid) != 0 || getgid() != nixGid || getegid() != nixGid) { - std::cerr << format("unable to set gid to `%1%'\n") % nixGid; - exit(1); - } - - /* The Nix uid is the effective uid of the owner of the current - executable, i.e., the current effective uid. */ - nixUid = geteuid(); - - /* 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(nixUid) != 0 || getuid() != nixUid || geteuid() != nixUid) { - std::cerr << format("unable to set uid to `%1%'\n") % nixUid; - exit(1); - } - - /* !!! for setuid operation, we should: 1) wipe the environment; - 2) verify file descriptors 0, 1, 2; 3) etc. - See: http://www.daemon-systems.org/man/setuid.7.html - */ - - haveSwitched = true; -#endif -} - - } diff --git a/src/libutil/util.hh b/src/libutil/util.hh index d1fb5b6e24..d49067dfe2 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -249,10 +249,6 @@ string int2String(int n); bool string2Int(const string & s, int & n); -/* Setuid support. */ -void switchToNixUser(); - - }