diff --git a/src/nix-setuid-helper/main.cc b/src/nix-setuid-helper/main.cc index e2bab9e3e6..6fa87e3729 100644 --- a/src/nix-setuid-helper/main.cc +++ b/src/nix-setuid-helper/main.cc @@ -17,7 +17,10 @@ using namespace nix; -static void secureChown(uid_t uidTarget, gid_t gidTarget, +/* Recursively change the ownership of `path' from `uidFrom' to + `uidTo' and `gidTo'. Barf if we encounter a file not owned by + `uidFrom'. */ +static void secureChown(uid_t uidFrom, uid_t uidTo, gid_t gidTo, const Path & path) { /* Recursively chown `path' to the specified uid and gid, but only @@ -35,24 +38,53 @@ static uid_t nameToUid(const string & userName) } -static void runBuilder(const string & targetUser, +/* Run `program' under user account `targetUser'. `targetUser' should + be a member of `buildUsersGroup'. The ownership of the current + directory is changed from the Nix user (uidNix) to the target + user. */ +static void runBuilder(uid_t uidNix, + const string & buildUsersGroup, const string & targetUser, string program, int argc, char * * argv) { uid_t uidTargetUser = nameToUid(targetUser); - gid_t gidBuilders = 1234; + + /* Sanity check. */ + if (uidTargetUser == 0) + throw Error("won't setuid to root"); + + /* Get the gid and members of buildUsersGroup. */ + struct group * gr = getgrnam(buildUsersGroup.c_str()); + if (!gr) + throw Error(format("group `%1%' does not exist") % buildUsersGroup); + gid_t gidBuildUsers = gr->gr_gid; + + /* Verify that the target user is a member of that group. */ + Strings users; + bool found = false; + for (char * * p = gr->gr_mem; *p; ++p) + if (string(*p) == targetUser) { + found = true; + break; + } + if (!found) + throw Error(format("user `%1%' is not a member of `%2%'") + % targetUser % buildUsersGroup); /* 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(uidTargetUser, gidBuilders, "."); + secureChown(uidNix, uidTargetUser, gidBuildUsers, "."); - /* Set the real, effective and saved gid. Must be done before setuid(), otherwise it won't set the real and saved gids. */ if (setgroups(0, 0) == -1) throw SysError("cannot clear the set of supplementary groups"); - //setgid(gidBuilders); + + if (setgid(gidBuildUsers) == -1 || + getgid() != gidBuildUsers || + getegid() != gidBuildUsers) + throw SysError("setgid failed"); /* Set the real, effective and saved uid. */ if (setuid(uidTargetUser) == -1 || @@ -116,14 +148,14 @@ static void run(int argc, char * * argv) throw Error(format("parse error in `%1%'") % configFile); Strings::iterator i = tokens.begin(); - string allowedUser = *i++; + string nixUser = *i++; string buildUsersGroup = *i++; /* Check that the caller (real uid) is the one allowed to call this program. */ - uid_t uidAllowedUser = nameToUid(allowedUser); - if (uidAllowedUser != getuid()) + uid_t uidNix = nameToUid(nixUser); + if (uidNix != getuid()) throw Error("you are not allowed to call this program, go away"); @@ -137,7 +169,8 @@ static void run(int argc, char * * argv) /* 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); + runBuilder(uidNix, buildUsersGroup, + argv[2], argv[3], argc - 4, argv + 4); } else if (command == "fix-ownership") {