util-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/5] unshare: Add support for mapping ranges of user/group IDs
@ 2021-11-17  2:10 Sean Anderson
  2021-11-17  2:10 ` [PATCH 1/5] include/c: Add abs_diff macro Sean Anderson
                   ` (4 more replies)
  0 siblings, 5 replies; 10+ messages in thread
From: Sean Anderson @ 2021-11-17  2:10 UTC (permalink / raw)
  To: util-linux, Karel Zak
  Cc: Mikhail Gusarov, Matthew Harm Bekkema, James Peach, Sean Anderson

This series adds support for mapping ranges of user/group IDs using the
newuidmap and newgidmap programs from shadow. The intent is to allow
for root-less bootstrapping of Linux root filesystems with correct
ownership. My primary inspiration is mmdebstrap [1], which uses
unshare(2) to create Debian root filesystems without needing root
access.

[1] https://gitlab.mister-muffin.de/josch/mmdebstrap


Sean Anderson (5):
  include/c: Add abs_diff macro
  unshare: Add waitchild helper
  unshare: Add options to map blocks of user/group IDs
  unshare: Add option to automatically create user and group maps
  unshare: Document --map-{groups,users,auto}

 include/c.h              |   8 +
 sys-utils/unshare.1.adoc |  32 ++++
 sys-utils/unshare.c      | 374 +++++++++++++++++++++++++++++++++++++--
 3 files changed, 399 insertions(+), 15 deletions(-)

-- 
2.33.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/5] include/c: Add abs_diff macro
  2021-11-17  2:10 [PATCH 0/5] unshare: Add support for mapping ranges of user/group IDs Sean Anderson
@ 2021-11-17  2:10 ` Sean Anderson
  2021-11-17  2:10 ` [PATCH 2/5] unshare: Add waitchild helper Sean Anderson
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 10+ messages in thread
From: Sean Anderson @ 2021-11-17  2:10 UTC (permalink / raw)
  To: util-linux, Karel Zak
  Cc: Mikhail Gusarov, Matthew Harm Bekkema, James Peach, Sean Anderson

This macro calculates abs(a - b). It is especially useful for unsigned
numbers.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 include/c.h | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/include/c.h b/include/c.h
index 354b59e29..ba799d8fd 100644
--- a/include/c.h
+++ b/include/c.h
@@ -159,6 +159,14 @@
 	_max1 > _max2 ? _max1 : _max2; })
 #endif
 
