| /* -*- Mode: C; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */ |
| |
| #ifndef NSUTILS_H |
| #define NSUTILS_H |
| |
| #include "util.h" |
| |
| static int try_setup_ns_internal(int ns_kind, int expect_to_be_root); |
| static inline int try_setup_ns_no_root(int ns_kind) { |
| return try_setup_ns_internal(ns_kind, 0); |
| } |
| |
| static inline int try_setup_ns(int ns_kind) { |
| return try_setup_ns_internal(ns_kind, 1); |
| } |
| |
| static int open_proc_file(pid_t pid, const char* file, int mode) { |
| char path[100]; |
| ssize_t n = snprintf(path, sizeof(path), "/proc/%d/%s", pid, file); |
| test_assert(n >= 0 && n < (ssize_t)sizeof(path)); |
| int fd = open(path, mode); |
| test_assert(fd != -1); |
| return fd; |
| } |
| |
| static int try_setup_ns_internal(int ns_kind, int expect_to_be_root) { |
| int err = -1; |
| errno = 0; |
| if (ns_kind != CLONE_NEWUSER) { |
| err = unshare(ns_kind); |
| } |
| if (ns_kind == CLONE_NEWUSER || err == -1) { |
| // Try again using user namespace functionality |
| test_assert(errno == 0 || errno == EPERM); |
| |
| int child_block[2]; |
| pipe(child_block); |
| |
| pid_t pid = getpid(); |
| pid_t child = fork(); |
| test_assert(child >= 0); |
| if (!child) { |
| char ch; |
| |
| close(child_block[1]); |
| |
| // This will block until the parent closes fds[1] |
| test_assert(0 == read(child_block[0], &ch, 1)); |
| close(child_block[0]); |
| |
| // Deny setgroups |
| int setgroups_fd = open_proc_file(pid, "setgroups", O_WRONLY); |
| char deny[] = "deny"; |
| test_assert(write(setgroups_fd, deny, sizeof(deny)) == sizeof(deny)); |
| close(setgroups_fd); |
| |
| // Make us root |
| ssize_t nbytes; |
| int uidmap_fd = open_proc_file(pid, "uid_map", O_WRONLY); |
| test_assert(uidmap_fd != -1); |
| char uidmap[100]; |
| if (expect_to_be_root) { |
| nbytes = |
| snprintf(uidmap, (ssize_t)sizeof(uidmap), "0\t%d\t1", getuid()); |
| } else { |
| nbytes = |
| snprintf(uidmap, (ssize_t)sizeof(uidmap), "1000\t%d\t1", getuid()); |
| } |
| test_assert(nbytes > 0 && nbytes <= (ssize_t)sizeof(uidmap)); |
| test_assert(write(uidmap_fd, uidmap, nbytes) == nbytes); |
| test_assert(uidmap_fd); |
| |
| int gidmap_fd = open_proc_file(pid, "gid_map", O_WRONLY); |
| test_assert(gidmap_fd != -1); |
| char gidmap[100]; |
| if (expect_to_be_root) { |
| nbytes = |
| snprintf(gidmap, (ssize_t)sizeof(gidmap), "0\t%d\t1", getgid()); |
| } else { |
| nbytes = |
| snprintf(gidmap, (ssize_t)sizeof(gidmap), "1000\t%d\t1", getgid()); |
| } |
| test_assert(nbytes > 0 && nbytes <= (ssize_t)sizeof(gidmap)); |
| test_assert(write(gidmap_fd, gidmap, nbytes) == nbytes); |
| |
| _exit(0); |
| } |
| close(child_block[0]); |
| |
| err = unshare(ns_kind | CLONE_NEWUSER); |
| |
| if (err == -1) { |
| // `EINVAL` is returned if the namespace is not supported/enabled. |
| test_assert(errno == EPERM || errno == EINVAL); |
| atomic_puts("Skipping test because namespaces are\n" |
| "not available at this privilege level"); |
| return -1; |
| } |
| |
| // Allow the child to configure this user namespace |
| close(child_block[1]); |
| |
| // Wait until our child has configured this namespace and has exited |
| int status = 0; |
| waitpid(child, &status, 0); |
| test_assert(WIFEXITED(status) && WEXITSTATUS(status) == 0); |
| } |
| |
| return 0; |
| } |
| |
| #endif // NSUTILS_H |