]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
conf: reorganize/split code to idmap_utils.c
authorAlexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com>
Sun, 18 Feb 2024 14:17:25 +0000 (15:17 +0100)
committerAlexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com>
Sun, 18 Feb 2024 14:17:25 +0000 (15:17 +0100)
Move some idmaps-related functions from lxc/conf.c
to a new idmap_utils.c file.

Signed-off-by: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com>
src/lxc/cmd/lxc_usernsexec.c
src/lxc/conf.c
src/lxc/conf.h
src/lxc/idmap_utils.c [new file with mode: 0644]
src/lxc/idmap_utils.h [new file with mode: 0644]
src/lxc/lxccontainer.c
src/lxc/meson.build
src/lxc/start.c
src/lxc/tools/lxc_unshare.c

index b17faa38c79bdc539bb0d37958e2a3ac717db354..4654df2c6f29ccff1d5098d45d5b8a91bac283ff 100644 (file)
@@ -22,6 +22,7 @@
 #include "compiler.h"
 #include "conf.h"
 #include "hlist.h"
+#include "idmap_utils.h"
 #include "list.h"
 #include "log.h"
 #include "macro.h"
index d006bccc15b6ac71291357b85474f5cb07156b90..93ca5f923ef91797003559080b2fa8d8e5554656 100644 (file)
@@ -41,6 +41,7 @@
 #include "confile.h"
 #include "confile_utils.h"
 #include "error.h"
+#include "idmap_utils.h"
 #include "log.h"
 #include "lsm/lsm.h"
 #include "lxclock.h"
@@ -1667,35 +1668,6 @@ static int lxc_setup_rootfs_switch_root(const struct lxc_rootfs *rootfs)
        return lxc_pivot_root(rootfs);
 }
 
-static const struct id_map *find_mapped_nsid_entry(const struct lxc_conf *conf,
-                                                  unsigned id,
-                                                  enum idtype idtype)
-{
-       struct id_map *map;
-       struct id_map *retmap = NULL;
-
-       /* Shortcut for container's root mappings. */
-       if (id == 0) {
-               if (idtype == ID_TYPE_UID)
-                       return conf->root_nsuid_map;
-
-               if (idtype == ID_TYPE_GID)
-                       return conf->root_nsgid_map;
-       }
-
-       list_for_each_entry(map, &conf->id_map, head) {
-               if (map->idtype != idtype)
-                       continue;
-
-               if (id >= map->nsid && id < map->nsid + map->range) {
-                       retmap = map;
-                       break;
-               }
-       }
-
-       return retmap;
-}
-
 static int lxc_recv_devpts_from_child(struct lxc_handler *handler)
 {
        int ret;
@@ -3479,243 +3451,6 @@ struct lxc_conf *lxc_conf_init(void)
        return new;
 }
 
