about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/nix-setuid-helper/main.cc53
1 files changed, 43 insertions, 10 deletions
diff --git a/src/nix-setuid-helper/main.cc b/src/nix-setuid-helper/main.cc
index e2bab9e3e6fd..6fa87e3729e0 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 <username> <program>
              <args...> */
         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") {