Skip to content
Merged
160 changes: 123 additions & 37 deletions jail/fs.c
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,31 @@ unsigned long detect_atime_flag(const char *mountpoint)
#define MOUNT_ATTR_NODIRATIME 0x00000080
#endif

/* MS_* -> MOUNT_ATTR_* for fsmount()/mount_setattr() */
static unsigned mountflags_to_attr(unsigned long mountflags)
{
unsigned attr = 0;

if (mountflags & MS_RDONLY)
attr |= MOUNT_ATTR_RDONLY;
if (mountflags & MS_NOSUID)
attr |= MOUNT_ATTR_NOSUID;
if (mountflags & MS_NODEV)
attr |= MOUNT_ATTR_NODEV;
if (mountflags & MS_NOEXEC)
attr |= MOUNT_ATTR_NOEXEC;
if (mountflags & MS_NODIRATIME)
attr |= MOUNT_ATTR_NODIRATIME;
if (mountflags & MS_NOATIME)
attr |= MOUNT_ATTR_NOATIME;
else if (mountflags & MS_STRICTATIME)
attr |= MOUNT_ATTR_STRICTATIME;
else
attr |= MOUNT_ATTR_RELATIME;

return attr;
}

int sys_openat2(int dfd, const char *path, struct open_how *how, size_t size)
{
return syscall(SYS_openat2, dfd, path, how, size);
Expand Down Expand Up @@ -312,6 +337,21 @@ int sys_move_mount(int from_dfd, const char *from_path, int to_dfd, const char *
return syscall(SYS_move_mount, from_dfd, from_path, to_dfd, to_path, flags);
}

int sys_fsopen(const char *fsname, unsigned flags)
{
return syscall(SYS_fsopen, fsname, flags);
}

int sys_fsconfig(int fd, unsigned cmd, const char *key, const void *value, int aux)
{
return syscall(SYS_fsconfig, fd, cmd, key, value, aux);
}

int sys_fsmount(int fd, unsigned flags, unsigned attr_flags)
{
return syscall(SYS_fsmount, fd, flags, attr_flags);
}

int sys_mount_setattr(int dfd, const char *path, unsigned flags, struct ujail_mount_attr *attr, size_t size)
{
return syscall(SYS_mount_setattr, dfd, path, flags, attr, size);
Expand Down Expand Up @@ -339,7 +379,7 @@ int mask_path_now(const char *path)
} else {
if (mount(JAIL_NOAFILE, path, "bind", MS_BIND, NULL))
return -1;
if (mount(JAIL_NOAFILE, path, "bind", MS_REMOUNT | MS_BIND | MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RELATIME, NULL))
if (remount_readonly(path, MS_NOSUID | MS_NOEXEC | MS_NODEV))
return -1;
}

Expand All @@ -363,9 +403,13 @@ static void mountinfo_unescape(char *s)
*w = '\0';
}

/*
* The flags a locked mount will not let a remount clear. The atime class is
* deliberately absent: path_mount() preserves it when the remount names none.
*/
static unsigned long mountinfo_current_flags(const char *path)
{
unsigned long flags = MS_RELATIME;
unsigned long flags = 0;
bool found = false;
FILE *f;
char *line = NULL;
Expand Down Expand Up @@ -403,12 +447,6 @@ static unsigned long mountinfo_current_flags(const char *path)
this_flags |= MS_NODEV;
else if (!strcmp(tok, "noexec"))
this_flags |= MS_NOEXEC;
else if (!strcmp(tok, "noatime"))
this_flags |= MS_NOATIME;
else if (!strcmp(tok, "relatime"))
this_flags |= MS_RELATIME;
else if (!strcmp(tok, "nodiratime"))
this_flags |= MS_NODIRATIME;
}

/* last match wins: it's the topmost/effective entry */
Expand All @@ -419,11 +457,32 @@ static unsigned long mountinfo_current_flags(const char *path)
fclose(f);

if (!found)
return MS_RELATIME;
return 0;

return flags;
}

