diff --git a/src/libmain/setuid-common.hh b/src/libmain/setuid-common.hh new file mode 100644 index 0000000000..a3e8409961 --- /dev/null +++ b/src/libmain/setuid-common.hh @@ -0,0 +1,22 @@ +/* Code shared between libmain and nix-setuid-helper. */ + +extern char * * environ; + + +namespace nix { + + +void setuidCleanup() +{ + /* Don't trust the environment. */ + environ = 0; + + /* 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(); + } +} + + +} diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 393f31fcda..fa72ca5bc4 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -14,7 +14,7 @@ #include -extern char * * environ; +#include "setuid-common.hh" namespace nix { @@ -218,20 +218,11 @@ static void setuidInit() uid_t nixUid = geteuid(); gid_t nixGid = getegid(); - fprintf(stderr, "<<< setuid mode >>>\n"); - - /* Don't trust the environment. */ - environ = 0; + setuidCleanup(); /* 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 diff --git a/src/nix-setuid-helper/main.cc b/src/nix-setuid-helper/main.cc index ff70fa6560..50a059f504 100644 --- a/src/nix-setuid-helper/main.cc +++ b/src/nix-setuid-helper/main.cc @@ -1,3 +1,119 @@ +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "util.hh" + +#include "../libmain/setuid-common.hh" + +using namespace nix; + + +static void secureChown(uid_t uidTarget, gid_t gidTarget, + const Path & path) +{ + /* Recursively chown `path' to the specified uid and gid, but only + if it is currently owned by the Nix account. */ + /* !!! */ +} + + +static void runBuilder(string userName, + string program, int argc, char * * argv) +{ + struct passwd * pw = getpwnam(userName.c_str()); + if (!pw) + throw Error(format("the user `%1%' does not exist") % userName); + + gid_t gidBuilders = 1234; + + /* Chown the current directory, *if* it is owned by the Nix + account. The idea is that the current directory is the + temporary build directory in /tmp or somewhere else, and we + don't want to create that directory here. */ + secureChown(pw->pw_uid, gidBuilders, "."); + + /* Set the real, effective and saved gid. Must be done before + setuid(), otherwise it won't set the real and saved gids. */ + //setgid(gidBuilders); + + /* Set the real, effective and saved uid. */ + setuid(pw->pw_uid); + if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) + throw Error("cannot setuid"); + + /* Execute the program. */ + std::vector args; + args.push_back(program.c_str()); + for (int i = 0; i < argc; ++i) + args.push_back(argv[i]); + args.push_back(0); + + if (execve(program.c_str(), (char * *) &args[0], 0) == -1) + throw SysError(format("cannot execute `%1%'") % program); +} + + +static void run(int argc, char * * argv) +{ + char * * oldEnviron = environ; + + setuidCleanup(); + + if (geteuid() != 0) + throw Error("nix-setuid-wrapper must be setuid root"); + + + /* Read the configuration file. It should consist of two words: + + + + The first is the privileged account under which the main Nix + processes run (i.e., the supposed caller). It should match our + real uid. The second is the Unix group to which the Nix + builders belong (and nothing else!). */ + /* !!! */ + + + /* Make sure that we are called by the Nix account, not by someone + else. */ + // ... + + /* Perform the desired command. */ + if (argc < 2) + throw Error("invalid arguments"); + + string command(argv[1]); + + if (command == "run-builder") { + /* Syntax: nix-setuid-helper run-builder + */ + if (argc < 4) throw Error("missing user name / program name"); + runBuilder(argv[2], argv[3], argc - 4, argv + 4); + } + + else if (command == "fix-ownership") { + /* Syntax: nix-setuid-helper */ + } + + else throw Error ("invalid command"); +} + + + int main(int argc, char * * argv) { + try { + run(argc, argv); + } catch (Error & e) { + std::cerr << e.msg() << std::endl; + return 1; + } }