* Remove most of the old setuid code.

* Much simpler setuid code for the worker in slave mode.
This commit is contained in:
Eelco Dolstra 2006-12-02 15:45:51 +00:00
parent 9c9cdb06d0
commit 536595b072
3 changed files with 57 additions and 138 deletions

View file

@ -15,6 +15,9 @@
#include <aterm2.h>
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
}
}
@ -213,9 +264,10 @@ 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;

View file

@ -11,11 +11,8 @@
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <fcntl.h>
#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
}
}

View file

@ -249,10 +249,6 @@ string int2String(int n);
bool string2Int(const string & s, int & n);
/* Setuid support. */
void switchToNixUser();
}