diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f268f7d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +pidjail \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..146b925 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +all: pidjail + +pidjail: main.c + gcc -o $@ $^ + chmod u+s $@ diff --git a/main.c b/main.c new file mode 100644 index 0000000..12793bf --- /dev/null +++ b/main.c @@ -0,0 +1,121 @@ +// we need this so sched.h exports unshare and CLONE_* +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include + + +char** argdup(int argc, const char** argv) +{ + char** newargs = malloc(sizeof(char*) * (argc+1)); + for(size_t i = 0; i < argc; i++) + { + newargs[i] = strdup(argv[i]); + } + newargs[argc] = NULL; + return newargs; +} + +int main(int argc, const char** argv) +{ + if(argc == 1) { + printf("Usage: pidjail PROGRAM ARGUMENTS...\n" + "Run command within its own pid namespace. Integrated init process.\n"); + return 0; + } + + // next fork shall be in a new pid namespace + if (unshare(CLONE_NEWPID) != 0) + { + int err = errno; + 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; + } + + if (pid != 0) + { + // parent waits for child then exits + int status; + if(waitpid(pid, &status, 0) == -1) + { + int err = errno; + 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) + { + // Init process wait for anything and exit if first child exits. + pid_t first_child = pid; + pid_t exited_child; + int child_status; + int err; + do { + exited_child = wait(&child_status); + err = errno; + } while(exited_child != first_child && exited_child != -1); + + 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. + + // Drop root privileges + if (seteuid(getuid()) == -1) + { + int err = errno; + printf("Failed to drop root privileges with seteuid (%d)\n", err); + return err; + } + + if (setegid(getgid()) == -1) + { + int err = errno; + printf("Failed to drop root privileges with setegid (%d)\n", err); + return err; + } + + char** newargs = argdup(argc-1, &argv[1]); + + if (execv(newargs[0], newargs) == -1) + { + printf("Failed to exec (%d)\n", errno); + return errno; + } + } + } +}