/*
* Self-bind @path and remount it read-only, preserving the flags already in
* effect. Mounts copied in by unshare(CLONE_NEWNS) under a userns that does
* not own them are MNT_LOCK_{NOSUID,NODEV,NOEXEC,ATIME}; a remount clearing
* any of those fails with EPERM.
*/
int remount_readonly(const char *path, unsigned long flags)
{
flags |= MS_REMOUNT | MS_BIND | MS_RDONLY | mountinfo_current_flags(path);

return mount(NULL, path, NULL, flags, NULL);
}

int bind_remount_readonly(const char *path, unsigned long flags)
{
if (mount(path, path, "bind", MS_BIND | (flags & MS_REC), NULL))
return -1;

return remount_readonly(path, flags);
}

static bool fs_userns;

void jail_fs_set_userns(bool enabled)
Expand All @@ -445,7 +504,7 @@ static int do_mount(const char *root, const char *orig_source, const char *targe
char devpts_data[512];
const char *mount_data;
char *source = (char *)orig_source;
int fd, ret = 0;
int fd, err, ret = 0;
bool is_bind = (orig_mountflags & MS_BIND);
bool is_mask = (source == (void *)(-1));
bool use_fd = false;
Expand Down Expand Up @@ -475,15 +534,21 @@ static int do_mount(const char *root, const char *orig_source, const char *targe
return 0; /* doesn't exists, nothing to mask */

if (S_ISDIR(s.st_mode)) {/* use empty 0-sized tmpfs for directories */
if (mount("none", new, "tmpfs", MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RELATIME, "size=0,mode=000"))
return error;
err = mount("none", new, "tmpfs", MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RELATIME, "size=0,mode=000");
} else {
/* mount-bind 0-sized file having mode 000 */
if (mount(UJAIL_NOAFILE, new, "bind", MS_BIND, NULL))
return error;
err = mount(UJAIL_NOAFILE, new, "bind", MS_BIND, NULL);
if (!err)
err = remount_readonly(new, MS_NOSUID | MS_NOEXEC | MS_NODEV);
}

if (mount(UJAIL_NOAFILE, new, "bind", MS_REMOUNT | MS_BIND | MS_RDONLY | MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_RELATIME, NULL))
return error;
if (err) {
if (error)
ERROR("failed to mask %s: %m\n", new);
else
WARNING("could not mask optional path %s: %m\n",
new);
return error;
}

DEBUG("masked path %s\n", new);
Expand Down Expand Up @@ -744,6 +809,39 @@ int add_mount_fd(int fd, const char *target, int error)
return 0;
}

/*
* sysfs needs CAP_SYS_ADMIN in the userns owning the netns. fsmount() the
* queued sysfs entries here, before clone(), for do_mount_fd().
*/
int premount_sysfs(void)
{
struct mount *m;
int fsfd, mfd;

list_for_each_entry(m, &mounts_order, list) {
if (!m->filesystemtype || strcmp(m->filesystemtype, "sysfs") ||
m->source_fd >= 0)
continue;

fsfd = sys_fsopen("sysfs", FSOPEN_CLOEXEC);
if (fsfd < 0)
return -1;
if (sys_fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0)) {
close(fsfd);
return -1;
}
mfd = sys_fsmount(fsfd, FSMOUNT_CLOEXEC, mountflags_to_attr(m->mountflags));
Comment thread
joshuacov1 marked this conversation as resolved.
close(fsfd);
if (mfd < 0)
return -1;

m->source_fd = mfd;
DEBUG("pre-mounted sysfs for %s as fd:%d\n", m->target, mfd);
}

return 0;
}