+#ifndef abs_diff
+# define abs_diff(x, y) __extension__ ({        \
+	__typeof__(x) _a = (x);			\
+	__typeof__(y) _b = (y);			\
+	(void) (&_a == &_b);			\
+	_a > _b ? _a - _b : _b - _a; })
+#endif
+
 #ifndef cmp_numbers
 # define cmp_numbers(x, y) __extension__ ({	\
 	__typeof__(x) _a = (x);			\
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/5] unshare: Add waitchild helper
  2021-11-17  2:10 [PATCH 0/5] unshare: Add support for mapping ranges of user/group IDs Sean Anderson
  2021-11-17  2:10 ` [PATCH 1/5] include/c: Add abs_diff macro Sean Anderson
@ 2021-11-17  2:10 ` Sean Anderson
  2021-11-17  2:10 ` [PATCH 3/5] unshare: Add options to map blocks of user/group IDs Sean Anderson
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 10+ messages in thread
From: Sean Anderson @ 2021-11-17  2:10 UTC (permalink / raw)
  To: util-linux, Karel Zak
  Cc: Mikhail Gusarov, Matthew Harm Bekkema, James Peach, Sean Anderson

This refactors out the waitpid() logic into a function which will be
reused for the upcoming patches.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 sys-utils/unshare.c | 30 ++++++++++++++++++------------
 1 file changed, 18 insertions(+), 12 deletions(-)

diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 3f8287799..540305ee7 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -261,6 +261,23 @@ static void bind_ns_files_from_child(pid_t *child, int fds[2])
 	}
 }
 
+static void waitchild(int pid)
+{
+	int rc, status;
+
+	do {
+		rc = waitpid(pid, &status, 0);
+		if (rc < 0) {
+			if (errno == EINTR)
+				continue;
+			err(EXIT_FAILURE, _("waitpid failed"));
+		}
+		if (WIFEXITED(status) &&
+		    WEXITSTATUS(status) != EXIT_SUCCESS)
+			exit(WEXITSTATUS(status));
+	} while (rc < 0);
+}
+
 static uid_t get_user(const char *s, const char *err)
 {
 	struct passwd *pw;
@@ -580,7 +597,6 @@ int main(int argc, char *argv[])
 	if (npersists && (pid || !forkit)) {
 		/* run in parent */
 		if (pid_bind && (unshare_flags & CLONE_NEWNS)) {
-			int rc;
 			char ch = PIPE_SYNC_BYTE;
 
 			/* signal child we are ready */
@@ -589,17 +605,7 @@ int main(int argc, char *argv[])
 			fds[1] = -1;
 
 			/* wait for bind_ns_files_from_child() */
-			do {
-				rc = waitpid(pid_bind, &status, 0);
-				if (rc < 0) {
-					if (errno == EINTR)
-						continue;
-					err(EXIT_FAILURE, _("waitpid failed"));
-				}
-				if (WIFEXITED(status) &&
-				    WEXITSTATUS(status) != EXIT_SUCCESS)
-					return WEXITSTATUS(status);
-			} while (rc < 0);
+			waitchild(pid_bind);
 		} else
 			/* simple way, just bind */
 			bind_ns_files(getpid());
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/5] unshare: Add options to map blocks of user/group IDs
  2021-11-17  2:10 [PATCH 0/5] unshare: Add support for mapping ranges of user/group IDs Sean Anderson
  2021-11-17  2:10 ` [PATCH 1/5] include/c: Add abs_diff macro Sean Anderson
  2021-11-17  2:10 ` [PATCH 2/5] unshare: Add waitchild helper Sean Anderson
@ 2021-11-17  2:10 ` Sean Anderson
  2021-11-23 14:33   ` Karel Zak
  2021-11-17  2:10 ` [PATCH 4/5] unshare: Add option to automatically create user and group maps Sean Anderson
  2021-11-17  2:10 ` [PATCH 5/5] unshare: Document --map-{groups,users,auto} Sean Anderson
  4 siblings, 1 reply; 10+ messages in thread
From: Sean Anderson @ 2021-11-17  2:10 UTC (permalink / raw)
  To: util-linux, Karel Zak
  Cc: Mikhail Gusarov, Matthew Harm Bekkema, James Peach, Sean Anderson

This adds the ability to map multiple user/group IDs when creating a new
user namespace. Regular processes cannot map any user other than the
effective user, so we need to use the setuid helpers newuidmap and
newgidmap, provided by shadow. Typically, users will be assigned blocks
of user/group IDs in /etc/sub{u,g}id, although it is also possible to
use NSS. There is a second advantage in using these helpers: because we
never write to /proc/self/gid_map, we don't have to disable setgroups.

Because the process of mapping IDs is almost identical, whether we are
mapping user IDs or group IDs, we put both in a common "map_range"
structure. These are read in by (ab)using string_to_idarray. In addition
to any map created with --map-users, we still need to handle a map of
size one created with --map-user. This makes constructing the helpers'
command line the trickiest part of the whole process. newuidmap/
newgidmap check to see if any ranges overlap before creating a mapping.
To avoid failing, we carve out a hole in the mapping for the singular
map. In the worst case, we may have three separate maps.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 sys-utils/unshare.c | 270 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 267 insertions(+), 3 deletions(-)

diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 540305ee7..247925aa6 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -24,6 +24,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <sys/eventfd.h>
 #include <sys/wait.h>
 #include <sys/mount.h>
 #include <sys/types.h>
@@ -314,6 +315,236 @@ static gid_t get_group(const char *s, const char *err)
 	return ret;
 }
 
+/**
+ * struct map_range - A range of IDs to map
+ * @outer: First ID inside the namespace
+ * @inner: First ID outside the namespace
+ * @count: Length of the inside and outside ranges
+ *
+ * A range of uids/gids to map using new[gu]idmap.
+ */
+struct map_range {
+	unsigned int outer;
+	unsigned int inner;
+	unsigned int count;
+};
+
+static int uint_to_id(const char *cname, size_t sz)
+{
+	char old, *name = (char *)cname;
+	unsigned long ret;
+
+	/* Add a NUL-terminator */
+	old = name[sz];
+	name[sz] = '\0';
+	ret = strtoul_or_err(name, _("could not parse ID"));
+	if (ret > UINT_MAX)
+		errx(EXIT_FAILURE, "id %lu is too big", ret);
+	/* And put back the old value to preserve const-ness */
+	name[sz] = old;
+	return ret;
+}
+
+/**
+ * get_map_range() - Parse a mapping range from a string
+ * @s: A string of the format upper,lower,count
+ * @res: The mapping to fill
+ *
+ * Parse a string of the form upper,lower,count into a new mapping range.
+ *
+ * Return: A new &struct map_range
+ */
+static struct map_range *get_map_range(const char *s)
+{
+	int n, map[3];
+	struct map_range *ret;
+
+	n = string_to_idarray(s, map, ARRAY_SIZE(map), uint_to_id);
+	if (n < 0)
+		errx(EXIT_FAILURE, _("too many elements for mapping '%s'"), s);
+	if (n != ARRAY_SIZE(map))
+		errx(EXIT_FAILURE, _("mapping '%s' contains only %d elements"),
+		     s, n);
+
+	ret = xmalloc(sizeof(*ret));
+	ret->outer = map[0];
+	ret->inner = map[1];
+	ret->count = map[2];
+	return ret;
+}
+
+/**
+ * map_ids() - Create a new uid/gid map
+ * @idmapper: Either newuidmap or newgidmap
+ * @ppid: Pid to set the map for
+ * @outer: ID outside the namespace for a single map.
+ * @inner: ID inside the namespace for a single map. May be -1 to only use @map.
+ * @map: A range of IDs to map
+ *
+ * This creates a new uid/gid map for @ppid using @idmapper. The ID @outer in
+ * the parent (our) namespace is mapped to the ID @inner in the child (@ppid's)
+ * namespace. In addition, the range of IDs beginning at @map->outer is mapped
+ * to the range of IDs beginning at @map->inner. The tricky bit is that we
+ * cannot let these mappings overlap. We accomplish this by removing a "hole"
+ * from @map, if @outer or @inner overlap it. This may result in one less than
+ * @map->count IDs being mapped from @map. The unmapped IDs are always the
+ * topmost IDs of the mapping (either in the parent or the child namespace).
+ *
+ * Most of the time, this function will be called with @map->outer as some
+ * large ID, @map->inner as 0, and @map->count as a large number (at least
+ * 1000, but less than @map->outer). Typically, there will be no conflict with
+ * @outer. However, @inner may split the mapping for e.g. --map-current-user.
+ *
+ * This function always exec()s or errors out and does not return.
+ */
+static void __attribute__((__noreturn__))
+map_ids(const char *idmapper, int ppid, unsigned int outer, unsigned int inner,
+	struct map_range *map)
+{
+	/* idmapper + pid + 4 * map + NULL */
+	char *argv[15];
+	/* argv - idmapper - "1" - NULL */
+	char args[12][16];
+	int i = 0, j = 0;
+	struct map_range lo, mid, hi;
+	unsigned int inner_offset, outer_offset;
+
+	/* Some helper macros to reduce bookkeeping */
+#define push_str(s) do { \
+	argv[i++] = s; \
+} while (0)
+#define push_ul(x) do { \
+	snprintf(args[j], 16, "%u", x); \
+	push_str(args[j++]); \
+} while (0)
+
+	push_str(xstrdup(idmapper));
+	push_ul(ppid);
+	if ((int)inner == -1) {
+		/*
+		 * If we don't have a "single" mapping, then we can just use
+		 * map directly
+		 */
+		push_ul(map->inner);
+		push_ul(map->outer);
+		push_ul(map->count);
+		push_str(NULL);
+
+		execvp(idmapper, argv);
+		errexec(idmapper);
+	}
+
+	/* If the mappings overlap, remove an ID from map */
+	if ((outer >= map->outer && outer <= map->outer + map->count) ||
+	    (inner >= map->inner && inner <= map->inner + map->count))
+		map->count--;
+
+	/* Determine where the splits between lo, mid, and hi will be */
+	outer_offset = min(outer > map->outer ? outer - map->outer : 0,
+			   map->count);
+	inner_offset = min(inner > map->inner ? inner - map->inner : 0,
+			   map->count);
+
+	/*
+	 * In the worst case, we need three mappings:
+	 * From the bottom of map to either inner or outer
+	 */
+	lo.outer = map->outer;
+	lo.inner = map->inner;
+	lo.count = min(inner_offset, outer_offset);
+
+	/* From the lower of inner or outer to the higher */
+	mid.outer = lo.outer + lo.count;
+	mid.outer += mid.outer == outer;
+	mid.inner = lo.inner + lo.count;
+	mid.inner += mid.inner == inner;
+	mid.count = abs_diff(outer_offset, inner_offset);
+
+	/* And from the higher of inner or outer to the end of the map */
+	hi.outer = mid.outer + mid.count;
+	hi.outer += hi.outer == outer;
+	hi.inner = mid.inner + mid.count;
+	hi.inner += hi.inner == inner;
+	hi.count = map->count - lo.count - mid.count;
+
+	push_ul(inner);
+	push_ul(outer);
+	push_str("1");
+	/* new[gu]idmap doesn't like zero-length mappings, so skip them */
+	if (lo.count) {
+		push_ul(lo.inner);
+		push_ul(lo.outer);
+		push_ul(lo.count);
+	}
+	if (mid.count) {
+		push_ul(mid.inner);
+		push_ul(mid.outer);
+		push_ul(mid.count);
+	}
+	if (hi.count) {
+		push_ul(hi.inner);
+		push_ul(hi.outer);
+		push_ul(hi.count);
+	}
+	push_str(NULL);
+	execvp(idmapper, argv);
+	errexec(idmapper);
+}
+
+/**
+ * map_ids_from_child() - Set up a new uid/gid map
+ * @child: The PID of the child process
+ * @fd: The eventfd to send our PID over
+ * @mapuser: The user to map the current user to (or -1)
+ * @usermap: The range of UIDs to map (or %NULL)
+ * @mapgroup: The group to map the current group to (or -1)
+ * @groupmap: The range of GIDs to map (or %NULL)
+ *
+ * Fork (to pid @child) and wait for a message on @fd. Upon recieving this
+ * message, use newuidmap and newgidmap to set the uid/gid map for our parent's
+ * PID.
+ */
+static void map_ids_from_child(pid_t *child, int *fd, uid_t mapuser,
+			       struct map_range *usermap, gid_t mapgroup,
+			       struct map_range *groupmap)
+{
+	pid_t pid = 0;
+	pid_t ppid = getpid();
+	uint64_t ch;
+
+	*fd = eventfd(0, 0);
+	if (*fd < 0)
+		err(EXIT_FAILURE, _("eventfd failed"));
+
+	*child = fork();
+	if (*child < 0)
+		err(EXIT_FAILURE, _("fork failed"));
+	if (*child)
+		return;
+
+	/* wait for the our parent to call unshare() */
+	if (read_all(*fd, (char *)&ch, sizeof(ch)) != sizeof(ch) ||
+	    ch != PIPE_SYNC_BYTE)
+		err(EXIT_FAILURE, _("failed to read eventfd"));
+	close(*fd);
+
+	/* Avoid forking more than we need to */
+	if (usermap && groupmap) {
+		pid = fork();
+		if (pid < 0)
+			err(EXIT_FAILURE, _("fork failed"));
+		if (pid)
+			waitchild(pid);
+	}
+
+	if (!pid && usermap)
+		map_ids("newuidmap", ppid, geteuid(), mapuser, usermap);
+	if (groupmap)
+		map_ids("newgidmap", ppid, getegid(), mapgroup, groupmap);
+	exit(EXIT_SUCCESS);
+}
+
+
 static void __attribute__((__noreturn__)) usage(void)
 {
 	FILE *out = stdout;
@@ -340,6 +571,10 @@ static void __attribute__((__noreturn__)) usage(void)
 	fputs(_(" --map-group=<gid>|<name>  map current group to gid (implies --user)\n"), out);
 	fputs(_(" -r, --map-root-user       map current user to root (implies --user)\n"), out);
 	fputs(_(" -c, --map-current-user    map current user to itself (implies --user)\n"), out);
+	fputs(_(" --map-users=<outeruid>,<inneruid>,<count>\n"
+		"                           map count users from outeruid to inneruid (implies --user)\n"), out);
+	fputs(_(" --map-groups=<outergid>,<innergid>,<count>\n"
+		"                           map count groups from outergid to innergid (implies --user)\n"), out);
 	fputs(USAGE_SEPARATOR, out);
 	fputs(_(" --kill-child[=<signame>]  when dying, kill the forked child (implies --fork)\n"
 		"                             defaults to SIGKILL\n"), out);
@@ -374,7 +609,9 @@ int main(int argc, char *argv[])
 		OPT_MONOTONIC,
 		OPT_BOOTTIME,
 		OPT_MAPUSER,
+		OPT_MAPUSERS,
 		OPT_MAPGROUP,
+		OPT_MAPGROUPS,
 	};
 	static const struct option longopts[] = {
 		{ "help",          no_argument,       NULL, 'h'             },
@@ -393,7 +630,9 @@ int main(int argc, char *argv[])
 		{ "kill-child",    optional_argument, NULL, OPT_KILLCHILD   },
 		{ "mount-proc",    optional_argument, NULL, OPT_MOUNTPROC   },
 		{ "map-user",      required_argument, NULL, OPT_MAPUSER     },
+		{ "map-users",     required_argument, NULL, OPT_MAPUSERS    },
 		{ "map-group",     required_argument, NULL, OPT_MAPGROUP    },
+		{ "map-groups",    required_argument, NULL, OPT_MAPGROUPS   },
 		{ "map-root-user", no_argument,       NULL, 'r'             },
 		{ "map-current-user", no_argument,    NULL, 'c'             },
 		{ "propagation",   required_argument, NULL, OPT_PROPAGATION },
@@ -413,13 +652,16 @@ int main(int argc, char *argv[])
 	int c, forkit = 0;
 	uid_t mapuser = -1;
 	gid_t mapgroup = -1;
+	struct map_range *usermap = NULL;
+	struct map_range *groupmap = NULL;
 	int kill_child_signo = 0; /* 0 means --kill-child was not used */
 	const char *procmnt = NULL;
 	const char *newroot = NULL;
 	const char *newdir = NULL;
-	pid_t pid_bind = 0;
+	pid_t pid_bind = 0, pid_idmap = 0;
 	pid_t pid = 0;
 	int fds[2];
+	int efd;
 	int status;
 	unsigned long propagation = UNSHARE_PROPAGATION_DEFAULT;
 	int force_uid = 0, force_gid = 0;
@@ -503,6 +745,14 @@ int main(int argc, char *argv[])
 			mapuser = real_euid;
 			mapgroup = real_egid;
 			break;
+		case OPT_MAPUSERS:
+			unshare_flags |= CLONE_NEWUSER;
+			usermap = get_map_range(optarg);
+			break;
+		case OPT_MAPGROUPS:
+			unshare_flags |= CLONE_NEWUSER;
+			groupmap = get_map_range(optarg);
+			break;
 		case OPT_SETGROUPS:
 			setgrpcmd = setgroups_str2id(optarg);
 			break;
@@ -565,9 +815,23 @@ int main(int argc, char *argv[])
 	if (npersists && (unshare_flags & CLONE_NEWNS))
 		bind_ns_files_from_child(&pid_bind, fds);
 
+	if (usermap || groupmap)
+		map_ids_from_child(&pid_idmap, &efd, mapuser, usermap,
+				   mapgroup, groupmap);
+
 	if (-1 == unshare(unshare_flags))
 		err(EXIT_FAILURE, _("unshare failed"));
 
+	if (usermap || groupmap) {
+		uint64_t ch = PIPE_SYNC_BYTE;
+
+		/* Tell child we've called unshare() */
+		write_all(efd, &ch, sizeof(ch));
+		close(efd);
+
+		waitchild(pid_idmap);
+	}
+
 	if (force_boottime)
 		settime(boottime, CLOCK_BOOTTIME);
 
@@ -628,14 +892,14 @@ int main(int argc, char *argv[])
 	if (kill_child_signo != 0 && prctl(PR_SET_PDEATHSIG, kill_child_signo) < 0)
 		err(EXIT_FAILURE, "prctl failed");
 
-        if (mapuser != (uid_t) -1)
+        if (mapuser != (uid_t) -1 && !usermap)
 		map_id(_PATH_PROC_UIDMAP, mapuser, real_euid);
 
         /* Since Linux 3.19 unprivileged writing of /proc/self/gid_map
          * has been disabled unless /proc/self/setgroups is written
          * first to permanently disable the ability to call setgroups
          * in that user namespace. */
-	if (mapgroup != (gid_t) -1) {
+	if (mapgroup != (gid_t) -1 && !groupmap) {
 		if (setgrpcmd == SETGROUPS_ALLOW)
 			errx(EXIT_FAILURE, _("options --setgroups=allow and "
 					"--map-group are mutually exclusive"));
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 4/5] unshare: Add option to automatically create user and group maps
  2021-11-17  2:10 [PATCH 0/5] unshare: Add support for mapping ranges of user/group IDs Sean Anderson
                   ` (2 preceding siblings ...)
  2021-11-17  2:10 ` [PATCH 3/5] unshare: Add options to map blocks of user/group IDs Sean Anderson
@ 2021-11-17  2:10 ` Sean Anderson
  2021-11-23 14:40   ` Karel Zak
  2021-11-17  2:10 ` [PATCH 5/5] unshare: Document --map-{groups,users,auto} Sean Anderson
  4 siblings, 1 reply; 10+ messages in thread
