pidjail/main.c

180 lines
4.6 KiB
C
Raw Normal View History

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>
#include <signal.h>
pid_t pid_child;
2021-01-06 04:42:04 +01:00
2021-01-15 20:22:22 +01:00
void drop_root(void)
{
/// 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)
{
int err = errno;
printf("Failed to drop root privileges with setresgid (%d)\n", err);
exit(err);
}
uid_t uid = getuid();
if (setresuid(-1,uid,uid) == -1)
{
int err = errno;
printf("Failed to drop root privileges with setresuid (%d)\n", err);
exit(err);
}
// sanity check
2021-01-15 20:22:22 +01:00
if (seteuid(0) != -1)
{
printf("Sanity check failed. I was able to regain root.\n");
exit(1);
}
}
2021-01-06 04:42:04 +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-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)
{
int err;
2021-01-15 20:22:22 +01:00
if (argc == 1)
{
printf("Usage: pidjail PROGRAM ARGUMENTS...\n"
"Run command within its own pid namespace. Integrated init process.\n");
return 0;
}
2021-01-06 04:42:04 +01:00
// next fork shall be in a new pid namespace
if (unshare(CLONE_NEWPID) != 0)
{
err = errno;
2021-01-06 04:42:04 +01:00
printf("Failed to unshare pid namespace (%d)\n", err);
return err;
}
pid_t pid = fork();
if (pid == -1)
{
int err = errno;
printf("Failed to fork (%d)\n", err);
return err;
}
// Drop root privileges, we only needed those for the unshare call and fork above.
drop_root();
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.
// Setup signal handler to forward SIGTERM
pid_child = pid;
2021-01-15 20:22:22 +01:00
if (signal(SIGTERM, forward_signal) == SIG_ERR)
{
printf("Unable to setup signal handler in head\n");
}
2021-01-06 04:42:04 +01:00
// parent waits for child then exits
int status;
2021-01-15 20:22:22 +01:00
if (waitpid(pid, &status, 0) == -1)
2021-01-06 04:42:04 +01:00
{
err = errno;
2021-01-06 04:42:04 +01:00
printf("Failed to wait (%d)\n", err);
return err;
}
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();
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;
// Setup forward for SIGTERM
pid_child = first_child;
2021-01-15 20:22:22 +01:00
if (signal(SIGTERM, forward_signal) == SIG_ERR)
{
printf("Unable to setup signal forward in init. Aborting.\n");
return 1;
}
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);
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]);
if (execvp(newargs[0], newargs) == -1)
2021-01-06 04:42:04 +01:00
{
printf("Failed to exec (%d)\n", err);
return err;
2021-01-06 04:42:04 +01:00
}
}
}
}