int add_mount_volume(const char *source, const char *target, int error)
{
struct mount *m;
Expand Down Expand Up @@ -1316,26 +1414,10 @@ static int idmap_tree_fd(const char *source, int source_fd, int userns_fd,
return -1;
}

attr.attr_set = MOUNT_ATTR_IDMAP;
if (mountflags & MS_RDONLY)
attr.attr_set |= MOUNT_ATTR_RDONLY;
if (mountflags & MS_NOSUID)
attr.attr_set |= MOUNT_ATTR_NOSUID;
if (mountflags & MS_NODEV)
attr.attr_set |= MOUNT_ATTR_NODEV;
if (mountflags & MS_NOEXEC)
attr.attr_set |= MOUNT_ATTR_NOEXEC;
if (mountflags & MS_NODIRATIME)
attr.attr_set |= MOUNT_ATTR_NODIRATIME;
if (mountflags & (MS_NOATIME | MS_RELATIME | MS_STRICTATIME)) {
/* keep the (possibly locked) atime mode unless one is requested */
attr.attr_set = MOUNT_ATTR_IDMAP | mountflags_to_attr(mountflags);
if (mountflags & (MS_NOATIME | MS_RELATIME | MS_STRICTATIME))
attr.attr_clr |= MOUNT_ATTR__ATIME;
if (mountflags & MS_NOATIME)
attr.attr_set |= MOUNT_ATTR_NOATIME;
else if (mountflags & MS_STRICTATIME)
attr.attr_set |= MOUNT_ATTR_STRICTATIME;
else
attr.attr_set |= MOUNT_ATTR_RELATIME;
}
attr.propagation = propagation;
attr.userns_fd = userns_fd;

Expand Down Expand Up @@ -1581,6 +1663,7 @@ static int mount_one(const char *jailroot, const char *jail_dev, struct mount *m
{
char devtarget[PATH_MAX];
struct mount *outer;
int ret;

if (m->mounted)
return 0;
Expand Down Expand Up @@ -1611,8 +1694,11 @@ static int mount_one(const char *jailroot, const char *jail_dev, struct mount *m
if (m->idmap)
return do_idmap_mount(jailroot, m) ? -1 : 0;

if (m->source_fd >= 0)
return do_mount_fd(jailroot, m->source_fd, m->target, m->error) ? -1 : 0;
if (m->source_fd >= 0) {
ret = do_mount_fd(jailroot, m->source_fd, m->target, m->error);
m->source_fd = -1;
return ret ? -1 : 0;
}

if (do_mount(jailroot, m->source, m->target, m->filesystemtype, m->mountflags,
m->propflags, m->optstr, m->error, m->inner, m->source_fd))
Expand Down
25 changes: 25 additions & 0 deletions jail/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ int fs_mount_enable_idmap(const char *target, uint32_t uid, uint32_t gid);
char *resolve_mount_source(const char *source);
int add_mount_fd(int fd, const char *target, int error);
int mask_path_now(const char *path);
int remount_readonly(const char *path, unsigned long flags);
int bind_remount_readonly(const char *path, unsigned long flags);

/* open_tree()/mount_setattr() wrappers - no glibc wrappers yet.
* Fields must match the kernel's struct mount_attr layout exactly
Expand All @@ -57,6 +59,29 @@ int sys_open_tree(int dfd, const char *path, unsigned flags);
int sys_move_mount(int from_dfd, const char *from_path, int to_dfd,
const char *to_path, unsigned flags);
int sys_mount_setattr(int dfd, const char *path, unsigned flags, struct ujail_mount_attr *attr, size_t size);
int sys_fsopen(const char *fsname, unsigned flags);
int sys_fsconfig(int fd, unsigned cmd, const char *key, const void *value, int aux);
int sys_fsmount(int fd, unsigned flags, unsigned attr_flags);
int premount_sysfs(void);

#ifndef FSOPEN_CLOEXEC
#define FSOPEN_CLOEXEC 0x00000001
#endif
#ifndef FSMOUNT_CLOEXEC
#define FSMOUNT_CLOEXEC 0x00000001
#endif
#ifndef FSCONFIG_CMD_CREATE
#define FSCONFIG_CMD_CREATE 6
#endif
#ifndef MOUNT_ATTR_NOSUID
#define MOUNT_ATTR_NOSUID 0x00000002
#endif
#ifndef MOUNT_ATTR_NODEV
#define MOUNT_ATTR_NODEV 0x00000004
#endif
#ifndef MOUNT_ATTR_NOEXEC
#define MOUNT_ATTR_NOEXEC 0x00000008
#endif

#ifndef OPEN_TREE_CLONE
#define OPEN_TREE_CLONE 1
Expand Down
Loading
Loading