From: Sean Anderson @ 2021-11-17  2:10 UTC (permalink / raw)
  To: util-linux, Karel Zak
  Cc: Mikhail Gusarov, Matthew Harm Bekkema, James Peach, Sean Anderson

This option is designed to handle the "garden path" user/group ID
mapping:

- The user has one big map in /etc/sub[u,g]id
- The user wants to map as many user and group IDs as they can,
  especially the first 1000 users and groups.

The "auto" map is designed to handle this. We find the first map
matching the current user, and then map the whole thing to the ID range
starting at ID 0.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 sys-utils/unshare.c | 74 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 74 insertions(+)

diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c
index 247925aa6..33d1a829c 100644
--- a/sys-utils/unshare.c
+++ b/sys-utils/unshare.c
@@ -373,6 +373,72 @@ static struct map_range *get_map_range(const char *s)
 	return ret;
 }
 
+/**
+ * read_subid_range() - Look up a user's sub[gu]id range
+ * @filename: The file to look up the range from. This should be either
+ *            ``/etc/subuid`` or ``/etc/subgid``.
+ * @uid: The uid of the user whose range we should look up.
+ *
+ * This finds the first subid range matching @uid in @filename.
+ */
+static struct map_range *read_subid_range(char *filename, uid_t uid)
+{
+        char *line = NULL, *pwbuf;
+        FILE *idmap;
+        size_t n;
+	struct passwd *pw;
+	struct map_range *map;
+
+	map = xmalloc(sizeof(*map));
+	map->inner = 0;
+
+	pw = xgetpwuid(uid, &pwbuf);
+	if (!pw)
+		errx(EXIT_FAILURE, _("you (user %d) don't exist."), uid);
+
+        idmap = fopen(filename, "r");
+        if (!idmap)
+                err(EXIT_FAILURE, _("could not open '%s'"), filename);
+
+        /*
+         * Each line in sub[ug]idmap looks like
+         * username:subuid:count
+         * OR
+         * uid:subuid:count
+         */
+        while (getline(&line, &n, idmap) != -1) {
+                char *rest, *s;
+
+                rest = strchr(line, ':');
+                if (!rest)
+                        continue;
+                *rest = '\0';
+
+                if (strcmp(line, pw->pw_name) &&
+                    strtoul(line, NULL, 10) != pw->pw_uid)
+                        continue;
+
+                s = rest + 1;
+                rest = strchr(s, ':');
+                if (!rest)
+                        continue;
+                *rest = '\0';
+		map->outer = strtoul_or_err(s, _("failed to parse subid map"));
+
+                s = rest + 1;
+                rest = strchr(s, '\n');
+                if (rest)
+                        *rest = '\0';
+		map->count = strtoul_or_err(s, _("failed to parse subid map"));
+
+                fclose(idmap);
+                return map;
+        }
+
+        err(EXIT_FAILURE, _("no line matching user \"%s\" in %s"),
+	    pw->pw_name, filename);
+}
+
 /**
  * map_ids() - Create a new uid/gid map
  * @idmapper: Either newuidmap or newgidmap
@@ -571,6 +637,7 @@ static void __attribute__((__noreturn__)) usage(void)
 	fputs(_(" --map-group=<gid>|<name>  map current group to gid (implies --user)\n"), out);
 	fputs(_(" -r, --map-root-user       map current user to root (implies --user)\n"), out);
 	fputs(_(" -c, --map-current-user    map current user to itself (implies --user)\n"), out);
+	fputs(_(" --map-auto                map users and groups automatically (implies --user)\n"), out);
 	fputs(_(" --map-users=<outeruid>,<inneruid>,<count>\n"
 		"                           map count users from outeruid to inneruid (implies --user)\n"), out);
 	fputs(_(" --map-groups=<outergid>,<innergid>,<count>\n"
@@ -612,6 +679,7 @@ int main(int argc, char *argv[])
 		OPT_MAPUSERS,
 		OPT_MAPGROUP,
 		OPT_MAPGROUPS,
+		OPT_MAPAUTO,
 	};
 	static const struct option longopts[] = {
 		{ "help",          no_argument,       NULL, 'h'             },
@@ -635,6 +703,7 @@ int main(int argc, char *argv[])
 		{ "map-groups",    required_argument, NULL, OPT_MAPGROUPS   },
 		{ "map-root-user", no_argument,       NULL, 'r'             },
 		{ "map-current-user", no_argument,    NULL, 'c'             },
+		{ "map-auto",      no_argument,       NULL, OPT_MAPAUTO     },
 		{ "propagation",   required_argument, NULL, OPT_PROPAGATION },
 		{ "setgroups",     required_argument, NULL, OPT_SETGROUPS   },
 		{ "keep-caps",     no_argument,       NULL, OPT_KEEPCAPS    },
@@ -753,6 +822,11 @@ int main(int argc, char *argv[])
 			unshare_flags |= CLONE_NEWUSER;
 			groupmap = get_map_range(optarg);
 			break;
+		case OPT_MAPAUTO:
+			unshare_flags |= CLONE_NEWUSER;
+			usermap = read_subid_range("/etc/subuid", real_euid);
+			groupmap = read_subid_range("/etc/subgid", real_egid);
+			break;
 		case OPT_SETGROUPS:
 			setgrpcmd = setgroups_str2id(optarg);
 			break;
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 5/5] unshare: Document --map-{groups,users,auto}
  2021-11-17  2:10 [PATCH 0/5] unshare: Add support for mapping ranges of user/group IDs Sean Anderson
                   ` (3 preceding siblings ...)
  2021-11-17  2:10 ` [PATCH 4/5] unshare: Add option to automatically create user and group maps Sean Anderson
@ 2021-11-17  2:10 ` Sean Anderson
  4 siblings, 0 replies; 10+ messages in thread
From: Sean Anderson @ 2021-11-17  2:10 UTC (permalink / raw)
  To: util-linux, Karel Zak
  Cc: Mikhail Gusarov, Matthew Harm Bekkema, James Peach, Sean Anderson

This documents the new options added in the previous few commits.
I have added another example to better demonstrate the these
options. The actual use is fairly straightforward, but the descriptions
are on the pithier side.

Signed-off-by: Sean Anderson <seanga2@gmail.com>
---

 sys-utils/unshare.1.adoc | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/sys-utils/unshare.1.adoc b/sys-utils/unshare.1.adoc
index 74183ebc1..531d42be1 100644
--- a/sys-utils/unshare.1.adoc
+++ b/sys-utils/unshare.1.adoc
@@ -93,9 +93,18 @@ Just before running the program, mount the proc filesystem at _mountpoint_ (defa
 **--map-user=**__uid|name__::
 Run the program only after the current effective user ID has been mapped to _uid_. If this option is specified multiple times, the last occurrence takes precedence. This option implies *--user*.
 
+**--map-users=**__outeruid,inneruid,count__::
+Run the program only after the block of user IDs of size _count_ beginning at _outeruid_ has been mapped to the block of user IDs beginning at _inneruid_. This mapping is created with *newuidmap*(1). If the range of user IDs overlaps with the mapping specified by *--map-user*, then a "hole" will be removed from the mapping. This may result in the highest user ID of the mapping not being mapped. If this option is specified multiple times, the last occurrence takes precedence. This option implies *--user*.
+
 **--map-group=**__gid|name__::
 Run the program only after the current effective group ID has been mapped to _gid_. If this option is specified multiple times, the last occurrence takes precedence. This option implies *--setgroups=deny* and *--user*.
 
+**--map-groups=**__outergid,innergid,count__::
+Run the program only after the block of group IDs of size _count_ beginning at _outergid_ has been mapped to the block of group IDs beginning at _innergid_. This mapping is created with *newgidmap*(1). If the range of group IDs overlaps with the mapping specified by *--map-group*, then a "hole" will be removed from the mapping. This may result in the highest group ID of the mapping not being mapped. If this option is specified multiple times, the last occurrence takes precedence. This option implies *--group*.
+
+**--map-auto**::
+Map the first block of user IDs owned by the effective user from _/etc/subuid_ to a block starting at user ID 0. In the same manner, also map the first block of group IDs owned by the effective group from _/etc/subgid_ to a block starting at group ID 0. This option is intended to handle the general case where all available subordinate user and group IDs are used to map as much of the ID space as possible. This option is equivalent to specifying **--map-users** and **--map-groups** with appropriate arguments.
+
 *-r*, *--map-root-user*::
 Run the program only after the current effective user and group IDs have been mapped to the superuser UID and GID in the newly created user namespace. This makes it possible to conveniently gain capabilities needed to manage various aspects of the newly created namespaces (such as configuring interfaces in the network namespace or mounting filesystems in the mount namespace) even when run unprivileged. As a mere convenience feature, it does not support more sophisticated use cases, such as mapping multiple ranges of UIDs and GIDs. This option implies *--setgroups=deny* and *--user*. This option is equivalent to *--map-user=0 --map-group=0*.
 
@@ -160,6 +169,27 @@ root
          0       1000          1
 ....
 
+As an unprivileged user, create a user namespace where the first 65536 IDs are all mapped, and the user's credentials are mapped to the root IDs inside the namespace. The map is determined by the subordinate IDs assigned in *subuid*(5) and *subgid*(5). Demonstrate this mapping by creating a file with user ID 1 and group ID 1. For brevity, only the user ID mappings are shown:
+
+....
+$ id -u
+1000
+$ cat /etc/subuid
+1000:100000:65536
+$ unshare --user --map-auto --map-root-user
+# id -u
+0
+# cat /proc/self/uid_map
+         0       1000          1
+         1     100000      65535
+# touch file; chown 1:1 file
+# ls -ln --time-style=+ file
+-rw-r--r-- 1 1 1 0  file
+# exit
+$ ls -ln --time-style=+ file
+-rw-r--r-- 1 100000 100000 0  file
+....
+
 The first of the following commands creates a new persistent UTS namespace and modifies the hostname as seen in that namespace. The namespace is then entered with *nsenter*(1) in order to display the modified hostname; this step demonstrates that the UTS namespace continues to exist even though the namespace had no member processes after the *unshare* command terminated. The namespace is then destroyed by removing the bind mount.
 
 ....
@@ -235,6 +265,8 @@ mailto:kzak@redhat.com[Karel Zak]
 
 == SEE ALSO
 
+*newuidmap*(1)
+*newgidmap*(1)
 *clone*(2),
 *unshare*(2),
 *namespaces*(7),
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/5] unshare: Add options to map blocks of user/group IDs
  2021-11-17  2:10 ` [PATCH 3/5] unshare: Add options to map blocks of user/group IDs Sean Anderson
@ 2021-11-23 14:33   ` Karel Zak
  2021-11-24  2:10     ` Sean Anderson
  0 siblings, 1 reply; 10+ messages in thread
From: Karel Zak @ 2021-11-23 14:33 UTC (permalink / raw)
  To: Sean Anderson
  Cc: util-linux, Mikhail Gusarov, Matthew Harm Bekkema, James Peach


 Hi Sean,

the patches looks pretty good, some notes:


On Tue, Nov 16, 2021 at 09:10:36PM -0500, Sean Anderson wrote:
> +static int uint_to_id(const char *cname, size_t sz)
> +{
> +	char old, *name = (char *)cname;

I'm not sure with this, it uses "const char" for good reason. It's
usually better to not touch process argv[].

> +	unsigned long ret;
> +
> +	/* Add a NUL-terminator */
> +	old = name[sz];
> +	name[sz] = '\0';
> +	ret = strtoul_or_err(name, _("could not parse ID"));
> +	if (ret > UINT_MAX)
> +		errx(EXIT_FAILURE, "id %lu is too big", ret);
> +	/* And put back the old value to preserve const-ness */
> +	name[sz] = old;
> +	return ret;
> +}

