Unverified Commit 466b8e7f by Stéphane Graber Committed by GitHub

Merge pull request #3397 from brauner/2020-05-03/fixes

conf: introduce userns_exec_mapped_root()
parents 33b8e598 cf68ffd9
......@@ -622,7 +622,7 @@ AC_CHECK_HEADER([ifaddrs.h],
AC_HEADER_MAJOR
# Check for some syscalls functions
AC_CHECK_FUNCS([setns pivot_root sethostname unshare rand_r confstr faccessat gettid memfd_create])
AC_CHECK_FUNCS([setns pivot_root sethostname unshare rand_r confstr faccessat gettid memfd_create move_mount open_tree])
# Check for strerror_r() support. Defines:
# - HAVE_STRERROR_R if available
......
......@@ -1576,7 +1576,8 @@ static int setup_personality(int persona)
}
static int lxc_setup_dev_console(const struct lxc_rootfs *rootfs,
const struct lxc_terminal *console)
const struct lxc_terminal *console,
int pts_mnt_fd)
{
int ret;
char path[PATH_MAX];
......@@ -1589,7 +1590,8 @@ static int lxc_setup_dev_console(const struct lxc_rootfs *rootfs,
if (ret < 0 || (size_t)ret >= sizeof(path))
return -1;
/* When we are asked to setup a console we remove any previous
/*
* When we are asked to setup a console we remove any previous
* /dev/console bind-mounts.
*/
if (file_exists(path)) {
......@@ -1600,7 +1602,8 @@ static int lxc_setup_dev_console(const struct lxc_rootfs *rootfs,
DEBUG("Cleared all (%d) mounts from \"%s\"", ret, path);
}
/* For unprivileged containers autodev or automounts will already have
/*
* For unprivileged containers autodev or automounts will already have
* taken care of creating /dev/console.
*/
ret = mknod(path, S_IFREG | 0000, 0);
......@@ -1611,17 +1614,20 @@ static int lxc_setup_dev_console(const struct lxc_rootfs *rootfs,
if (ret < 0)
return log_error_errno(-errno, errno, "Failed to set mode \"0%o\" to \"%s\"", S_IXUSR | S_IXGRP, console->name);
ret = safe_mount(console->name, path, "none", MS_BIND, 0, rootfs_path);
if (pts_mnt_fd >= 0)
ret = move_mount(pts_mnt_fd, "", -EBADF, path, MOVE_MOUNT_F_EMPTY_PATH);
else
ret = safe_mount(console->name, path, "none", MS_BIND, 0, rootfs_path);
if (ret < 0)
return log_error_errno(-1, errno, "Failed to mount \"%s\" on \"%s\"", console->name, path);
return log_error_errno(-1, errno, "Failed to mount %d(%s) on \"%s\"", pts_mnt_fd, console->name, path);
DEBUG("Mounted pts device \"%s\" onto \"%s\"", console->name, path);
DEBUG("Mounted pts device %d(%s) onto \"%s\"", pts_mnt_fd, console->name, path);
return 0;
}
static int lxc_setup_ttydir_console(const struct lxc_rootfs *rootfs,
const struct lxc_terminal *console,
char *ttydir)
char *ttydir, int pts_mnt_fd)
{
int ret;
char path[PATH_MAX], lxcpath[PATH_MAX];
......@@ -1669,9 +1675,12 @@ static int lxc_setup_ttydir_console(const struct lxc_rootfs *rootfs,
return log_error_errno(-errno, errno, "Failed to set mode \"0%o\" to \"%s\"", S_IXUSR | S_IXGRP, console->name);
/* bind mount console->name to '/dev/<ttydir>/console' */
ret = safe_mount(console->name, lxcpath, "none", MS_BIND, 0, rootfs_path);
if (pts_mnt_fd >= 0)
ret = move_mount(pts_mnt_fd, "", -EBADF, path, MOVE_MOUNT_F_EMPTY_PATH);
else
ret = safe_mount(console->name, lxcpath, "none", MS_BIND, 0, rootfs_path);
if (ret < 0)
return log_error_errno(-1, errno, "Failed to mount \"%s\" on \"%s\"", console->name, lxcpath);
return log_error_errno(-1, errno, "Failed to mount %d(%s) on \"%s\"", pts_mnt_fd, console->name, lxcpath);
DEBUG("Mounted \"%s\" onto \"%s\"", console->name, lxcpath);
/* bind mount '/dev/<ttydir>/console' to '/dev/console' */
......@@ -1685,13 +1694,14 @@ static int lxc_setup_ttydir_console(const struct lxc_rootfs *rootfs,
}
static int lxc_setup_console(const struct lxc_rootfs *rootfs,
const struct lxc_terminal *console, char *ttydir)
const struct lxc_terminal *console, char *ttydir,
int pts_mnt_fd)
{
if (!ttydir)
return lxc_setup_dev_console(rootfs, console);
return lxc_setup_dev_console(rootfs, console, pts_mnt_fd);
return lxc_setup_ttydir_console(rootfs, console, ttydir);
return lxc_setup_ttydir_console(rootfs, console, ttydir, pts_mnt_fd);
}
static int parse_mntopt(char *opt, unsigned long *flags, char **data, size_t size)
......@@ -2791,11 +2801,11 @@ int lxc_map_ids(struct lxc_list *idmap, pid_t pid)
return 0;
}
/* Return the host uid/gid to which the container root is mapped in val.
/*
* Return the host uid/gid to which the container root is mapped in val.
* Return true if id was found, false otherwise.
*/
static bool get_mapped_rootid(const struct lxc_conf *conf, enum idtype idtype,
unsigned long *val)
static id_t get_mapped_rootid(const struct lxc_conf *conf, enum idtype idtype)
{
unsigned nsid;
struct id_map *map;
......@@ -2812,11 +2822,13 @@ static bool get_mapped_rootid(const struct lxc_conf *conf, enum idtype idtype,
continue;
if (map->nsid != nsid)
continue;
*val = map->hostid;
return true;
return map->hostid;
}
return false;
if (idtype == ID_TYPE_UID)
return LXC_INVALID_UID;
return LXC_INVALID_GID;
}
int mapped_hostid(unsigned id, const struct lxc_conf *conf, enum idtype idtype)
......@@ -2873,7 +2885,6 @@ int chown_mapped_root_exec_wrapper(void *args)
int chown_mapped_root(const char *path, const struct lxc_conf *conf)
{
uid_t rootuid, rootgid;
unsigned long val;
int hostuid, hostgid, ret;
struct stat sb;
char map1[100], map2[100], map3[100], map4[100], map5[100];
......@@ -2895,17 +2906,15 @@ int chown_mapped_root(const char *path, const struct lxc_conf *conf)
NULL};
char cmd_output[PATH_MAX];
hostuid = geteuid();
hostgid = getegid();
if (!get_mapped_rootid(conf, ID_TYPE_UID, &val))
rootuid = get_mapped_rootid(conf, ID_TYPE_UID);
if (!uid_valid(rootuid))
return log_error(-1, "No uid mapping for container root");
rootuid = (uid_t)val;
if (!get_mapped_rootid(conf, ID_TYPE_GID, &val))
rootgid = get_mapped_rootid(conf, ID_TYPE_GID);
if (!gid_valid(rootgid))
return log_error(-1, "No gid mapping for container root");
rootgid = (gid_t)val;
hostuid = geteuid();
if (hostuid == 0) {
if (chown(path, rootuid, rootgid) < 0)
return log_error(-1, "Error chowning %s", path);
......@@ -2929,6 +2938,7 @@ int chown_mapped_root(const char *path, const struct lxc_conf *conf)
* A file has to be group-owned by a gid mapped into the
* container, or the container won't be privileged over it.
*/
hostgid = getegid();
DEBUG("trying to chown \"%s\" to %d", path, hostgid);
if (sb.st_uid == hostuid &&
mapped_hostid(sb.st_gid, conf, ID_TYPE_GID) < 0 &&
......@@ -3267,6 +3277,7 @@ static int lxc_setup_boot_id(void)
int lxc_setup(struct lxc_handler *handler)
{
__do_close int pts_mnt_fd = -EBADF;
int ret;
const char *lxcpath = handler->lxcpath, *name = handler->name;
struct lxc_conf *lxc_conf = handler->conf;
......@@ -3305,6 +3316,12 @@ int lxc_setup(struct lxc_handler *handler)
return log_error(-1, "Failed to send network device names and ifindices to parent");
}
pts_mnt_fd = open_tree(-EBADF, lxc_conf->console.name,
OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC | AT_EMPTY_PATH);
if (pts_mnt_fd < 0)
SYSTRACE("Failed to create detached mount for container's console \"%s\"",
lxc_conf->console.name);
if (lxc_conf->autodev > 0) {
ret = mount_autodev(name, &lxc_conf->rootfs, lxc_conf->autodevtmpfssize, lxcpath);
if (ret < 0)
......@@ -3377,8 +3394,12 @@ int lxc_setup(struct lxc_handler *handler)
if (!verify_start_hooks(lxc_conf))
return log_error(-1, "Failed to verify start hooks");
ret = lxc_create_tmp_proc_mount(lxc_conf);
if (ret < 0)
return log_error(-1, "Failed to \"/proc\" LSMs");
ret = lxc_setup_console(&lxc_conf->rootfs, &lxc_conf->console,
lxc_conf->ttys.dir);
lxc_conf->ttys.dir, pts_mnt_fd);
if (ret < 0)
return log_error(-1, "Failed to setup console");
......@@ -3386,10 +3407,6 @@ int lxc_setup(struct lxc_handler *handler)
if (ret < 0)
return log_error(-1, "Failed to setup \"/dev\" symlinks");
ret = lxc_create_tmp_proc_mount(lxc_conf);
if (ret < 0)
return log_error(-1, "Failed to \"/proc\" LSMs");
ret = lxc_setup_rootfs_switch_root(&lxc_conf->rootfs);
if (ret < 0)
return log_error(-1, "Failed to pivot root into rootfs");
......@@ -4420,6 +4437,224 @@ on_error:
return ret;
}
static int add_idmap_entry(struct lxc_list *idmap, enum idtype idtype,
unsigned long nsid, unsigned long hostid,
unsigned long range)
{
__do_free struct id_map *new_idmap = NULL;
__do_free struct lxc_list *new_list = NULL;
new_idmap = zalloc(sizeof(*new_idmap));
if (!new_idmap)
return ret_errno(ENOMEM);
new_idmap->idtype = idtype;
new_idmap->hostid = hostid;
new_idmap->nsid = nsid;
new_idmap->range = range;
new_list = zalloc(sizeof(*new_list));
if (!new_list)
return ret_errno(ENOMEM);
new_list->elem = move_ptr(new_idmap);
lxc_list_add_tail(idmap, move_ptr(new_list));
INFO("Adding id map: type %c nsid %lu hostid %lu range %lu",
idtype == ID_TYPE_UID ? 'u' : 'g', nsid, hostid, range);
return 0;
}
int userns_exec_mapped_root(const char *path, int path_fd,
const struct lxc_conf *conf)
{
call_cleaner(lxc_free_idmap) struct lxc_list *idmap = NULL;
__do_close int fd = -EBADF;
int target_fd = -EBADF;
char c = '1';
ssize_t ret;
pid_t pid;
int sock_fds[2];
uid_t container_host_uid, hostuid;
gid_t container_host_gid, hostgid;
struct stat st;
if (!conf || (!path && path_fd < 0))
return ret_errno(EINVAL);
if (!path)
path = "(null)";
container_host_uid = get_mapped_rootid(conf, ID_TYPE_UID);
if (!uid_valid(container_host_uid))
return log_error(-1, "No uid mapping for container root");
container_host_gid = get_mapped_rootid(conf, ID_TYPE_GID);
if (!gid_valid(container_host_gid))
return log_error(-1, "No gid mapping for container root");
if (path_fd < 0) {
fd = open(path, O_RDWR | O_CLOEXEC | O_NOCTTY | O_PATH);
if (fd < 0)
return log_error_errno(-errno, errno, "Failed to open \"%s\"", path);
target_fd = fd;
} else {
target_fd = path_fd;
}
hostuid = geteuid();
/* We are root so chown directly. */
if (hostuid == 0) {
ret = fchown(target_fd, container_host_uid, container_host_gid);
if (ret)
return log_error_errno(-errno, errno,
"Failed to fchown(%d(%s), %d, %d)",
target_fd, path, container_host_uid,
container_host_gid);
return log_trace(0, "Chowned %d(%s) to uid %d and %d", target_fd, path,
container_host_uid, container_host_gid);
}
/* The container's root host id matches */
if (container_host_uid == hostuid)
return log_info(0, "Container root id is mapped to our uid");
/* Get the current ids of our target. */
ret = fstat(target_fd, &st);
if (ret)
return log_error_errno(-errno, errno, "Failed to stat \"%s\"", path);
hostgid = getegid();
if (st.st_uid == hostuid && mapped_hostid(st.st_gid, conf, ID_TYPE_GID) < 0) {
ret = fchown(target_fd, -1, hostgid);
if (ret)
return log_error_errno(-errno, errno,
"Failed to fchown(%d(%s), -1, %d)",
target_fd, path, hostgid);
}
idmap = malloc(sizeof(*idmap));
if (!idmap)
return -ENOMEM;
lxc_list_init(idmap);
/* "u:0:rootuid:1" */
ret = add_idmap_entry(idmap, ID_TYPE_UID, 0, container_host_uid, 1);
if (ret < 0)
return log_error_errno(ret, -ret, "Failed to add idmap entry");
/* "u:hostuid:hostuid:1" */
ret = add_idmap_entry(idmap, ID_TYPE_UID, hostuid, hostuid, 1);
if (ret < 0)
return log_error_errno(ret, -ret, "Failed to add idmap entry");
/* "g:0:rootgid:1" */
ret = add_idmap_entry(idmap, ID_TYPE_GID, 0, container_host_gid, 1);
if (ret < 0)
return log_error_errno(ret, -ret, "Failed to add idmap entry");
/* "g:hostgid:hostgid:1" */
ret = add_idmap_entry(idmap, ID_TYPE_GID, hostgid, hostgid, 1);
if (ret < 0)
return log_error_errno(ret, -ret, "Failed to add idmap entry");
if (hostgid != st.st_gid) {
/* "g:pathgid:rootgid+pathgid:1" */
ret = add_idmap_entry(idmap, ID_TYPE_GID, st.st_gid,
container_host_gid + (gid_t)st.st_gid, 1);
if (ret < 0)
return log_error_errno(ret, -ret, "Failed to add idmap entry");
}
ret = socketpair(PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0, sock_fds);
if (ret < 0)
return -errno;
pid = fork();
if (pid < 0) {
SYSERROR("Failed to create new process");
goto on_error;
}
if (pid == 0) {
close_prot_errno_disarm(sock_fds[1]);
ret = unshare(CLONE_NEWUSER);
if (ret < 0) {
SYSERROR("Failed to unshare new user namespace");
_exit(EXIT_FAILURE);
}
ret = lxc_write_nointr(sock_fds[0], &c, 1);
if (ret != 1)
_exit(EXIT_FAILURE);
ret = lxc_read_nointr(sock_fds[0], &c, 1);
if (ret != 1)
_exit(EXIT_FAILURE);
close_prot_errno_disarm(sock_fds[0]);
if (!lxc_switch_uid_gid(0, 0))
_exit(EXIT_FAILURE);
if (!lxc_setgroups(0, NULL))
_exit(EXIT_FAILURE);
ret = chown(path, 0, st.st_gid);
if (ret) {
SYSERROR("Failed to chown \"%s\"", path);
_exit(EXIT_FAILURE);
}
_exit(EXIT_SUCCESS);
}
close_prot_errno_disarm(sock_fds[0]);
if (lxc_log_get_level() == LXC_LOG_LEVEL_TRACE ||
conf->loglevel == LXC_LOG_LEVEL_TRACE) {
struct id_map *map;
struct lxc_list *it;
lxc_list_for_each(it, idmap) {
map = it->elem;
TRACE("Establishing %cid mapping for \"%d\" in new user namespace: nsuid %lu - hostid %lu - range %lu",
(map->idtype == ID_TYPE_UID) ? 'u' : 'g', pid, map->nsid, map->hostid, map->range);
}
}
ret = lxc_read_nointr(sock_fds[1], &c, 1);
if (ret != 1) {
SYSERROR("Failed waiting for child process %d\" to tell us to proceed", pid);
goto on_error;
}
/* Set up {g,u}id mapping for user namespace of child process. */
ret = lxc_map_ids(idmap, pid);
if (ret < 0) {
ERROR("Error setting up {g,u}id mappings for child process \"%d\"", pid);
goto on_error;
}
/* Tell child to proceed. */
ret = lxc_write_nointr(sock_fds[1], &c, 1);
if (ret != 1) {
SYSERROR("Failed telling child process \"%d\" to proceed", pid);
goto on_error;
}
on_error:
close_prot_errno_disarm(sock_fds[0]);
close_prot_errno_disarm(sock_fds[1]);
/* Wait for child to finish. */
if (pid < 0)
return -1;
return wait_for_pid(pid);
}
/* not thread-safe, do not use from api without first forking */
static char *getuname(void)
{
......
......@@ -473,5 +473,7 @@ extern int lxc_clear_namespace(struct lxc_conf *c);
extern int userns_exec_minimal(const struct lxc_conf *conf,
int (*fn_parent)(void *), void *fn_parent_data,
int (*fn_child)(void *), void *fn_child_data);
extern int userns_exec_mapped_root(const char *path, int path_fd,
const struct lxc_conf *conf);
#endif /* __LXC_CONF_H */
......@@ -57,6 +57,20 @@
#define CAP_SETGID 6
#endif
/* move_mount */
#ifndef MOVE_MOUNT_F_EMPTY_PATH
#define MOVE_MOUNT_F_EMPTY_PATH 0x00000004 /* Empty from path permitted */
#endif
/* open_tree */
#ifndef OPEN_TREE_CLONE
#define OPEN_TREE_CLONE 1 /* Clone the target tree and attach the clone */
#endif
#ifndef OPEN_TREE_CLOEXEC
#define OPEN_TREE_CLOEXEC O_CLOEXEC /* Close the file on execve() */
#endif
/* prctl */
#ifndef PR_CAPBSET_READ
#define PR_CAPBSET_READ 23
......
......@@ -501,4 +501,40 @@
#endif
#endif
#ifndef __NR_move_mount
#if defined __alpha__
#define __NR_move_mount 539
#elif defined _MIPS_SIM
#if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */
#define __NR_move_mount 4429
#endif
#if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */
#define __NR_move_mount 6429
#endif
#if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */
#define __NR_move_mount 5429
#endif
#else
#define __NR_move_mount 429
#endif
#endif
#ifndef __NR_open_tree
#if defined __alpha__
#define __NR_open_tree 538
#elif defined _MIPS_SIM
#if _MIPS_SIM == _MIPS_SIM_ABI32 /* o32 */
#define __NR_open_tree 4428
#endif
#if _MIPS_SIM == _MIPS_SIM_NABI32 /* n32 */
#define __NR_open_tree 6428
#endif
#if _MIPS_SIM == _MIPS_SIM_ABI64 /* n64 */
#define __NR_open_tree 5428
#endif
#else
#define __NR_open_tree 428
#endif
#endif
#endif /* __LXC_SYSCALL_NUMBERS_H */
......@@ -137,4 +137,28 @@ static int faccessat(int __fd, const char *__file, int __type, int __flag)
}
#endif
#ifndef HAVE_MOVE_MOUNT
static inline int move_mount_lxc(int from_dfd, const char *from_pathname,
int to_dfd, const char *to_pathname,
unsigned int flags)
{
return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd,
to_pathname, flags);
}
#define move_mount move_mount_lxc
#else
extern int move_mount(int from_dfd, const char *from_pathname, int to_dfd,
const char *to_pathname, unsigned int flags);
#endif
#ifndef HAVE_OPEN_TREE
static inline int open_tree_lxc(int dfd, const char *filename, unsigned int flags)
{
return syscall(__NR_open_tree, dfd, filename, flags);
}
#define open_tree open_tree_lxc
#else
extern int open_tree(int dfd, const char *filename, unsigned int flags);
#endif
#endif /* __LXC_SYSCALL_WRAPPER_H */
......@@ -1167,13 +1167,16 @@ int lxc_terminal_map_ids(struct lxc_conf *c, struct lxc_terminal *terminal)
if (strcmp(terminal->name, "") == 0)
return 0;
ret = chown_mapped_root(terminal->name, c);
if (terminal->slave >= 0)
ret = userns_exec_mapped_root(terminal->name, terminal->slave, c);
else
ret = userns_exec_mapped_root(terminal->name, terminal->slave, c);
if (ret < 0) {
ERROR("Failed to chown terminal \"%s\"", terminal->name);
return -1;
return log_error(-1, "Failed to chown terminal %d(%s)",
terminal->slave, terminal->name);
}
TRACE("Chowned terminal \"%s\"", terminal->name);
TRACE("Chowned terminal %d(%s)", terminal->slave, terminal->name);
return 0;
}
......@@ -241,4 +241,14 @@ extern bool lxc_can_use_pidfd(int pidfd);
extern int fix_stdio_permissions(uid_t uid);
static inline bool uid_valid(uid_t uid)
{
return uid != LXC_INVALID_UID;
}
static inline bool gid_valid(gid_t gid)
{
return gid != LXC_INVALID_GID;
}
#endif /* __LXC_UTILS_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment