mirror of
https://github.com/openwrt/packages.git
synced 2025-12-22 12:34:35 +04:00
LXC after the switch to the Meson build system increased the binary sizes significantly as each binary is basically static so shipping complete liblxc which should be linked dynamically. Upstream later fixed it with series of 10 commits and this fixes are available in LXC release v6.0.0. Since we can't upstep to that release, lets fix it by backporting those fixes only, basically making libxlc a shared library again. Package sizes before: 384K lxc-user-nic_5.0.3-1_aarch64_cortex-a53.ipk 383K lxc-ls_5.0.3-1_aarch64_cortex-a53.ipk 382K lxc-top_5.0.3-1_aarch64_cortex-a53.ipk 382K lxc-copy_5.0.3-1_aarch64_cortex-a53.ipk 381K lxc-unshare_5.0.3-1_aarch64_cortex-a53.ipk 380K lxc-start_5.0.3-1_aarch64_cortex-a53.ipk 380K lxc-monitor_5.0.3-1_aarch64_cortex-a53.ipk 380K lxc-info_5.0.3-1_aarch64_cortex-a53.ipk 380K lxc-create_5.0.3-1_aarch64_cortex-a53.ipk 380K lxc-autostart_5.0.3-1_aarch64_cortex-a53.ipk 380K lxc-attach_5.0.3-1_aarch64_cortex-a53.ipk 379K lxc-execute_5.0.3-1_aarch64_cortex-a53.ipk 378K lxc-wait_5.0.3-1_aarch64_cortex-a53.ipk 378K lxc-usernsexec_5.0.3-1_aarch64_cortex-a53.ipk 378K lxc-unfreeze_5.0.3-1_aarch64_cortex-a53.ipk 378K lxc-stop_5.0.3-1_aarch64_cortex-a53.ipk 378K lxc-freeze_5.0.3-1_aarch64_cortex-a53.ipk 378K lxc-device_5.0.3-1_aarch64_cortex-a53.ipk 378K lxc-destroy_5.0.3-1_aarch64_cortex-a53.ipk 378K lxc-console_5.0.3-1_aarch64_cortex-a53.ipk 378K lxc-cgroup_5.0.3-1_aarch64_cortex-a53.ipk 376K liblxc_5.0.3-1_aarch64_cortex-a53.ipk 375K lxc-config_5.0.3-1_aarch64_cortex-a53.ipk 12K lxc-hooks_5.0.3-1_aarch64_cortex-a53.ipk 11K lxc-templates_5.0.3-1_aarch64_cortex-a53.ipk 3.7K lxc-checkconfig_5.0.3-1_aarch64_cortex-a53.ipk 2.4K lxc-configs_5.0.3-1_aarch64_cortex-a53.ipk 1.9K lxc-auto_5.0.3-1_aarch64_cortex-a53.ipk 1.6K lxc-common_5.0.3-1_aarch64_cortex-a53.ipk 1.2K lxc-unprivileged_5.0.3-1_aarch64_cortex-a53.ipk 978 lxc_5.0.3-1_aarch64_cortex-a53.ipk Sizes after: 378K liblxc_5.0.3-2_aarch64_cortex-a53.ipk 27K lxc-user-nic_5.0.3-2_aarch64_cortex-a53.ipk 24K lxc-ls_5.0.3-2_aarch64_cortex-a53.ipk 21K lxc-usernsexec_5.0.3-2_aarch64_cortex-a53.ipk 21K lxc-top_5.0.3-2_aarch64_cortex-a53.ipk 20K lxc-unshare_5.0.3-2_aarch64_cortex-a53.ipk 20K lxc-copy_5.0.3-2_aarch64_cortex-a53.ipk 20K lxc-attach_5.0.3-2_aarch64_cortex-a53.ipk 18K lxc-start_5.0.3-2_aarch64_cortex-a53.ipk 18K lxc-info_5.0.3-2_aarch64_cortex-a53.ipk 18K lxc-execute_5.0.3-2_aarch64_cortex-a53.ipk 18K lxc-device_5.0.3-2_aarch64_cortex-a53.ipk 18K lxc-create_5.0.3-2_aarch64_cortex-a53.ipk 18K lxc-autostart_5.0.3-2_aarch64_cortex-a53.ipk 17K lxc-destroy_5.0.3-2_aarch64_cortex-a53.ipk 16K lxc-wait_5.0.3-2_aarch64_cortex-a53.ipk 16K lxc-unfreeze_5.0.3-2_aarch64_cortex-a53.ipk 16K lxc-stop_5.0.3-2_aarch64_cortex-a53.ipk 16K lxc-freeze_5.0.3-2_aarch64_cortex-a53.ipk 16K lxc-console_5.0.3-2_aarch64_cortex-a53.ipk 16K lxc-cgroup_5.0.3-2_aarch64_cortex-a53.ipk 15K lxc-monitor_5.0.3-2_aarch64_cortex-a53.ipk 13K lxc-config_5.0.3-2_aarch64_cortex-a53.ipk 12K lxc-hooks_5.0.3-2_aarch64_cortex-a53.ipk 11K lxc-templates_5.0.3-2_aarch64_cortex-a53.ipk 3.7K lxc-checkconfig_5.0.3-2_aarch64_cortex-a53.ipk 2.4K lxc-configs_5.0.3-2_aarch64_cortex-a53.ipk 1.9K lxc-auto_5.0.3-2_aarch64_cortex-a53.ipk 1.6K lxc-common_5.0.3-2_aarch64_cortex-a53.ipk 1.1K lxc-unprivileged_5.0.3-2_aarch64_cortex-a53.ipk 944 lxc_5.0.3-2_aarch64_cortex-a53.ipk Sum of Package Sizes: Before: 8758.78K After: 814.64K The total package size has decreased by approximately 90% after the fix. References: https://github.com/lxc/lxc/pull/4401 Signed-off-by: Petr Štetiar <ynezz@true.cz>
940 lines
27 KiB
Diff
940 lines
27 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Alexander Mikhalitsyn <aleksandr.mikhalitsyn@canonical.com>
|
|
Date: Sun, 18 Feb 2024 15:17:25 +0100
|
|
Subject: [PATCH] conf: reorganize/split code to idmap_utils.c
|
|
|
|
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>
|
|
(cherry picked from commit 863c59dc3aca086a892ad44eb2dfa53438544944)
|
|
---
|
|
src/lxc/cmd/lxc_usernsexec.c | 1 +
|
|
src/lxc/conf.c | 371 +--------------------------------
|
|
src/lxc/conf.h | 6 -
|
|
src/lxc/idmap_utils.c | 391 +++++++++++++++++++++++++++++++++++
|
|
src/lxc/idmap_utils.h | 30 +++
|
|
src/lxc/lxccontainer.c | 1 +
|
|
src/lxc/meson.build | 2 +
|
|
src/lxc/start.c | 1 +
|
|
src/lxc/tools/lxc_unshare.c | 1 +
|
|
9 files changed, 428 insertions(+), 376 deletions(-)
|
|
create mode 100644 src/lxc/idmap_utils.c
|
|
create mode 100644 src/lxc/idmap_utils.h
|
|
|
|
--- a/src/lxc/cmd/lxc_usernsexec.c
|
|
+++ b/src/lxc/cmd/lxc_usernsexec.c
|
|
@@ -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"
|
|
--- a/src/lxc/conf.c
|
|
+++ b/src/lxc/conf.c
|
|
@@ -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"
|
|
@@ -1664,35 +1665,6 @@ static int lxc_setup_rootfs_switch_root(
|
|
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;
|
|
@@ -3476,243 +3448,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.
|
|
@@ -3741,40 +3476,6 @@ static id_t get_mapped_rootid(const stru
|
|
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
|
|
@@ -4910,76 +4611,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)
|
|
{
|
|
--- a/src/lxc/conf.h
|
|
+++ b/src/lxc/conf.h
|
|
@@ -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(str
|
|
__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_pre
|
|
__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,
|
|
--- /dev/null
|
|
+++ b/src/lxc/idmap_utils.c
|
|
@@ -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);
|
|
+}
|
|
--- /dev/null
|
|
+++ b/src/lxc/idmap_utils.h
|
|
@@ -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
|
|
--- a/src/lxc/lxccontainer.c
|
|
+++ b/src/lxc/lxccontainer.c
|
|
@@ -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"
|
|
--- a/src/lxc/meson.build
|
|
+++ b/src/lxc/meson.build
|
|
@@ -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',
|
|
--- a/src/lxc/start.c
|
|
+++ b/src/lxc/start.c
|
|
@@ -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"
|
|
--- a/src/lxc/tools/lxc_unshare.c
|
|
+++ b/src/lxc/tools/lxc_unshare.c
|
|
@@ -21,6 +21,7 @@
|
|
|
|
#include "arguments.h"
|
|
#include "caps.h"
|
|
+#include "idmap_utils.h"
|
|
#include "list.h"
|
|
#include "log.h"
|
|
#include "namespace.h"
|