I think we can keep it simple and robust:

#define UID_BUFSIZ  sizeof(stringify_value(ULONG_MAX))

static int uint_to_id(const char *cname, size_t sz)
{
    char buf[UID_BUFSIZ];
    unsigned long id;

    mem2strcpy(buf, cname, sz, sizeof(buf));
    id = strtoul_or_err(buf, _("could not parse ID"));
    return id;
}

> +/**
> + * map_ids() - Create a new uid/gid map
> + * @idmapper: Either newuidmap or newgidmap
> + * @ppid: Pid to set the map for
> + * @outer: ID outside the namespace for a single map.
> + * @inner: ID inside the namespace for a single map. May be -1 to only use @map.
> + * @map: A range of IDs to map
> + *
> + * This creates a new uid/gid map for @ppid using @idmapper. The ID @outer in
> + * the parent (our) namespace is mapped to the ID @inner in the child (@ppid's)
> + * namespace. In addition, the range of IDs beginning at @map->outer is mapped
> + * to the range of IDs beginning at @map->inner. The tricky bit is that we
> + * cannot let these mappings overlap. We accomplish this by removing a "hole"
> + * from @map, if @outer or @inner overlap it. This may result in one less than
> + * @map->count IDs being mapped from @map. The unmapped IDs are always the
> + * topmost IDs of the mapping (either in the parent or the child namespace).
> + *
> + * Most of the time, this function will be called with @map->outer as some
> + * large ID, @map->inner as 0, and @map->count as a large number (at least
> + * 1000, but less than @map->outer). Typically, there will be no conflict with
> + * @outer. However, @inner may split the mapping for e.g. --map-current-user.
> + *
> + * This function always exec()s or errors out and does not return.
> + */
> +static void __attribute__((__noreturn__))
> +map_ids(const char *idmapper, int ppid, unsigned int outer, unsigned int inner,
> +	struct map_range *map)
> +{
> +	/* idmapper + pid + 4 * map + NULL */
> +	char *argv[15];
> +	/* argv - idmapper - "1" - NULL */
> +	char args[12][16];

May be we can minimize magic constants and use some readable macro here, what about:

args[12][UID_BUFSIZ]

> +	int i = 0, j = 0;
> +	struct map_range lo, mid, hi;
> +	unsigned int inner_offset, outer_offset;
> +
> +	/* Some helper macros to reduce bookkeeping */
> +#define push_str(s) do { \
> +	argv[i++] = s; \
> +} while (0)
> +#define push_ul(x) do { \
> +	snprintf(args[j], 16, "%u", x); \

