diff --git a/Makefile.am b/Makefile.am index 5734cbd7..911f3080 100644 --- a/Makefile.am +++ b/Makefile.am @@ -20,10 +20,11 @@ install-data-local: init-state fi if INIT_STATE -if SETUID_HACK -INIT_FLAGS = -g @NIX_GROUP@ -o @NIX_USER@ -GROUP_WRITABLE = -m 775 -endif + +# For setuid operation, you can enable the following: +# INIT_FLAGS = -g @NIX_GROUP@ -o @NIX_USER@ +# GROUP_WRITABLE = -m 775 + init-state: $(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix $(INSTALL) $(INIT_FLAGS) -d $(DESTDIR)$(localstatedir)/nix/db @@ -40,6 +41,7 @@ init-state: $(INSTALL) $(INIT_FLAGS) -m 1777 -d $(DESTDIR)$(prefix)/store $(INSTALL) $(INIT_FLAGS) $(GROUP_WRITABLE) -d $(DESTDIR)$(localstatedir)/nix/manifests # $(bindir)/nix-store --init + else init-state: endif diff --git a/configure.ac b/configure.ac index 37ebe06b..b5c0e61b 100644 --- a/configure.ac +++ b/configure.ac @@ -237,33 +237,13 @@ AM_CONDITIONAL(INIT_STATE, test "$init_state" = "yes") # Setuid installations. -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_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) -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]) - # This is needed if ATerm, Berkeley DB or bzip2 are static libraries, # and the Nix libraries are dynamic. if test "$(uname)" = "Darwin"; then diff --git a/src/Makefile.am b/src/Makefile.am index 1cdbc558..2bf789fd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,13 +3,4 @@ SUBDIRS = bin2c boost libutil libstore libmain nix-store nix-hash \ EXTRA_DIST = aterm-helper.pl -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 +#SETUID_PROGS = nix-store nix-instantiate nix-env diff --git a/src/libutil/util.cc b/src/libutil/util.cc index f80fb7db..8bd15a9a 100644 --- a/src/libutil/util.cc +++ b/src/libutil/util.cc @@ -911,7 +911,7 @@ static gid_t savedGid, nixGid; SwitchToOriginalUser::SwitchToOriginalUser() { -#if SETUID_HACK && HAVE_SETRESUID +#if HAVE_SETRESUID /* Temporarily switch the effective uid/gid back to the saved uid/gid (which is the uid/gid of the user that executed the Nix program; it's *not* the real uid/gid, since we changed that to @@ -928,7 +928,7 @@ SwitchToOriginalUser::SwitchToOriginalUser() SwitchToOriginalUser::~SwitchToOriginalUser() { -#if SETUID_HACK && HAVE_SETRESUID +#if HAVE_SETRESUID /* Switch the effective uid/gid back to the Nix user. */ if (haveSwitched) { if (setuid(nixUid) == -1) @@ -942,21 +942,43 @@ SwitchToOriginalUser::~SwitchToOriginalUser() void switchToNixUser() { -#if SETUID_HACK + fprintf(stderr, "real = %d/%d, effective = %d/%d\n", + getuid(), geteuid(), getgid(), getegid()); + /* 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. Otherwise we just drop all privileges. */ - - /* Lookup the Nix gid. */ - struct group * gr = getgrnam(NIX_GROUP); - if (!gr) { - std::cerr << format("missing group `%1%'\n") % NIX_GROUP; - exit(1); - } + 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; @@ -976,7 +998,7 @@ void switchToNixUser() /* 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) { + if (gids[i] == nixGid) { found = true; break; } @@ -988,31 +1010,29 @@ void switchToNixUser() 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, 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. */ - nixGid = gr->gr_gid; + /* 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") % NIX_GROUP; + std::cerr << format("unable to set gid to `%1%'\n") % nixGid; exit(1); } - /* Lookup the Nix uid. */ - struct passwd * pw = getpwnam(NIX_USER); - if (!pw) { - std::cerr << format("missing user `%1%'\n") % NIX_USER; - 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.*/ - nixUid = pw->pw_uid; if (_setuid(nixUid) != 0 || getuid() != nixUid || geteuid() != nixUid) { - std::cerr << format("unable to set uid to `%1%'\n") % NIX_USER; + std::cerr << format("unable to set uid to `%1%'\n") % nixUid; exit(1); } @@ -1022,7 +1042,6 @@ void switchToNixUser() */ haveSwitched = true; - #endif }