#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <sched.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/fsuid.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <linux/mount.h>
#include <linux/types.h>
#include "kselftest_harness.h"
#include "wrappers.h"
#include "utils.h"
#define MAP_HOST 10000
#define MAP_RANGE 10000
#define UNMAPPED 50000
#ifndef MOUNT_ATTR_IDMAP
#define MOUNT_ATTR_IDMAP 0x00100000
#endif
#ifndef __NR_mount_setattr
#define __NR_mount_setattr 442
#endif
static inline int sys_mount_setattr(int dfd, const char *path,
unsigned int flags,
struct mount_attr *attr, size_t size)
{
return syscall(__NR_mount_setattr, dfd, path, flags, attr, size);
}
static int idmapped_clone(const char *path)
{
struct mount_attr attr = {
.attr_set = MOUNT_ATTR_IDMAP,
};
int fd_tree, userns_fd, ret;
fd_tree = sys_open_tree(AT_FDCWD, path,
OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC);
if (fd_tree < 0)
return -1;
userns_fd = get_userns_fd(MAP_HOST, 0, MAP_RANGE);
if (userns_fd < 0) {
close(fd_tree);
return -1;
}
attr.userns_fd = userns_fd;
ret = sys_mount_setattr(fd_tree, "", AT_EMPTY_PATH, &attr, sizeof(attr));
close(userns_fd);
if (ret) {
close(fd_tree);
return -1;
}
return fd_tree;
}
FIXTURE(idmapped_tmpfile) {
char dir[64];
};
FIXTURE_SETUP(idmapped_tmpfile)
{
ASSERT_EQ(unshare(CLONE_NEWNS), 0);
ASSERT_EQ(sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL), 0);
ASSERT_EQ(sys_mount("tmpfs", "/tmp", "tmpfs", 0, NULL), 0);
snprintf(self->dir, sizeof(self->dir), "/tmp/d");
ASSERT_EQ(mkdir(self->dir, 0777), 0);
ASSERT_EQ(chmod(self->dir, 0777), 0);
}
FIXTURE_TEARDOWN(idmapped_tmpfile)
{
}
TEST_F(idmapped_tmpfile, unmapped_caller_is_refused)
{
int mfd, fd;
mfd = idmapped_clone(self->dir);
if (mfd < 0)
SKIP(return, "idmapped mounts not supported");
setfsgid(UNMAPPED);
setfsuid(UNMAPPED);
ASSERT_EQ(setfsuid(-1), UNMAPPED);
fd = openat(mfd, ".", O_TMPFILE | O_WRONLY, 0644);
ASSERT_LT(fd, 0);
EXPECT_EQ(errno, EOVERFLOW);
if (fd >= 0)
close(fd);
EXPECT_EQ(close(mfd), 0);
}
TEST_F(idmapped_tmpfile, mapped_caller_creates_and_links)
{
char path[PATH_MAX];
struct stat st;
int mfd, fd;
mfd = idmapped_clone(self->dir);
if (mfd < 0)
SKIP(return, "idmapped mounts not supported");
fd = openat(mfd, ".", O_TMPFILE | O_RDWR, 0600);
ASSERT_GE(fd, 0);
ASSERT_EQ(fstat(fd, &st), 0);
EXPECT_EQ(st.st_uid, 0);
EXPECT_EQ(st.st_gid, 0);
ASSERT_EQ(linkat(fd, "", mfd, "linked", AT_EMPTY_PATH), 0);
EXPECT_EQ(close(fd), 0);
ASSERT_EQ(fstatat(mfd, "linked", &st, 0), 0);
EXPECT_EQ(st.st_uid, 0);
EXPECT_EQ(st.st_gid, 0);
snprintf(path, sizeof(path), "%s/linked", self->dir);
ASSERT_EQ(stat(path, &st), 0);
EXPECT_EQ(st.st_uid, MAP_HOST);
EXPECT_EQ(st.st_gid, MAP_HOST);
EXPECT_EQ(close(mfd), 0);
}
TEST_HARNESS_MAIN