-int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf,
-                    size_t buf_size)
-{
-       __do_close int fd = -EBADF;
-       int ret;
-       char path[PATH_MAX];
-
-       if (geteuid() != 0 && idtype == ID_TYPE_GID) {
-               __do_close int setgroups_fd = -EBADF;
-
-               ret = strnprintf(path, sizeof(path), "/proc/%d/setgroups", pid);
-               if (ret < 0)
-                       return -E2BIG;
-
-               setgroups_fd = open(path, O_WRONLY);
-               if (setgroups_fd < 0 && errno != ENOENT)
-                       return log_error_errno(-1, errno, "Failed to open \"%s\"", path);
-
-               if (setgroups_fd >= 0) {
-                       ret = lxc_write_nointr(setgroups_fd, "deny\n",
-                                              STRLITERALLEN("deny\n"));
-                       if (ret != STRLITERALLEN("deny\n"))
-                               return log_error_errno(-1, errno, "Failed to write \"deny\" to \"/proc/%d/setgroups\"", pid);
-                       TRACE("Wrote \"deny\" to \"/proc/%d/setgroups\"", pid);
-               }
-       }
-
-       ret = strnprintf(path, sizeof(path), "/proc/%d/%cid_map", pid,
-                      idtype == ID_TYPE_UID ? 'u' : 'g');
-       if (ret < 0)
-               return -E2BIG;
-
-       fd = open(path, O_WRONLY | O_CLOEXEC);
-       if (fd < 0)
-               return log_error_errno(-1, errno, "Failed to open \"%s\"", path);
-
-       ret = lxc_write_nointr(fd, buf, buf_size);
-       if (ret < 0 || (size_t)ret != buf_size)
-               return log_error_errno(-1, errno, "Failed to write %cid mapping to \"%s\"",
-                                      idtype == ID_TYPE_UID ? 'u' : 'g', path);
-
-       return 0;
-}
-
-/* Check whether a binary exist and has either CAP_SETUID, CAP_SETGID or both.
- *
- * @return  1      if functional binary was found
- * @return  0      if binary exists but is lacking privilege
- * @return -ENOENT if binary does not exist
- * @return -EINVAL if cap to check is neither CAP_SETUID nor CAP_SETGID
- */
-static int idmaptool_on_path_and_privileged(const char *binary, cap_value_t cap)
-{
-       __do_free char *path = NULL;
-       int ret;
-       struct stat st;
-
-       if (cap != CAP_SETUID && cap != CAP_SETGID)
-               return ret_errno(EINVAL);
-
-       path = on_path(binary, NULL);
-       if (!path)
-               return ret_errno(ENOENT);
-
-       ret = stat(path, &st);
-       if (ret < 0)
-               return -errno;
-
-       /* Check if the binary is setuid. */
-       if (st.st_mode & S_ISUID)
-               return log_debug(1, "The binary \"%s\" does have the setuid bit set", path);
-
-#if HAVE_LIBCAP && LIBCAP_SUPPORTS_FILE_CAPABILITIES
-       /* Check if it has the CAP_SETUID capability. */
-       if ((cap & CAP_SETUID) &&
-           lxc_file_cap_is_set(path, CAP_SETUID, CAP_EFFECTIVE) &&
-           lxc_file_cap_is_set(path, CAP_SETUID, CAP_PERMITTED))
-               return log_debug(1, "The binary \"%s\" has CAP_SETUID in its CAP_EFFECTIVE and CAP_PERMITTED sets", path);
-
-       /* Check if it has the CAP_SETGID capability. */
-       if ((cap & CAP_SETGID) &&
-           lxc_file_cap_is_set(path, CAP_SETGID, CAP_EFFECTIVE) &&
-           lxc_file_cap_is_set(path, CAP_SETGID, CAP_PERMITTED))
-               return log_debug(1, "The binary \"%s\" has CAP_SETGID in its CAP_EFFECTIVE and CAP_PERMITTED sets", path);
-
-       return 0;
-#else
-       /*
-        * If we cannot check for file capabilities we need to give the benefit
-        * of the doubt. Otherwise we might fail even though all the necessary
-        * file capabilities are set.
-        */
-       DEBUG("Cannot check for file capabilities as full capability support is missing. Manual intervention needed");
-       return 1;
-#endif
-}
-
-static int lxc_map_ids_exec_wrapper(void *args)
-{
-       execl("/bin/sh", "sh", "-c", (char *)args, (char *)NULL);
-       return -1;
-}
-
-static struct id_map *find_mapped_hostid_entry(const struct list_head *idmap,
-                                              unsigned id, enum idtype idtype);
-
-int lxc_map_ids(struct list_head *idmap, pid_t pid)
-{
-       int fill, left;
-       uid_t hostuid;
-       gid_t hostgid;
-       char u_or_g;
-       char *pos;
-       char cmd_output[PATH_MAX];
-       struct id_map *map;
-       enum idtype type;
-       int ret = 0, gidmap = 0, uidmap = 0;
-       char mapbuf[STRLITERALLEN("new@idmap") + STRLITERALLEN(" ") +
-                   INTTYPE_TO_STRLEN(pid_t) + STRLITERALLEN(" ") +
-                   LXC_IDMAPLEN] = {0};
-       bool had_entry = false, maps_host_root = false, use_shadow = false;
-
-       hostuid = geteuid();
-       hostgid = getegid();
-
-       /*
-        * Check whether caller wants to map host root.
-        * Due to a security fix newer kernels require CAP_SETFCAP when mapping
-        * host root into the child userns as you would be able to write fscaps
-        * that would be valid in the ancestor userns. Mapping host root should
-        * rarely be the case but LXC is being clever in a bunch of cases.
-        */
-       if (find_mapped_hostid_entry(idmap, 0, ID_TYPE_UID))
-               maps_host_root = true;
-
-       /* If new{g,u}idmap exists, that is, if shadow is handing out subuid
-        * ranges, then insist that root also reserve ranges in subuid. This
-        * will protected it by preventing another user from being handed the
-        * range by shadow.
-        */
-       uidmap = idmaptool_on_path_and_privileged("newuidmap", CAP_SETUID);
-       if (uidmap == -ENOENT)
-               WARN("newuidmap binary is missing");
-       else if (!uidmap)
-               WARN("newuidmap is lacking necessary privileges");
-
-       gidmap = idmaptool_on_path_and_privileged("newgidmap", CAP_SETGID);
-       if (gidmap == -ENOENT)
-               WARN("newgidmap binary is missing");
-       else if (!gidmap)
-               WARN("newgidmap is lacking necessary privileges");
-
-       if (maps_host_root) {
-               INFO("Caller maps host root. Writing mapping directly");
-       } else if (uidmap > 0 && gidmap > 0) {
-               DEBUG("Functional newuidmap and newgidmap binary found");
-               use_shadow = true;
-       } else {
-               /* In case unprivileged users run application containers via
-                * execute() or a start*() there are valid cases where they may
-                * only want to map their own {g,u}id. Let's not block them from
-                * doing so by requiring geteuid() == 0.
-                */
-               DEBUG("No newuidmap and newgidmap binary found. Trying to "
-                     "write directly with euid %d", hostuid);
-       }
-
-       /* Check if we really need to use newuidmap and newgidmap.
-       * If the user is only remapping their own {g,u}id, we don't need it.
-       */
-       if (use_shadow && list_len(map, idmap, head) == 2) {
-               use_shadow = false;
-               list_for_each_entry(map, idmap, head) {
-                       if (map->idtype == ID_TYPE_UID && map->range == 1 &&
-                           map->nsid == hostuid && map->hostid == hostuid)
-                               continue;
-                       if (map->idtype == ID_TYPE_GID && map->range == 1 &&
-                           map->nsid == hostgid && map->hostid == hostgid)
-                               continue;
-                       use_shadow = true;
-                       break;
-               }
-       }
-
-       for (type = ID_TYPE_UID, u_or_g = 'u'; type <= ID_TYPE_GID;
-            type++, u_or_g = 'g') {
-               pos = mapbuf;
-
-               if (use_shadow)
-                       pos += sprintf(mapbuf, "new%cidmap %d", u_or_g, pid);
-
-               list_for_each_entry(map, idmap, head) {
-                       if (map->idtype != type)
-                               continue;
-
-                       had_entry = true;
-
-                       left = LXC_IDMAPLEN - (pos - mapbuf);
-                       fill = strnprintf(pos, left, "%s%lu %lu %lu%s",
-                                       use_shadow ? " " : "", map->nsid,
-                                       map->hostid, map->range,
-                                       use_shadow ? "" : "\n");
-                       /*
-                        * The kernel only takes <= 4k for writes to
-                        * /proc/<pid>/{g,u}id_map
-                        */
-                       if (fill <= 0)
-                               return log_error_errno(-1, errno, "Too many %cid mappings defined", u_or_g);
-
-                       pos += fill;
-               }
-               if (!had_entry)
-                       continue;
-
-               /* Try to catch the output of new{g,u}idmap to make debugging
-                * easier.
-                */
-               if (use_shadow) {
-                       ret = run_command(cmd_output, sizeof(cmd_output),
-                                         lxc_map_ids_exec_wrapper,
-                                         (void *)mapbuf);
-                       if (ret < 0)
-                               return log_error(-1, "new%cidmap failed to write mapping \"%s\": %s", u_or_g, cmd_output, mapbuf);
-                       TRACE("new%cidmap wrote mapping \"%s\"", u_or_g, mapbuf);
-               } else {
-                       ret = write_id_mapping(type, pid, mapbuf, pos - mapbuf);
-                       if (ret < 0)
-                               return log_error(-1, "Failed to write mapping: %s", mapbuf);
-                       TRACE("Wrote mapping \"%s\"", mapbuf);
-               }
-
-               memset(mapbuf, 0, sizeof(mapbuf));
-       }
-
-       return 0;
-}
-
 /*
  * Return the host uid/gid to which the container root is mapped in val.
  * Return true if id was found, false otherwise.
@@ -3744,40 +3479,6 @@ static id_t get_mapped_rootid(const struct lxc_conf *conf, enum idtype idtype)
        return LXC_INVALID_GID;
 }
 
-int mapped_hostid(unsigned id, const struct lxc_conf *conf, enum idtype idtype)
-{
-       struct id_map *map;
-
-       list_for_each_entry(map, &conf->id_map, head) {
-               if (map->idtype != idtype)
-                       continue;
-
-               if (id >= map->hostid && id < map->hostid + map->range)
-                       return (id - map->hostid) + map->nsid;
-       }
-
-       return -1;
-}
-
-int find_unmapped_nsid(const struct lxc_conf *conf, enum idtype idtype)
-{
-       struct id_map *map;
-       unsigned int freeid = 0;
-
-again:
-       list_for_each_entry(map, &conf->id_map, head) {
-               if (map->idtype != idtype)
-                       continue;
-
-               if (freeid >= map->nsid && freeid < map->nsid + map->range) {
-                       freeid = map->nsid + map->range;
-                       goto again;
-               }
-       }
-
-       return freeid;
-}
-
 /*
  * Mount a proc under @rootfs if proc self points to a pid other than
  * my own.  This is needed to have a known-good proc mount for setting
@@ -4915,76 +4616,6 @@ static int run_userns_fn(void *data)
        return d->fn(d->arg);
 }
 
-static struct id_map *mapped_nsid_add(const struct lxc_conf *conf, unsigned id,
-                                     enum idtype idtype)
-{
-       const struct id_map *map;
-       struct id_map *retmap;
-
-       map = find_mapped_nsid_entry(conf, id, idtype);
-       if (!map)
-               return NULL;
-
-       retmap = zalloc(sizeof(*retmap));
-       if (!retmap)
-               return NULL;
-
-       memcpy(retmap, map, sizeof(*retmap));
-       return retmap;
-}
-
-static struct id_map *find_mapped_hostid_entry(const struct list_head *idmap,
-                                              unsigned id, enum idtype idtype)
-{
-       struct id_map *retmap = NULL;
-       struct id_map *map;
-
-       list_for_each_entry(map, idmap, head) {
-               if (map->idtype != idtype)
-                       continue;
-
-               if (id >= map->hostid && id < map->hostid + map->range) {
-                       retmap = map;
-                       break;
-               }
-       }
-
-       return retmap;
-}
-
-/* Allocate a new {g,u}id mapping for the given {g,u}id. Re-use an already
- * existing one or establish a new one.
- */
-static struct id_map *mapped_hostid_add(const struct lxc_conf *conf, uid_t id,
-                                       enum idtype type)
-{
-       __do_free struct id_map *entry = NULL;
-       int hostid_mapped;
-       struct id_map *tmp = NULL;
-
-       entry = zalloc(sizeof(*entry));
-       if (!entry)
-               return NULL;
-
-       /* Reuse existing mapping. */
-       tmp = find_mapped_hostid_entry(&conf->id_map, id, type);
-       if (tmp) {
-               memcpy(entry, tmp, sizeof(*entry));
-       } else {
-               /* Find new mapping. */
-               hostid_mapped = find_unmapped_nsid(conf, type);
-               if (hostid_mapped < 0)
-                       return log_debug(NULL, "Failed to find free mapping for id %d", id);
-
-               entry->idtype = type;
-               entry->nsid = hostid_mapped;
-               entry->hostid = (unsigned long)id;
-               entry->range = 1;
-       }
-
-       return move_ptr(entry);
-}
-
 static int get_minimal_idmap(const struct lxc_conf *conf, uid_t *resuid,
                             gid_t *resgid, struct list_head *head_ret)
 {
index 4ad8970443913a6493bd0d388f76f2e68134fdd0..489e5c20459e1fee57af89743de1938c64d5b056 100644 (file)
@@ -578,9 +578,6 @@ struct lxc_conf {
        __u64 sched_core_cookie;
 };
 
-__hidden extern int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, size_t buf_size)
-    __access_r(3, 4);
-
 extern thread_local struct lxc_conf *current_config;
 
 __hidden extern int run_lxc_hooks(const char *name, char *hook, struct lxc_conf *conf, char *argv[]);