snprintf(args[j], UID_BUFSIZ, "%u", x);

> +	push_str(args[j++]); \
> +} while (0)

...

> +/**
> + * map_ids_from_child() - Set up a new uid/gid map
> + * @child: The PID of the child process
> + * @fd: The eventfd to send our PID over
> + * @mapuser: The user to map the current user to (or -1)
> + * @usermap: The range of UIDs to map (or %NULL)
> + * @mapgroup: The group to map the current group to (or -1)
> + * @groupmap: The range of GIDs to map (or %NULL)
> + *
> + * Fork (to pid @child) and wait for a message on @fd. Upon recieving this
> + * message, use newuidmap and newgidmap to set the uid/gid map for our parent's
> + * PID.
> + */
> +static void map_ids_from_child(pid_t *child, int *fd, uid_t mapuser,
> +			       struct map_range *usermap, gid_t mapgroup,
> +			       struct map_range *groupmap)
> +{
> +	pid_t pid = 0;
> +	pid_t ppid = getpid();
> +	uint64_t ch;
> +
> +	*fd = eventfd(0, 0);
> +	if (*fd < 0)
> +		err(EXIT_FAILURE, _("eventfd failed"));
> +
> +	*child = fork();
> +	if (*child < 0)
> +		err(EXIT_FAILURE, _("fork failed"));
> +	if (*child)
> +		return;
> +
> +	/* wait for the our parent to call unshare() */
> +	if (read_all(*fd, (char *)&ch, sizeof(ch)) != sizeof(ch) ||
> +	    ch != PIPE_SYNC_BYTE)
> +		err(EXIT_FAILURE, _("failed to read eventfd"));
> +	close(*fd);
> +
> +	/* Avoid forking more than we need to */
> +	if (usermap && groupmap) {
> +		pid = fork();
> +		if (pid < 0)
> +			err(EXIT_FAILURE, _("fork failed"));
> +		if (pid)
> +			waitchild(pid);
> +	}

I like the idea with eventfd(). What about to use it also in
bind_ns_files_from_child()? Now we use pipe() here.

It seems we can consolidate the code and add small functions
like

 sync_with_parent()
 sync_with_child()

to hide SYNC_BYTE read(), write() and waitchild().

 ...

> @@ -413,13 +652,16 @@ int main(int argc, char *argv[])
>  	int c, forkit = 0;
>  	uid_t mapuser = -1;
>  	gid_t mapgroup = -1;
> +	struct map_range *usermap = NULL;
> +	struct map_range *groupmap = NULL;
>  	int kill_child_signo = 0; /* 0 means --kill-child was not used */
>  	const char *procmnt = NULL;
>  	const char *newroot = NULL;
>  	const char *newdir = NULL;
> -	pid_t pid_bind = 0;
> +	pid_t pid_bind = 0, pid_idmap = 0;
>  	pid_t pid = 0;
>  	int fds[2];
> +	int efd;

int fd_idmap, fd_bind;


 Karel

-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 4/5] unshare: Add option to automatically create user and group maps
  2021-11-17  2:10 ` [PATCH 4/5] unshare: Add option to automatically create user and group maps Sean Anderson
@ 2021-11-23 14:40   ` Karel Zak
  2021-11-24  2:11     ` Sean Anderson
  0 siblings, 1 reply; 10+ messages in thread
From: Karel Zak @ 2021-11-23 14:40 UTC (permalink / raw)
  To: Sean Anderson
  Cc: util-linux, Mikhail Gusarov, Matthew Harm Bekkema, James Peach

On Tue, Nov 16, 2021 at 09:10:37PM -0500, Sean Anderson wrote:
> This option is designed to handle the "garden path" user/group ID
> mapping:
> 
> - The user has one big map in /etc/sub[u,g]id
> - The user wants to map as many user and group IDs as they can,
>   especially the first 1000 users and groups.
> 
> The "auto" map is designed to handle this. We find the first map
> matching the current user, and then map the whole thing to the ID range
> starting at ID 0.

...

>   * map_ids() - Create a new uid/gid map
>   * @idmapper: Either newuidmap or newgidmap
> @@ -571,6 +637,7 @@ static void __attribute__((__noreturn__)) usage(void)
>  	fputs(_(" --map-group=<gid>|<name>  map current group to gid (implies --user)\n"), out);
>  	fputs(_(" -r, --map-root-user       map current user to root (implies --user)\n"), out);
>  	fputs(_(" -c, --map-current-user    map current user to itself (implies --user)\n"), out);
> +	fputs(_(" --map-auto                map users and groups automatically (implies --user)\n"), out);
>  	fputs(_(" --map-users=<outeruid>,<inneruid>,<count>\n"
>  		"                           map count users from outeruid to inneruid (implies --user)\n"), out);
>  	fputs(_(" --map-groups=<outergid>,<innergid>,<count>\n"


What about to support "auto" as a placeholder too:

  --map-users=auto
  --map-groups=auto

in this case you can select what you want to map (UID/GID)
automatically.

> +		case OPT_MAPAUTO:
> +			unshare_flags |= CLONE_NEWUSER;
> +			usermap = read_subid_range("/etc/subuid", real_euid);
> +			groupmap = read_subid_range("/etc/subgid", real_egid);
> +			break;

Please, add _PATH_SUBUID and _PATH_SUBGID to include/pathnames.h. We
usually do not use paths in the code.

  Karel


-- 
 Karel Zak  <kzak@redhat.com>
 http://karelzak.blogspot.com


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 3/5] unshare: Add options to map blocks of user/group IDs
  2021-11-23 14:33   ` Karel Zak
