2021-01-06 04:42:04 +01:00
|
|
|
// we need this so sched.h exports unshare and CLONE_*
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <sched.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <unistd.h>
|
2021-01-12 19:51:05 +01:00
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
pid_t pid_child;
|
2021-01-06 04:42:04 +01:00
|
|
|
|
2021-01-15 20:22:22 +01:00
|
|
|
void fatal(const char* str, int errcode)
|
|
|
|
{
|
|
|
|
printf("%s (%d)\n", str, errcode);
|
|
|
|
exit(errcode);
|
|
|
|
}
|
|
|
|
|
2021-01-15 20:22:22 +01:00
|
|
|
void drop_root(void)
|
|
|
|
{
|
2021-01-15 20:22:22 +01:00
|
|
|
/// Drop root privileges
|
|
|
|
// First group then user because we might not
|
|
|
|
// be able to drop group once we dropped user
|
|
|
|
gid_t gid = getgid();
|
|
|
|
if (setresgid(-1,gid,gid) == -1)
|
2021-01-15 20:22:22 +01:00
|
|
|
fatal("Failed to drop root privileges with setresgid", errno);
|
2021-01-12 19:50:07 +01:00
|
|
|
|
2021-01-15 20:22:22 +01:00
|
|
|
uid_t uid = getuid();
|
|
|
|
if (setresuid(-1,uid,uid) == -1)
|
2021-01-15 20:22:22 +01:00
|
|
|
fatal("Failed to drop root privileges with setresuid", errno);
|
2021-01-15 20:22:22 +01:00
|
|
|
|
|
|
|
// sanity check
|
2021-01-15 20:22:22 +01:00
|
|
|
if (seteuid(0) != -1)
|
2021-01-15 20:22:22 +01:00
|
|
|
fatal("Sanity check failed. Able to regain root", 42);
|
2021-01-12 19:50:07 +01:00
|
|
|
}
|
2021-01-06 04:42:04 +01:00
|
|
|
|
2021-01-15 20:22:22 +01:00
|
|
|
struct sigaction forward_signal_descriptor;
|
2021-01-12 19:51:05 +01:00
|
|
|
void forward_signal(int sig)
|
|
|
|
{
|
2021-01-15 20:22:22 +01:00
|
|
|
if (kill(pid_child, sig) == -1)
|
|
|
|
{
|
|
|
|
if (sig == SIGTERM)
|
|
|
|
exit(1);
|
|
|
|
}
|
2021-01-12 19:51:05 +01:00
|
|
|
}
|
|
|
|
|
2021-01-06 04:42:04 +01:00
|
|
|
char** argdup(int argc, const char** argv)
|
|
|
|
{
|
|
|
|
char** newargs = malloc(sizeof(char*) * (argc+1));
|
2021-01-15 20:22:22 +01:00
|
|
|
for (size_t i = 0; i < argc; i++)
|
2021-01-06 04:42:04 +01:00
|
|
|
{
|
|
|
|
newargs[i] = strdup(argv[i]);
|
|
|
|
}
|
|
|
|
newargs[argc] = NULL;
|
|
|
|
return newargs;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, const char** argv)
|
|
|
|
{
|
2021-01-15 20:22:22 +01:00
|
|
|
int err;
|
2021-01-15 20:22:22 +01:00
|
|
|
|
|
|
|
forward_signal_descriptor.sa_flags = SA_RESTART;
|
|
|
|
forward_signal_descriptor.sa_handler = &forward_signal;
|
|
|
|
|
2021-01-15 20:22:22 +01:00
|
|
|
if (argc == 1)
|
|
|
|
{
|
2021-01-15 20:22:22 +01:00
|
|
|
fprintf(stderr,"Usage: %s PROGRAM ARGUMENTS...\n"
|
|
|
|
"Run command within its own pid namespace. Integrated init process.\n",
|
|
|
|
argv[0]);
|
2021-01-15 20:22:22 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2021-01-06 04:42:04 +01:00
|
|
|
|
|
|
|
// next fork shall be in a new pid namespace
|
|
|
|
if (unshare(CLONE_NEWPID) != 0)
|
|
|
|
{
|
2021-01-15 20:22:22 +01:00
|
|
|
fatal("Failed to unshare pid namespace", errno);
|
2021-01-06 04:42:04 +01:00
|
|
|
}
|
|
|
|
|
2021-01-15 20:22:22 +01:00
|
|
|
// Drop root privileges, we only needed those for the unshare call.
|
|
|
|
drop_root();
|
|
|
|
|
2021-01-06 04:42:04 +01:00
|
|
|
pid_t pid = fork();
|
|
|
|
|
|
|
|
if (pid == -1)
|
|
|
|
{
|
2021-01-15 20:22:22 +01:00
|
|
|
fatal("Failed to fork", errno);
|
2021-01-06 04:42:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (pid != 0)
|
|
|
|
{
|
2021-01-15 20:22:22 +01:00
|
|
|
/// Head process
|
|
|
|
// Wait for the init process in the PID namespace to terminate and forward its exit code.
|
|
|
|
// Also forward SIGTERM signals towards that init process.
|
2021-01-12 19:51:05 +01:00
|
|
|
|
|
|
|
// Setup signal handler to forward SIGTERM
|
|
|
|
pid_child = pid;
|
2021-01-15 20:22:22 +01:00
|
|
|
if (sigaction(SIGTERM, &forward_signal_descriptor, NULL) == -1)
|
2021-01-15 20:22:22 +01:00
|
|
|
{
|
|
|
|
printf("Unable to setup signal handler in head\n");
|
|
|
|
}
|
2021-01-06 04:42:04 +01:00
|
|
|
// parent waits for child then exits
|
2021-01-15 20:22:22 +01:00
|
|
|
// Could be interrupt due to a signal. Retry in that case.
|
2021-01-06 04:42:04 +01:00
|
|
|
int status;
|
2021-01-15 20:22:22 +01:00
|
|
|
if (waitpid(pid, &status, 0) == -1)
|
2021-01-06 04:42:04 +01:00
|
|
|
{
|
2021-01-15 20:22:22 +01:00
|
|
|
fatal("Failed to wait for init process", errno);
|
2021-01-06 04:42:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return WEXITSTATUS(status);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Child should be in new pid namespace and
|
|
|
|
// functions as the the init process
|
|
|
|
// it needs to fork again then wait for any child.
|
|
|
|
// if the forked child exits then exit.
|
|
|
|
|
|
|
|
pid = fork();
|
2021-01-15 20:22:22 +01:00
|
|
|
if (pid == -1)
|
|
|
|
{
|
|
|
|
fatal("Failed to fork in init process", errno);
|
|
|
|
}
|
|
|
|
|
2021-01-06 04:42:04 +01:00
|
|
|
if (pid != 0)
|
|
|
|
{
|
2021-01-15 20:22:22 +01:00
|
|
|
/// Init process
|
|
|
|
// This part of the program runs as first process in the pid namespace
|
|
|
|
// When this terminates then Linux terminates all remaining processes
|
|
|
|
// in the PID namespace. As we want this to happen when our first child
|
|
|
|
// terminates, we wait for our first child to terminate before terminating
|
|
|
|
// ourselves.
|
|
|
|
// As first process in the PID namespace, this also functions as adopting parent
|
|
|
|
// for orphaned processes in the PID namespace and therefore has to wait for
|
|
|
|
// any child process and then check if the a child process that has terminated
|
|
|
|
// is the one we were waiting for.
|
|
|
|
|
2021-01-06 04:42:04 +01:00
|
|
|
pid_t first_child = pid;
|
|
|
|
pid_t exited_child;
|
|
|
|
int child_status;
|
2021-01-12 19:51:05 +01:00
|
|
|
|
|
|
|
// Setup forward for SIGTERM
|
|
|
|
pid_child = first_child;
|
2021-01-15 20:22:22 +01:00
|
|
|
if (sigaction(SIGTERM, &forward_signal_descriptor, NULL) == -1)
|
2021-01-15 20:22:22 +01:00
|
|
|
{
|
2021-01-15 20:22:22 +01:00
|
|
|
fatal("Unable to setup signal forward in init", 1);
|
2021-01-15 20:22:22 +01:00
|
|
|
}
|
2021-01-12 19:51:05 +01:00
|
|
|
|
2021-01-15 20:22:22 +01:00
|
|
|
// wait could be interrupt due to a signal. In that case just call wait again.
|
2021-01-06 04:42:04 +01:00
|
|
|
do {
|
|
|
|
exited_child = wait(&child_status);
|
|
|
|
err = errno;
|
2021-01-15 20:22:22 +01:00
|
|
|
} while (!(exited_child == first_child || (exited_child == -1 && err == ECHILD)));
|
2021-01-06 04:42:04 +01:00
|
|
|
|
|
|
|
if (exited_child == -1)
|
|
|
|
{
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int exit_code = WEXITSTATUS(child_status);
|
|
|
|
return exit_code;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// First child of init process. do exec here
|
|
|
|
// use cli arguments for subprocess. skip 0 as it's our programs name.
|
|
|
|
|
|
|
|
char** newargs = argdup(argc-1, &argv[1]);
|
|
|
|
|
2021-01-12 13:24:08 +01:00
|
|
|
if (execvp(newargs[0], newargs) == -1)
|
2021-01-06 04:42:04 +01:00
|
|
|
{
|
2021-01-15 20:22:22 +01:00
|
|
|
fatal("Failed to exec", errno);
|
2021-01-06 04:42:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|