@@ -592,7 +589,6 @@ __hidden extern void lxc_storage_put(struct lxc_conf *conf);
 __hidden extern int lxc_rootfs_init(struct lxc_conf *conf, bool userns);
 __hidden extern int lxc_rootfs_prepare_parent(struct lxc_handler *handler);
 __hidden extern int lxc_idmapped_mounts_parent(struct lxc_handler *handler);
-__hidden extern int lxc_map_ids(struct list_head *idmap, pid_t pid);
 __hidden extern int lxc_create_tty(const char *name, struct lxc_conf *conf);
 __hidden extern void lxc_delete_tty(struct lxc_tty_info *ttys);
 __hidden extern int lxc_clear_config_caps(struct lxc_conf *c);
@@ -611,8 +607,6 @@ __hidden extern int lxc_setup_rootfs_prepare_root(struct lxc_conf *conf, const c
 __hidden extern int lxc_setup(struct lxc_handler *handler);
 __hidden extern int lxc_setup_parent(struct lxc_handler *handler);
 __hidden extern int setup_resource_limits(struct lxc_conf *conf, pid_t pid);
-__hidden extern int find_unmapped_nsid(const struct lxc_conf *conf, enum idtype idtype);
-__hidden extern int mapped_hostid(unsigned id, const struct lxc_conf *conf, enum idtype idtype);
 __hidden extern int userns_exec_1(const struct lxc_conf *conf, int (*fn)(void *), void *data,
                                  const char *fn_name);
 __hidden extern int userns_exec_full(struct lxc_conf *conf, int (*fn)(void *), void *data,
diff --git a/src/lxc/idmap_utils.c b/src/lxc/idmap_utils.c
new file mode 100644 (file)
index 0000000..a0f0a40
--- /dev/null
@@ -0,0 +1,391 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <malloc.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/file.h>
+#include <unistd.h>
+
+#include "lxc.h"
+
+#include "log.h"
+#include "lxclock.h"
+#include "memory_utils.h"
+#include "utils.h"
+
+lxc_log_define(idmap_utils, lxc);
+
+int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf,
+                    size_t buf_size)
+{
+       __do_close int fd = -EBADF;
+       int ret;
+       char path[PATH_MAX];
+
+       if (geteuid() != 0 && idtype == ID_TYPE_GID) {
+               __do_close int setgroups_fd = -EBADF;
+
+               ret = strnprintf(path, sizeof(path), "/proc/%d/setgroups", pid);
+               if (ret < 0)
+                       return -E2BIG;
+
+               setgroups_fd = open(path, O_WRONLY);
+               if (setgroups_fd < 0 && errno != ENOENT)
+                       return log_error_errno(-1, errno, "Failed to open \"%s\"", path);
+
+               if (setgroups_fd >= 0) {
+                       ret = lxc_write_nointr(setgroups_fd, "deny\n",
+                                              STRLITERALLEN("deny\n"));
+                       if (ret != STRLITERALLEN("deny\n"))
+                               return log_error_errno(-1, errno, "Failed to write \"deny\" to \"/proc/%d/setgroups\"", pid);
+                       TRACE("Wrote \"deny\" to \"/proc/%d/setgroups\"", pid);
+               }
+       }
+
+       ret = strnprintf(path, sizeof(path), "/proc/%d/%cid_map", pid,
+                      idtype == ID_TYPE_UID ? 'u' : 'g');
+       if (ret < 0)
+               return -E2BIG;
+
+       fd = open(path, O_WRONLY | O_CLOEXEC);
+       if (fd < 0)
+               return log_error_errno(-1, errno, "Failed to open \"%s\"", path);
+
+       ret = lxc_write_nointr(fd, buf, buf_size);
+       if (ret < 0 || (size_t)ret != buf_size)
+               return log_error_errno(-1, errno, "Failed to write %cid mapping to \"%s\"",
+                                      idtype == ID_TYPE_UID ? 'u' : 'g', path);
+
+       return 0;
+}
+
+/* Check whether a binary exist and has either CAP_SETUID, CAP_SETGID or both.
+ *
+ * @return  1      if functional binary was found
+ * @return  0      if binary exists but is lacking privilege
+ * @return -ENOENT if binary does not exist
+ * @return -EINVAL if cap to check is neither CAP_SETUID nor CAP_SETGID
+ */
+static int idmaptool_on_path_and_privileged(const char *binary, cap_value_t cap)
+{
+       __do_free char *path = NULL;
+       int ret;
+       struct stat st;
+
+       if (cap != CAP_SETUID && cap != CAP_SETGID)
+               return ret_errno(EINVAL);
+
+       path = on_path(binary, NULL);
+       if (!path)
+               return ret_errno(ENOENT);
+
+       ret = stat(path, &st);
+       if (ret < 0)
+               return -errno;
+
+       /* Check if the binary is setuid. */
+       if (st.st_mode & S_ISUID)
+               return log_debug(1, "The binary \"%s\" does have the setuid bit set", path);
+
+#if HAVE_LIBCAP && LIBCAP_SUPPORTS_FILE_CAPABILITIES
+       /* Check if it has the CAP_SETUID capability. */
+       if ((cap & CAP_SETUID) &&
+           lxc_file_cap_is_set(path, CAP_SETUID, CAP_EFFECTIVE) &&
+           lxc_file_cap_is_set(path, CAP_SETUID, CAP_PERMITTED))
+               return log_debug(1, "The binary \"%s\" has CAP_SETUID in its CAP_EFFECTIVE and CAP_PERMITTED sets", path);
+
+       /* Check if it has the CAP_SETGID capability. */
+       if ((cap & CAP_SETGID) &&
+           lxc_file_cap_is_set(path, CAP_SETGID, CAP_EFFECTIVE) &&
+           lxc_file_cap_is_set(path, CAP_SETGID, CAP_PERMITTED))
+               return log_debug(1, "The binary \"%s\" has CAP_SETGID in its CAP_EFFECTIVE and CAP_PERMITTED sets", path);
+
+       return 0;
+#else
+       /*
+        * If we cannot check for file capabilities we need to give the benefit
+        * of the doubt. Otherwise we might fail even though all the necessary
+        * file capabilities are set.
+        */
+       DEBUG("Cannot check for file capabilities as full capability support is missing. Manual intervention needed");
+       return 1;
+#endif
+}
+
+static int lxc_map_ids_exec_wrapper(void *args)
+{
+       execl("/bin/sh", "sh", "-c", (char *)args, (char *)NULL);
+       return -1;
+}
+
+static struct id_map *find_mapped_hostid_entry(const struct list_head *idmap,
+                                              unsigned id, enum idtype idtype);
+
+int lxc_map_ids(struct list_head *idmap, pid_t pid)
+{
+       int fill, left;
+       uid_t hostuid;
+       gid_t hostgid;
+       char u_or_g;
+       char *pos;
+       char cmd_output[PATH_MAX];
+       struct id_map *map;
+       enum idtype type;
+       int ret = 0, gidmap = 0, uidmap = 0;
+       char mapbuf[STRLITERALLEN("new@idmap") + STRLITERALLEN(" ") +
+                   INTTYPE_TO_STRLEN(pid_t) + STRLITERALLEN(" ") +
+                   LXC_IDMAPLEN] = {0};
+       bool had_entry = false, maps_host_root = false, use_shadow = false;
+
+       hostuid = geteuid();
+       hostgid = getegid();
+
+       /*
+        * Check whether caller wants to map host root.
+        * Due to a security fix newer kernels require CAP_SETFCAP when mapping
+        * host root into the child userns as you would be able to write fscaps
+        * that would be valid in the ancestor userns. Mapping host root should
+        * rarely be the case but LXC is being clever in a bunch of cases.
+        */
+       if (find_mapped_hostid_entry(idmap, 0, ID_TYPE_UID))
+               maps_host_root = true;
+
+       /* If new{g,u}idmap exists, that is, if shadow is handing out subuid
+        * ranges, then insist that root also reserve ranges in subuid. This
+        * will protected it by preventing another user from being handed the
+        * range by shadow.
+        */
+       uidmap = idmaptool_on_path_and_privileged("newuidmap", CAP_SETUID);
+       if (uidmap == -ENOENT)
+               WARN("newuidmap binary is missing");
+       else if (!uidmap)
+               WARN("newuidmap is lacking necessary privileges");
+
+       gidmap = idmaptool_on_path_and_privileged("newgidmap", CAP_SETGID);
+       if (gidmap == -ENOENT)
+               WARN("newgidmap binary is missing");
+       else if (!gidmap)
+               WARN("newgidmap is lacking necessary privileges");
+
+       if (maps_host_root) {
+               INFO("Caller maps host root. Writing mapping directly");
+       } else if (uidmap > 0 && gidmap > 0) {
+               DEBUG("Functional newuidmap and newgidmap binary found");
+               use_shadow = true;
+       } else {
+               /* In case unprivileged users run application containers via
+                * execute() or a start*() there are valid cases where they may
+                * only want to map their own {g,u}id. Let's not block them from
+                * doing so by requiring geteuid() == 0.
+                */
+               DEBUG("No newuidmap and newgidmap binary found. Trying to "
+                     "write directly with euid %d", hostuid);
+       }
+
+       /* Check if we really need to use newuidmap and newgidmap.
+       * If the user is only remapping their own {g,u}id, we don't need it.
+       */
+       if (use_shadow && list_len(map, idmap, head) == 2) {
+               use_shadow = false;
+               list_for_each_entry(map, idmap, head) {
+                       if (map->idtype == ID_TYPE_UID && map->range == 1 &&
+                           map->nsid == hostuid && map->hostid == hostuid)
+                               continue;
+                       if (map->idtype == ID_TYPE_GID && map->range == 1 &&
+                           map->nsid == hostgid && map->hostid == hostgid)
+                               continue;
+                       use_shadow = true;
+                       break;
+               }
+       }
+
+       for (type = ID_TYPE_UID, u_or_g = 'u'; type <= ID_TYPE_GID;
+            type++, u_or_g = 'g') {
+               pos = mapbuf;
+
+               if (use_shadow)
+                       pos += sprintf(mapbuf, "new%cidmap %d", u_or_g, pid);
+
+               list_for_each_entry(map, idmap, head) {
+                       if (map->idtype != type)
+                               continue;
+
+                       had_entry = true;
+
+                       left = LXC_IDMAPLEN - (pos - mapbuf);
+                       fill = strnprintf(pos, left, "%s%lu %lu %lu%s",
+                                       use_shadow ? " " : "", map->nsid,
+                                       map->hostid, map->range,
+                                       use_shadow ? "" : "\n");
+                       /*
+                        * The kernel only takes <= 4k for writes to
+                        * /proc/<pid>/{g,u}id_map
+                        */
+                       if (fill <= 0)
+                               return log_error_errno(-1, errno, "Too many %cid mappings defined", u_or_g);
+
+                       pos += fill;
+               }
+               if (!had_entry)
+                       continue;
+
+               /* Try to catch the output of new{g,u}idmap to make debugging
+                * easier.
+                */
+               if (use_shadow) {
+                       ret = run_command(cmd_output, sizeof(cmd_output),
+                                         lxc_map_ids_exec_wrapper,
+                                         (void *)mapbuf);
+                       if (ret < 0)
+                               return log_error(-1, "new%cidmap failed to write mapping \"%s\": %s", u_or_g, cmd_output, mapbuf);
+                       TRACE("new%cidmap wrote mapping \"%s\"", u_or_g, mapbuf);
+               } else {
+                       ret = write_id_mapping(type, pid, mapbuf, pos - mapbuf);
+                       if (ret < 0)
+                               return log_error(-1, "Failed to write mapping: %s", mapbuf);
+                       TRACE("Wrote mapping \"%s\"", mapbuf);
+               }
+
+               memset(mapbuf, 0, sizeof(mapbuf));
+       }
+
+       return 0;
+}
+
+int mapped_hostid(unsigned id, const struct lxc_conf *conf, enum idtype idtype)
+{
+       struct id_map *map;
+
+       list_for_each_entry(map, &conf->id_map, head) {
+               if (map->idtype != idtype)
+                       continue;
+
+               if (id >= map->hostid && id < map->hostid + map->range)
+                       return (id - map->hostid) + map->nsid;
+       }
+
+       return -1;
+}
+
+int find_unmapped_nsid(const struct lxc_conf *conf, enum idtype idtype)
+{
+       struct id_map *map;
+       unsigned int freeid = 0;
+
+again:
+       list_for_each_entry(map, &conf->id_map, head) {
+               if (map->idtype != idtype)
+                       continue;
+
+               if (freeid >= map->nsid && freeid < map->nsid + map->range) {
+                       freeid = map->nsid + map->range;
+                       goto again;
+               }
+       }
+
+       return freeid;
+}
+
+static const struct id_map *find_mapped_nsid_entry(const struct lxc_conf *conf,
+                                                  unsigned id,
+                                                  enum idtype idtype)
+{
+       struct id_map *map;
+       struct id_map *retmap = NULL;
+
+       /* Shortcut for container's root mappings. */
+       if (id == 0) {
+               if (idtype == ID_TYPE_UID)
+                       return conf->root_nsuid_map;
+
+               if (idtype == ID_TYPE_GID)
+                       return conf->root_nsgid_map;
+       }
+
+       list_for_each_entry(map, &conf->id_map, head) {
+               if (map->idtype != idtype)
+                       continue;
+
+               if (id >= map->nsid && id < map->nsid + map->range) {
+                       retmap = map;
+                       break;
+               }
+       }
+
+       return retmap;
+}
+
+struct id_map *mapped_nsid_add(const struct lxc_conf *conf, unsigned id,
+                                     enum idtype idtype)
+{
+       const struct id_map *map;
+       struct id_map *retmap;
+
+       map = find_mapped_nsid_entry(conf, id, idtype);
+       if (!map)
+               return NULL;
+
+       retmap = zalloc(sizeof(*retmap));
+       if (!retmap)
+               return NULL;
+
+       memcpy(retmap, map, sizeof(*retmap));
+       return retmap;
+}
+
+static struct id_map *find_mapped_hostid_entry(const struct list_head *idmap,
+                                              unsigned id, enum idtype idtype)
+{
+       struct id_map *retmap = NULL;
+       struct id_map *map;
+
+       list_for_each_entry(map, idmap, head) {
+               if (map->idtype != idtype)
+                       continue;
+
+               if (id >= map->hostid && id < map->hostid + map->range) {
+                       retmap = map;
+                       break;
+               }
+       }
+
+       return retmap;
+}
+
+/* Allocate a new {g,u}id mapping for the given {g,u}id. Re-use an already
+ * existing one or establish a new one.
+ */
+struct id_map *mapped_hostid_add(const struct lxc_conf *conf, uid_t id,
+                                       enum idtype type)
+{
+       __do_free struct id_map *entry = NULL;
+       int hostid_mapped;
+       struct id_map *tmp = NULL;
+
+       entry = zalloc(sizeof(*entry));
+       if (!entry)
+               return NULL;
+
+       /* Reuse existing mapping. */
+       tmp = find_mapped_hostid_entry(&conf->id_map, id, type);
+       if (tmp) {
+               memcpy(entry, tmp, sizeof(*entry));
+       } else {
+               /* Find new mapping. */
+               hostid_mapped = find_unmapped_nsid(conf, type);
+               if (hostid_mapped < 0)
+                       return log_debug(NULL, "Failed to find free mapping for id %d", id);
+
+               entry->idtype = type;
+               entry->nsid = hostid_mapped;
+               entry->hostid = (unsigned long)id;
+               entry->range = 1;
+       }
+
+       return move_ptr(entry);
+}
diff --git a/src/lxc/idmap_utils.h b/src/lxc/idmap_utils.h
new file mode 100644 (file)
index 0000000..da13636
--- /dev/null
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: LGPL-2.1+ */
+
+#ifndef __LXC_IDMAP_UTILS_H
+#define __LXC_IDMAP_UTILS_H
+
+#include "config.h"
+
+#include <fcntl.h>
+#include <semaphore.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "compiler.h"
+#include "conf.h"
+
+__hidden extern int lxc_map_ids(struct list_head *idmap, pid_t pid);
+__hidden extern int find_unmapped_nsid(const struct lxc_conf *conf, enum idtype idtype);
+__hidden extern int mapped_hostid(unsigned id, const struct lxc_conf *conf, enum idtype idtype);
+__hidden extern struct id_map *mapped_hostid_add(const struct lxc_conf *conf, uid_t id,
+                                                enum idtype type);
+__hidden extern struct id_map *mapped_nsid_add(const struct lxc_conf *conf, unsigned id,
+                                              enum idtype idtype);
+
+__hidden extern int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, size_t buf_size)
+    __access_r(3, 4);
+
+#endif
index 70d4fece2bf74859160efad4527dbdfd9268673f..7a88c8d6625df6391cac88463c8f2eb5397aa7e0 100644 (file)
@@ -39,6 +39,7 @@
 #include "confile_utils.h"
 #include "criu.h"
 #include "error.h"