@ 2021-11-24  2:10     ` Sean Anderson
  0 siblings, 0 replies; 10+ messages in thread
From: Sean Anderson @ 2021-11-24  2:10 UTC (permalink / raw)
  To: Karel Zak; +Cc: util-linux, Mikhail Gusarov, Matthew Harm Bekkema, James Peach

On 11/23/21 9:33 AM, Karel Zak wrote:
> 
>   Hi Sean,
> 
> the patches looks pretty good, some notes:
> 
> 
> On Tue, Nov 16, 2021 at 09:10:36PM -0500, Sean Anderson wrote:
>> +static int uint_to_id(const char *cname, size_t sz)
>> +{
>> +	char old, *name = (char *)cname;
> 
> I'm not sure with this, it uses "const char" for good reason. It's
> usually better to not touch process argv[].

*shrug* no one else is using it :)

>> +	unsigned long ret;
>> +
>> +	/* Add a NUL-terminator */
>> +	old = name[sz];
>> +	name[sz] = '\0';
>> +	ret = strtoul_or_err(name, _("could not parse ID"));
>> +	if (ret > UINT_MAX)
>> +		errx(EXIT_FAILURE, "id %lu is too big", ret);
>> +	/* And put back the old value to preserve const-ness */
>> +	name[sz] = old;
>> +	return ret;
>> +}
> 
> I think we can keep it simple and robust:
> 
> #define UID_BUFSIZ  sizeof(stringify_value(ULONG_MAX))

That's a nice trick

> static int uint_to_id(const char *cname, size_t sz)
> {
>      char buf[UID_BUFSIZ];
>      unsigned long id;
> 
>      mem2strcpy(buf, cname, sz, sizeof(buf));
>      id = strtoul_or_err(buf, _("could not parse ID"));
>      return id;
> }

Ok, this looks good.

>> +/**
>> + * map_ids() - Create a new uid/gid map
>> + * @idmapper: Either newuidmap or newgidmap
>> + * @ppid: Pid to set the map for
>> + * @outer: ID outside the namespace for a single map.
>> + * @inner: ID inside the namespace for a single map. May be -1 to only use @map.
>> + * @map: A range of IDs to map
>> + *
>> + * This creates a new uid/gid map for @ppid using @idmapper. The ID @outer in
>> + * the parent (our) namespace is mapped to the ID @inner in the child (@ppid's)
>> + * namespace. In addition, the range of IDs beginning at @map->outer is mapped
>> + * to the range of IDs beginning at @map->inner. The tricky bit is that we
>> + * cannot let these mappings overlap. We accomplish this by removing a "hole"
>> + * from @map, if @outer or @inner overlap it. This may result in one less than
>> + * @map->count IDs being mapped from @map. The unmapped IDs are always the
>> + * topmost IDs of the mapping (either in the parent or the child namespace).
>> + *
>> + * Most of the time, this function will be called with @map->outer as some
>> + * large ID, @map->inner as 0, and @map->count as a large number (at least
>> + * 1000, but less than @map->outer). Typically, there will be no conflict with
>> + * @outer. However, @inner may split the mapping for e.g. --map-current-user.
>> + *
>> + * This function always exec()s or errors out and does not return.
>> + */
>> +static void __attribute__((__noreturn__))
>> +map_ids(const char *idmapper, int ppid, unsigned int outer, unsigned int inner,
>> +	struct map_range *map)
>> +{
>> +	/* idmapper + pid + 4 * map + NULL */
>> +	char *argv[15];
>> +	/* argv - idmapper - "1" - NULL */
>> +	char args[12][16];
> 
> May be we can minimize magic constants and use some readable macro here, what about:
> 
> args[12][UID_BUFSIZ]

Sure.

>> +	int i = 0, j = 0;
>> +	struct map_range lo, mid, hi;
>> +	unsigned int inner_offset, outer_offset;
>> +
>> +	/* Some helper macros to reduce bookkeeping */
>> +#define push_str(s) do { \
>> +	argv[i++] = s; \
>> +} while (0)
>> +#define push_ul(x) do { \
>> +	snprintf(args[j], 16, "%u", x); \
> 
> snprintf(args[j], UID_BUFSIZ, "%u", x);
> 
>> +	push_str(args[j++]); \
>> +} while (0)
> 
> ...
> 
>> +/**
>> + * map_ids_from_child() - Set up a new uid/gid map
>> + * @child: The PID of the child process
>> + * @fd: The eventfd to send our PID over
>> + * @mapuser: The user to map the current user to (or -1)
>> + * @usermap: The range of UIDs to map (or %NULL)
>> + * @mapgroup: The group to map the current group to (or -1)
>> + * @groupmap: The range of GIDs to map (or %NULL)
>> + *
>> + * Fork (to pid @child) and wait for a message on @fd. Upon recieving this
>> + * message, use newuidmap and newgidmap to set the uid/gid map for our parent's
>> + * PID.
>> + */
>> +static void map_ids_from_child(pid_t *child, int *fd, uid_t mapuser,
>> +			       struct map_range *usermap, gid_t mapgroup,
>> +			       struct map_range *groupmap)
>> +{
>> +	pid_t pid = 0;
>> +	pid_t ppid = getpid();
>> +	uint64_t ch;
>> +
>> +	*fd = eventfd(0, 0);
>> +	if (*fd < 0)
>> +		err(EXIT_FAILURE, _("eventfd failed"));
>> +
>> +	*child = fork();
>> +	if (*child < 0)
>> +		err(EXIT_FAILURE, _("fork failed"));
>> +	if (*child)
>> +		return;
>> +
>> +	/* wait for the our parent to call unshare() */
>> +	if (read_all(*fd, (char *)&ch, sizeof(ch)) != sizeof(ch) ||
>> +	    ch != PIPE_SYNC_BYTE)
>> +		err(EXIT_FAILURE, _("failed to read eventfd"));
>> +	close(*fd);
>> +
>> +	/* Avoid forking more than we need to */
>> +	if (usermap && groupmap) {
>> +		pid = fork();
>> +		if (pid < 0)
>> +			err(EXIT_FAILURE, _("fork failed"));
>> +		if (pid)
>> +			waitchild(pid);
>> +	}
> 
> I like the idea with eventfd(). What about to use it also in
> bind_ns_files_from_child()? Now we use pipe() here.
> 
> It seems we can consolidate the code and add small functions
> like
> 
>   sync_with_parent()
>   sync_with_child()
> 
> to hide SYNC_BYTE read(), write() and waitchild().

OK. I will look into converting the pipe user as well.

>   ...
> 
>> @@ -413,13 +652,16 @@ int main(int argc, char *argv[])
>>   	int c, forkit = 0;
>>   	uid_t mapuser = -1;
>>   	gid_t mapgroup = -1;
>> +	struct map_range *usermap = NULL;
>> +	struct map_range *groupmap = NULL;
>>   	int kill_child_signo = 0; /* 0 means --kill-child was not used */
>>   	const char *procmnt = NULL;
>>   	const char *newroot = NULL;
>>   	const char *newdir = NULL;
>> -	pid_t pid_bind = 0;
>> +	pid_t pid_bind = 0, pid_idmap = 0;
>>   	pid_t pid = 0;
>>   	int fds[2];
>> +	int efd;
> 
> int fd_idmap, fd_bind;
> 
> 
>   Karel
> 

Thanks for the review.

--Sean

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 4/5] unshare: Add option to automatically create user and group maps
  2021-11-23 14:40   ` Karel Zak
