#define _GNU_SOURCE /* Required for CLONE_NEWNS */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void die_with_error (const char *format, ...) { va_list args; int errsv; errsv = errno; va_start (args, format); vfprintf (stderr, format, args); va_end (args); fprintf (stderr, ": %s\n", strerror (errsv)); exit (1); } static void die (const char *format, ...) { va_list args; va_start (args, format); vfprintf (stderr, format, args); va_end (args); fprintf (stderr, "\n"); exit (1); } static char* strdup_printf (const char *format, ...) { char *buffer = NULL; va_list args; va_start (args, format); vasprintf (&buffer, format, args); va_end (args); if (buffer == NULL) die ("oom"); return buffer; } static inline int raw_clone(unsigned long flags, void *child_stack) { #if defined(__s390__) || defined(__CRIS__) /* On s390 and cris the order of the first and second arguments * of the raw clone() system call is reversed. */ return (int) syscall(__NR_clone, child_stack, flags); #else return (int) syscall(__NR_clone, flags, child_stack); #endif } static int write_to_file (int fd, const char *content) { ssize_t len = strlen (content); ssize_t res; while (len > 0) { res = write (fd, content, len); if (res < 0 && errno == EINTR) continue; if (res <= 0) return -1; len -= res; content += res; } return 0; } static int write_file (const char *path, const char *content) { int fd; int res; fd = open (path, O_RDWR | O_CLOEXEC, 0); if (fd == -1) return -1; res = 0; if (content) res = write_to_file (fd, content); close (fd); return res; } int main (int argc, char **argv) { char *args[] = { "/bin/sh", NULL }; pid_t pid; char *uid_map, *gid_map; int uid, gid; uid = getuid(); gid = getgid(); pid = raw_clone (SIGCHLD | CLONE_NEWUSER | CLONE_NEWNS, NULL); if (pid == -1) die_with_error ("Creating new namespace failed"); if (pid != 0) { int status; wait(&status); exit (0); /* Should not be reached, but better safe... */ } if (write_file("/proc/self/setgroups", "deny\n") < 0) die_with_error ("error writing to setgroups"); uid_map = strdup_printf ("%d %d 1\n", uid, uid); if (write_file ("/proc/self/uid_map", uid_map) < 0) die_with_error ("setting up uid map"); free (uid_map); gid_map = strdup_printf ("%d %d 1\n", gid, gid); if (write_file ("/proc/self/gid_map", gid_map) < 0) { int errsv = errno; fprintf (stderr, "error writing to gid_map: %s, content: %s", strerror (errsv), gid_map); } free (gid_map); if (mkdir ("/tmp/foo", 0755) && errno != EEXIST) die_with_error ("unable to create tmp"); if (mount ("", "/tmp/foo", "tmpfs", MS_NODEV|MS_NOEXEC|MS_NOSUID, NULL) != 0) die_with_error ("Failed to mount tmpfs"); if (mkdir ("/tmp/foo/devpts", 0755)) die_with_error ("unable to mount devpts"); if (mount ("", "/tmp/foo/devpts", "devpts", MS_NOEXEC|MS_NOSUID, "newinstance,ptmxuid=1000,ptmxgid=1000") != 0) { int errsv = errno; fprintf (stderr, "error mounting devpts: %s\n", strerror (errsv)); } if (execv ("/bin/sh", args) == -1) die_with_error ("execvp %s", args[0]); printf ("end??\n"); return 1; }