Created
January 30, 2024 20:31
-
-
Save ig0rmin/10452d454cd9e269903dab70509b579d to your computer and use it in GitHub Desktop.
Clone a process into a new user namespace
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _GNU_SOURCE | |
#include <sched.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
#include <signal.h> | |
#include <fcntl.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <limits.h> | |
#include <sched.h> | |
#include <errno.h> | |
#include <sys/wait.h> | |
#include <sys/syscall.h> | |
static void waitChild(pid_t pid) { | |
int status = 0; | |
pid_t res = waitpid(pid, &status, 0); | |
if (res == -1) { | |
printf("Waitpid returned error, errno: %d\n", errno); | |
return; | |
} | |
if (WIFEXITED(status)) { | |
printf("Child %d exited with exit status %d\n", pid, WEXITSTATUS(status)); | |
} else if (WIFSIGNALED(status)) { | |
printf("Child %d was killed by a signal %d (core dumped: %d)\n", | |
pid, WTERMSIG(status), __WCOREDUMP(status)); | |
} else { | |
printf("error: unexpected waitpid result\n"); | |
} | |
} | |
static void writeFile(const char* fileName, const char* content) { | |
int fd = open(fileName, O_RDWR); | |
write(fd, content, strlen(content)); | |
close(fd); | |
} | |
static int _clone(int flags) { | |
return syscall(SYS_clone, flags, NULL, NULL, NULL, NULL); | |
} | |
#define MAP_BUFF_SIZE 128 | |
static int updateUIDmap(pid_t pid) { | |
char path[PATH_MAX] = {0}; | |
snprintf(path, PATH_MAX, "/proc/%d/uid_map", pid); | |
char userMap[MAP_BUFF_SIZE] = {0}; | |
snprintf(userMap, MAP_BUFF_SIZE, "0 %d 1", getuid()); | |
writeFile(path, userMap); | |
} | |
static int denySetgroups(pid_t pid) { | |
char path[PATH_MAX] = {0}; | |
snprintf(path, PATH_MAX, "/proc/%d/setgroups", pid); | |
writeFile(path, "deny"); | |
} | |
static int updateGIDmap(pid_t pid) { | |
denySetgroups(pid); | |
char path[PATH_MAX] = {0}; | |
snprintf(path, PATH_MAX, "/proc/%d/gid_map", pid); | |
char groupMap[MAP_BUFF_SIZE] = {0}; | |
snprintf(groupMap, MAP_BUFF_SIZE, "0 %d 1", getgid()); | |
writeFile(path, groupMap); | |
} | |
int main(int argc, char* argv[]) { | |
static int pipe_fd[2]; // The pipe used to synchronize parent and child | |
pipe(pipe_fd); | |
int pid = _clone(SIGCHLD | CLONE_NEWUSER | CLONE_NEWPID); | |
if (pid == -1) { | |
// Error | |
fprintf(stderr, "clone returned -1\n"); | |
exit(EXIT_FAILURE); | |
} else if (pid == 0) { | |
// Child | |
close(pipe_fd[1]); | |
char dummy; | |
read(pipe_fd[0], &dummy, 1); // Wait for the parent to close write end of the pipe | |
printf("Child, PID: %d\n", getpid()); | |
printf("Child, UID: %d\n", getuid()); | |
close(pipe_fd[0]); | |
exit(EXIT_SUCCESS); | |
} else { | |
printf("Parent, Child PID: %d\n", pid); | |
printf("Parent, UID: %d\n", getuid()); | |
updateUIDmap(pid); | |
updateGIDmap(pid); | |
close(pipe_fd[1]); // Signal child that maps are updated | |
close(pipe_fd[0]); | |
waitChild(pid); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment