blob: cf54db32181e7d641caa0e5d2d8e914b9b644ba8 [file] [log] [blame]
/* -*- 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