Skip to content

Instantly share code, notes, and snippets.

@ig0rmin
Created January 30, 2024 20:31
Show Gist options
  • Save ig0rmin/10452d454cd9e269903dab70509b579d to your computer and use it in GitHub Desktop.
Save ig0rmin/10452d454cd9e269903dab70509b579d to your computer and use it in GitHub Desktop.
Clone a process into a new user namespace
#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