]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
unshare: Add option to automatically create user and group maps
authorSean Anderson <seanga2@gmail.com>
Wed, 24 Nov 2021 18:26:17 +0000 (13:26 -0500)
committerKarel Zak <kzak@redhat.com>
Wed, 1 Dec 2021 12:36:00 +0000 (13:36 +0100)
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>
include/pathnames.h
sys-utils/unshare.c

index 17e4b940f561a4e3a2c740fe21960ab314b1ffa7..5fbb54ec343191e2af2a93c39549d946b75c17f6 100644 (file)
@@ -98,6 +98,9 @@
 #define _PATH_PROC_LOCKS        "/proc/locks"
 #define _PATH_PROC_CDROMINFO   "/proc/sys/dev/cdrom/info"
 
+/* unshare paths */
+#define _PATH_SUBUID           "/etc/subuid"
+#define _PATH_SUBGID           "/etc/subgid"
 #define _PATH_PROC_UIDMAP      "/proc/self/uid_map"
 #define _PATH_PROC_GIDMAP      "/proc/self/gid_map"
 #define _PATH_PROC_SETGROUPS   "/proc/self/setgroups"
index 6d0a563344993b8cb62f637159c02aa331759cd8..b5a18ed950ad3ff31486ac4cda1d361e703ddb55 100644 (file)
@@ -418,6 +418,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
@@ -603,6 +669,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"
@@ -644,6 +711,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'             },
@@ -667,6 +735,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    },
@@ -778,11 +847,22 @@ int main(int argc, char *argv[])
                        break;
                case OPT_MAPUSERS:
                        unshare_flags |= CLONE_NEWUSER;
-                       usermap = get_map_range(optarg);
+                       if (!strcmp(optarg, "auto"))
+                               usermap = read_subid_range(_PATH_SUBUID, real_euid);
+                       else
+                               usermap = get_map_range(optarg);
                        break;
                case OPT_MAPGROUPS:
                        unshare_flags |= CLONE_NEWUSER;
-                       groupmap = get_map_range(optarg);
+                       if (!strcmp(optarg, "auto"))
+                               groupmap = read_subid_range(_PATH_SUBGID, real_egid);
+                       else
+                               groupmap = get_map_range(optarg);
+                       break;
+               case OPT_MAPAUTO:
+                       unshare_flags |= CLONE_NEWUSER;
+                       usermap = read_subid_range(_PATH_SUBUID, real_euid);
+                       groupmap = read_subid_range(_PATH_SUBGID, real_egid);
                        break;
                case OPT_SETGROUPS:
                        setgrpcmd = setgroups_str2id(optarg);