[lxc-devel] [lxc/master] conf: introduce userns_exec_mapped_root()
brauner on Github
lxc-bot at linuxcontainers.org
Mon May 4 08:57:12 UTC 2020
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 435 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20200504/2f3da02c/attachment.bin>
-------------- next part --------------
From 234998b4f2a42623565f58b0ad20fa8254cbedde Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner at ubuntu.com>
Date: Mon, 4 May 2020 10:56:05 +0200
Subject: [PATCH] conf: introduce userns_exec_mapped_root()
to avoid the overhead of calling to lxc-usernsexec whenever we can.
Signed-off-by: Christian Brauner <christian.brauner at ubuntu.com>
---
src/lxc/conf.c | 246 ++++++++++++++++++++++++++++++++++++++++++---
src/lxc/conf.h | 2 +
src/lxc/terminal.c | 11 +-
src/lxc/utils.h | 10 ++
4 files changed, 251 insertions(+), 18 deletions(-)
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 20d1583fc4..90d464f686 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -2791,11 +2791,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 +2812,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 +2875,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 +2896,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 +2928,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 &&
@@ -4420,6 +4420,224 @@ int userns_exec_full(struct lxc_conf *conf, int (*fn)(void *), void *data,
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 = open(path, O_RDWR | O_CLOEXEC | O_NOCTTY);
+ 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)
{
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index 3ff226b729..346b736e17 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -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 */
diff --git a/src/lxc/terminal.c b/src/lxc/terminal.c
index 1b170cabe8..c2516b205a 100644
--- a/src/lxc/terminal.c
+++ b/src/lxc/terminal.c
@@ -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;
}
diff --git a/src/lxc/utils.h b/src/lxc/utils.h
index 339217c506..45ca5270de 100644
--- a/src/lxc/utils.h
+++ b/src/lxc/utils.h
@@ -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 */
More information about the lxc-devel
mailing list