aboutsummaryrefslogtreecommitdiff
path: root/src/nix-setuid-helper/main.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/nix-setuid-helper/main.cc')
-rw-r--r--src/nix-setuid-helper/main.cc80
1 files changed, 47 insertions, 33 deletions
diff --git a/src/nix-setuid-helper/main.cc b/src/nix-setuid-helper/main.cc
index ea60b2800..e9ffcfd02 100644
--- a/src/nix-setuid-helper/main.cc
+++ b/src/nix-setuid-helper/main.cc
@@ -17,27 +17,42 @@
using namespace nix;
-/* 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 change the ownership of `path' to user `uidTo' and
+ group `gidTo'. `path' must currently be owned by user `uidFrom',
+ or, if `uidFrom' is -1, by group `gidFrom'. */
+static void secureChown(uid_t uidFrom, gid_t gidFrom,
+ uid_t uidTo, gid_t gidTo, const Path & path)
{
+ format error = format("cannot change ownership of `%1%'") % path;
+
struct stat st;
if (lstat(path.c_str(), &st) == -1)
- throw SysError(format("statting of `%1%'") % path);
+ /* Important: don't give any detailed error messages here.
+ Otherwise, the Nix account can discover information about
+ the existence of paths that it doesn't normally have access
+ to. */
+ throw Error(error);
+
+ if (uidFrom != -1) {
+ assert(uidFrom != 0);
+ if (st.st_uid != uidFrom)
+ throw Error(error);
+ } else {
+ assert(gidFrom != 0);
+ if (st.st_gid != gidFrom)
+ throw Error(error);
+ }
- if (st.st_uid != uidFrom)
- throw Error(format("path `%1%' owned by the wrong owner") % path);
+ assert(uidTo != 0 && gidTo != 0);
if (lchown(path.c_str(), uidTo, gidTo) == -1)
- throw SysError(format("changing ownership of `%1%'") % path);
+ throw Error(error);
if (S_ISDIR(st.st_mode)) {
Strings names = readDirectory(path);
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
/* !!! recursion; check stack depth */
- secureChown(uidFrom, uidTo, gidTo, path + "/" + *i);
+ secureChown(uidFrom, gidFrom, uidTo, gidTo, path + "/" + *i);
}
}
@@ -55,8 +70,8 @@ static uid_t nameToUid(const string & userName)
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,
+static void runBuilder(uid_t uidNix, gid_t gidBuildUsers,
+ const StringSet & buildUsers, const string & targetUser,
string program, int argc, char * * argv, char * * env)
{
uid_t uidTargetUser = nameToUid(targetUser);
@@ -65,29 +80,16 @@ static void runBuilder(uid_t uidNix,
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);
+ if (buildUsers.find(targetUser) == buildUsers.end())
+ throw Error(format("user `%1%' is not a member of the build users group")
+ % targetUser);
/* 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(uidNix, uidTargetUser, gidBuildUsers, ".");
+ secureChown(uidNix, -1, uidTargetUser, gidBuildUsers, ".");
/* Set the real, effective and saved gid. Must be done before
setuid(), otherwise it won't set the real and saved gids. */
@@ -171,6 +173,17 @@ static void run(int argc, char * * argv)
throw Error("you are not allowed to call this program, go away");
+ /* 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;
+
+ StringSet buildUsers;
+ for (char * * p = gr->gr_mem; *p; ++p)
+ buildUsers.insert(*p);
+
+
/* Perform the desired command. */
if (argc < 2)
throw Error("invalid arguments");
@@ -181,19 +194,20 @@ static void run(int argc, char * * argv)
/* Syntax: nix-setuid-helper run-builder <username> <program>
<arg0 arg1...> */
if (argc < 4) throw Error("missing user name / program name");
- runBuilder(uidNix, buildUsersGroup,
+ runBuilder(uidNix, gidBuildUsers, buildUsers,
argv[2], argv[3], argc - 4, argv + 4, oldEnviron);
}
- else if (command == "fix-ownership") {
- /* Syntax: nix-setuid-helper <fix-ownership> <path> */
+ else if (command == "get-ownership") {
+ /* Syntax: nix-setuid-helper get-ownership <path> */
+ if (argc != 3) throw Error("missing path");
+ secureChown(-1, gidBuildUsers, uidNix, gidBuildUsers, argv[2]);
}
else throw Error ("invalid command");
}
-
int main(int argc, char * * argv)
{
try {