The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxc/pull/3397

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
to avoid the overhead of calling to lxc-usernsexec whenever we can.

Signed-off-by: Christian Brauner <christian.brau...@ubuntu.com>
From 234998b4f2a42623565f58b0ad20fa8254cbedde Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brau...@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.brau...@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 */
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to