* Move killUser() to libutil so that the setuid helper can use it.

This commit is contained in:
Eelco Dolstra 2006-12-07 00:16:07 +00:00
parent 79875c5e42
commit 6a8e60913a
3 changed files with 82 additions and 51 deletions

View file

@ -352,6 +352,8 @@ public:
uid_t getUID(); uid_t getUID();
uid_t getGID(); uid_t getGID();
bool enabled();
}; };
@ -452,55 +454,28 @@ uid_t UserLock::getGID()
} }
static void killUser(uid_t uid) bool UserLock::enabled()
{ {
debug(format("killing all processes running under uid `%1%'") % uid); return uid != 0;
assert(uid != rootUserId); /* just to be safe... */
/* The system call kill(-1, sig) sends the signal `sig' to all
users to which the current process can send signals. So we
fork a process, switch to uid, and send a mass kill. */
Pid pid;
pid = fork();
switch (pid) {
case -1:
throw SysError("unable to fork");
case 0:
try { /* child */
if (setuid(uid) == -1) abort();
while (true) {
if (kill(-1, SIGKILL) == 0) break;
if (errno == ESRCH) break; /* no more processes */
if (errno != EINTR)
throw SysError(format("cannot kill processes for uid `%1%'") % uid);
} }
} catch (std::exception & e) {
std::cerr << format("killing processes beloging to uid `%1%': %1%\n") static bool amPrivileged()
% uid % e.what(); {
quickExit(1); return geteuid() == 0;
}
quickExit(0);
} }
/* parent */
if (pid.wait(true) != 0)
throw Error(format("cannot kill processes for uid `%1%'") % uid);
/* !!! We should really do some check to make sure that there are void killUserWrapped(uid_t uid)
no processes left running under `uid', but there is no portable {
way to do so (I think). The most reliable way may be `ps -eo if (amPrivileged())
uid | grep -q $uid'. */ killUser(uid);
else
/* !!! TODO */
printMsg(lvlError, "must kill");
} }
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -825,8 +800,8 @@ void DerivationGoal::buildDone()
malicious user from leaving behind a process that keeps files malicious user from leaving behind a process that keeps files
open and modifies them after they have been chown'ed to open and modifies them after they have been chown'ed to
root. */ root. */
if (buildUser.getUID() != 0) if (buildUser.enabled())
killUser(buildUser.getUID()); killUserWrapped(buildUser.getUID());
/* Close the read side of the logger pipe. */ /* Close the read side of the logger pipe. */
logPipe.readSide.close(); logPipe.readSide.close();
@ -1308,11 +1283,15 @@ void DerivationGoal::startBuilder()
/* Make sure that no other processes are executing under this /* Make sure that no other processes are executing under this
uid. */ uid. */
killUser(buildUser.getUID()); killUserWrapped(buildUser.getUID());
/* Change ownership of the temporary build directory. !!! gid */ /* Change ownership of the temporary build directory, if we're
if (chown(tmpDir.c_str(), buildUser.getUID(), (gid_t) -1) == -1) root. If we're not root, then the setuid helper will do it
just before it starts the builder. */
if (amPrivileged()) {
if (chown(tmpDir.c_str(), buildUser.getUID(), buildUser.getGID()) == -1)
throw SysError(format("cannot change ownership of `%1%'") % tmpDir); throw SysError(format("cannot change ownership of `%1%'") % tmpDir);
}
/* Check that the Nix store has the appropriate permissions, /* Check that the Nix store has the appropriate permissions,
i.e., owned by root and mode 1775 (sticky bit on so that i.e., owned by root and mode 1775 (sticky bit on so that
@ -1325,7 +1304,7 @@ void DerivationGoal::startBuilder()
((st.st_mode & S_IRWXG) != S_IRWXG) || ((st.st_mode & S_IRWXG) != S_IRWXG) ||
(st.st_gid != buildUser.getGID())) (st.st_gid != buildUser.getGID()))
throw Error(format( throw Error(format(
"builder does not have write permission to `%1%'; " "builder does not have write permission to `%2%'; "
"try `chgrp %1% %2%; chmod 1775 %2%'") "try `chgrp %1% %2%; chmod 1775 %2%'")
% buildUser.getGID() % nixStore); % buildUser.getGID() % nixStore);
} }

View file

@ -707,6 +707,53 @@ void Pid::setSeparatePG(bool separatePG)
} }
void killUser(uid_t uid)
{
debug(format("killing all processes running under uid `%1%'") % uid);
assert(uid != 0); /* just to be safe... */
/* The system call kill(-1, sig) sends the signal `sig' to all
users to which the current process can send signals. So we
fork a process, switch to uid, and send a mass kill. */
Pid pid;
pid = fork();
switch (pid) {
case -1:
throw SysError("unable to fork");
case 0:
try { /* child */
if (setuid(uid) == -1) abort();
while (true) {
if (kill(-1, SIGKILL) == 0) break;
if (errno == ESRCH) break; /* no more processes */
if (errno != EINTR)
throw SysError(format("cannot kill processes for uid `%1%'") % uid);
}
} catch (std::exception & e) {
std::cerr << format("killing processes beloging to uid `%1%': %1%\n")
% uid % e.what();
quickExit(1);
}
quickExit(0);
}
/* parent */
if (pid.wait(true) != 0)
throw Error(format("cannot kill processes for uid `%1%'") % uid);
/* !!! We should really do some check to make sure that there are
no processes left running under `uid', but there is no portable
way to do so (I think). The most reliable way may be `ps -eo
uid | grep -q $uid'. */
}
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////

View file

@ -224,6 +224,11 @@ public:
}; };
/* Kill all processes running under the specified uid by sending them
a SIGKILL. */
void killUser(uid_t uid);
/* Run a program and return its stdout in a string (i.e., like the /* Run a program and return its stdout in a string (i.e., like the
shell backtick operator). */ shell backtick operator). */
string runProgram(Path program); string runProgram(Path program);