@ 2021-11-24  2:11     ` Sean Anderson
  0 siblings, 0 replies; 10+ messages in thread
From: Sean Anderson @ 2021-11-24  2:11 UTC (permalink / raw)
  To: Karel Zak; +Cc: util-linux, Mikhail Gusarov, Matthew Harm Bekkema, James Peach

On 11/23/21 9:40 AM, Karel Zak wrote:
> On Tue, Nov 16, 2021 at 09:10:37PM -0500, Sean Anderson wrote:
>> This option is designed to handle the "garden path" user/group ID
>> mapping:
>>
>> - The user has one big map in /etc/sub[u,g]id
>> - The user wants to map as many user and group IDs as they can,
>>    especially the first 1000 users and groups.
>>
>> The "auto" map is designed to handle this. We find the first map
>> matching the current user, and then map the whole thing to the ID range
>> starting at ID 0.
> 
> ...
> 
>>    * map_ids() - Create a new uid/gid map
>>    * @idmapper: Either newuidmap or newgidmap
>> @@ -571,6 +637,7 @@ static void __attribute__((__noreturn__)) usage(void)
>>   	fputs(_(" --map-group=<gid>|<name>  map current group to gid (implies --user)\n"), out);
>>   	fputs(_(" -r, --map-root-user       map current user to root (implies --user)\n"), out);
>>   	fputs(_(" -c, --map-current-user    map current user to itself (implies --user)\n"), out);
>> +	fputs(_(" --map-auto                map users and groups automatically (implies --user)\n"), out);
>>   	fputs(_(" --map-users=<outeruid>,<inneruid>,<count>\n"
>>   		"                           map count users from outeruid to inneruid (implies --user)\n"), out);
>>   	fputs(_(" --map-groups=<outergid>,<innergid>,<count>\n"
> 
> 
> What about to support "auto" as a placeholder too:
> 
>    --map-users=auto
>    --map-groups=auto
> 
> in this case you can select what you want to map (UID/GID)
> automatically.

That sounds good.

>> +		case OPT_MAPAUTO:
>> +			unshare_flags |= CLONE_NEWUSER;
>> +			usermap = read_subid_range("/etc/subuid", real_euid);
>> +			groupmap = read_subid_range("/etc/subgid", real_egid);
>> +			break;
> 
> Please, add _PATH_SUBUID and _PATH_SUBGID to include/pathnames.h. We
> usually do not use paths in the code.

OK. I did not know about that. Will add.

--Sean


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2021-11-24  2:11 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-17  2:10 [PATCH 0/5] unshare: Add support for mapping ranges of user/group IDs Sean Anderson
2021-11-17  2:10 ` [PATCH 1/5] include/c: Add abs_diff macro Sean Anderson
2021-11-17  2:10 ` [PATCH 2/5] unshare: Add waitchild helper Sean Anderson
2021-11-17  2:10 ` [PATCH 3/5] unshare: Add options to map blocks of user/group IDs Sean Anderson
2021-11-23 14:33   ` Karel Zak
2021-11-24  2:10     ` Sean Anderson
2021-11-17  2:10 ` [PATCH 4/5] unshare: Add option to automatically create user and group maps Sean Anderson
2021-11-23 14:40   ` Karel Zak
2021-11-24  2:11     ` Sean Anderson
2021-11-17  2:10 ` [PATCH 5/5] unshare: Document --map-{groups,users,auto} Sean Anderson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).