+#include "idmap_utils.h"
 #include "initutils.h"
 #include "log.h"
 #include "lxc.h"
index f76971c15f538d3c13b1ba53fcc930e82e138150..9fa9d898353c2aecd5976355e174e415531b7e2f 100644 (file)
@@ -86,6 +86,8 @@ liblxc_sources = files(
     'file_utils.h',
     'freezer.c',
     'hlist.h',
+    'idmap_utils.c',
+    'idmap_utils.h',
     'initutils.c',
     'initutils.h',
     'list.h',
index 33e4ac94aa52897cc26c8b401b02504ef698db5e..8daa7d73c51f9aee060e32212a5602fac3ca94f5 100644 (file)
@@ -38,6 +38,7 @@
 #include "confile_utils.h"
 #include "error.h"
 #include "file_utils.h"
+#include "idmap_utils.h"
 #include "list.h"
 #include "log.h"
 #include "lsm/lsm.h"
index d4ab2e34785e45180fbc7016ffb310087fbf264a..a92b450a3a287099f1aec052c77451bb78f3bc79 100644 (file)
@@ -21,6 +21,7 @@
 
 #include "arguments.h"
 #include "caps.h"
+#include "idmap_utils.h"
 #include "list.h"
 #include "log.h"
 #include "namespace.h"