From: Sean Anderson <seanga2@gmail.com>
To: util-linux@vger.kernel.org, Karel Zak <kzak@redhat.com>
Cc: Mikhail Gusarov <dottedmag@dottedmag.net>,
Matthew Harm Bekkema <id@mbekkema.name>,
James Peach <jpeach@apache.org>,
Sean Anderson <seanga2@gmail.com>
Subject: [PATCH 4/5] unshare: Add option to automatically create user and group maps
Date: Tue, 16 Nov 2021 21:10:37 -0500 [thread overview]
Message-ID: <20211117021038.823851-5-seanga2@gmail.com> (raw)
In-Reply-To: <20211117021038.823851-1-seanga2@gmail.com>
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
next prev parent reply other threads:[~2021-11-17 2:10 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
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 ` Sean Anderson [this message]
2021-11-23 14:40 ` [PATCH 4/5] unshare: Add option to automatically create user and group maps 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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20211117021038.823851-5-seanga2@gmail.com \
--to=seanga2@gmail.com \
--cc=dottedmag@dottedmag.net \
--cc=id@mbekkema.name \
--cc=jpeach@apache.org \
--cc=kzak@redhat.com \
--cc=util-linux@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).