]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
unshare: Add options to identity map the user's subordinate uids and gids
authorDavid Gibson <david@gibson.dropbear.id.au>
Tue, 9 Jul 2024 05:44:31 +0000 (15:44 +1000)
committerDavid Gibson <david@gibson.dropbear.id.au>
Tue, 9 Jul 2024 07:25:14 +0000 (17:25 +1000)
--map-users=auto, --map-groups=auto and --map-auto allow automatically
mapping the first block of user or group IDs owned by the effective user
from /etc/sub[ug]id to a block starting at user ID 0.  Add options
--map-users=subids, --map-groups=subids and --map-subids to perform a
mapping of the same IDs, but as an identity mapping instead of to UID 0.

This is useful in similar situations to --map-current-user, but preserves
access to the user's subordinate IDs.  That allows nested namespaces to
be created with more complex user mappings, either with unshare or with
a container runtime such as podman.

Fixes: https://github.com/util-linux/util-linux/issues/3120
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
sys-utils/unshare.1.adoc
sys-utils/unshare.c

index 6396c4892ddf4775dfaacc438863f6af2a55a062..5c2b268b194a41823347ffeb38c77611c002fa42 100644 (file)
@@ -97,7 +97,7 @@ Just before running the program, mount the binfmt_misc filesystem at _mountpoint
 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=**__inneruid:outeruid:count__|**auto**|**all**::
-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 *unshare* was run unprivileged. 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. Use *--map-users* multiple times to map more than one block of user IDs. The special value *auto* will map the first block of user IDs owned by the effective user from _/etc/subuid_ to a block starting at user ID 0. The special value *all* will create a pass-through map for every user ID available in the parent namespace. This option implies *--user*.
+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 *unshare* was run unprivileged. 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. Use *--map-users* multiple times to map more than one block of user IDs. The special value *auto* will map the first block of user IDs owned by the effective user from _/etc/subuid_ to a block starting at user ID 0. The special value *subids* will identity map the same block. The special value *all* will create a pass-through map for every user ID available in the parent namespace. This option implies *--user*.
 +
 Before util-linux version 2.39, this option expected a comma-separated argument of the form _outeruid,inneruid,count_ but that format is now deprecated for consistency with the ordering used in _/proc/[pid]/uid_map_ and the _X-mount.idmap_ mount option.
 
@@ -105,13 +105,16 @@ Before util-linux version 2.39, this option expected a comma-separated argument
 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=**__innergid:outergid:count__|**auto**|**all**::
-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 *unshare* was run unprivileged. 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. Use *--map-groups* multiple times to map more than one block of group IDs. The special value *auto* will map the first block of user IDs owned by the effective user from _/etc/subgid_ to a block starting at group ID 0. The special value *all* will create a pass-through map for every group ID available in the parent namespace. This option implies *--user*.
+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 *unshare* was run unprivileged. 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. Use *--map-groups* multiple times to map more than one block of group IDs. The special value *auto* will map the first block of user IDs owned by the effective user from _/etc/subgid_ to a block starting at group ID 0. The special value *subids* will identity map the same block. The special value *all* will create a pass-through map for every group ID available in the parent namespace. This option implies *--user*.
 +
 Before util-linux version 2.39, this option expected a comma-separated argument of the form _outergid,innergid,count_ but that format is now deprecated for consistency with the ordering used in _/proc/[pid]/gid_map_ and the _X-mount.idmap_ mount option.
 
 **--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 common case where the first block of subordinate user and group IDs can map the whole user and group ID space. This option is equivalent to specifying *--map-users=auto* and *--map-groups=auto*.
 
+**--map-subids**::
+Identity map the first block of user IDs owned by the effective user from _/etc/subuid_. In the same manner, also identity map the first block of group IDs owned by the effective group from _/etc/subgid_. This option is equivalent to specifying *--map-users=subids* and *--map-groups=subids*.
+
 *-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*.
 
index e48e4c9f55a586c19dcfdfee76cd66362bc0cc4b..9263606c3ed27f659a832c6ada7738ddae2db37f 100644 (file)
@@ -413,10 +413,11 @@ static struct map_range get_map_range(const char *s)
  * @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.
+ * @identity: (boolean) If true identity map the range, otherwise map to 0
  *
  * This finds the first subid range matching @uid in @filename.
  */
-static struct map_range read_subid_range(char *filename, uid_t uid)
+static struct map_range read_subid_range(char *filename, uid_t uid, int identity)
 {
        char *line = NULL, *pwbuf;
        FILE *idmap;
@@ -463,6 +464,9 @@ static struct map_range read_subid_range(char *filename, uid_t uid)
                        *rest = '\0';
                map.count = strtoul_or_err(s, _("failed to parse subid map"));
 
+               if (identity)
+                       map.inner = map.outer;
+
                fclose(idmap);
                free(pw);
                free(pwbuf);
@@ -541,15 +545,17 @@ static void add_single_map_range(struct map_range **chain, unsigned int outer,
                                 *next = map->next;
                unsigned int inner_offset, outer_offset;
 
-               /*
-                * Start inner IDs from zero for an auto mapping; otherwise, if
-                * the single mapping exists and overlaps the range, remove an ID
-                */
+               /* Start inner IDs from zero for an auto mapping */
                if (map->inner + 1 == 0)
                        map->inner = 0;
-               else if (inner + 1 != 0 &&
-                        ((outer >= map->outer && outer <= map->outer + map->count) ||
-                         (inner >= map->inner && inner <= map->inner + map->count)))
+
+               /*
+                * If the single mapping exists and overlaps the range, remove
+                * an ID
+                */
+               if (inner + 1 != 0 &&
+                   ((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 */
@@ -826,6 +832,7 @@ int main(int argc, char *argv[])
                OPT_MAPGROUP,
                OPT_MAPGROUPS,
                OPT_MAPAUTO,
+               OPT_MAPSUBIDS,
        };
        static const struct option longopts[] = {
                { "help",          no_argument,       NULL, 'h'             },
@@ -851,6 +858,7 @@ int main(int argc, char *argv[])
                { "map-root-user", no_argument,       NULL, 'r'             },
                { "map-current-user", no_argument,    NULL, 'c'             },
                { "map-auto",      no_argument,       NULL, OPT_MAPAUTO     },
+               { "map-subids",    no_argument,       NULL, OPT_MAPSUBIDS   },
                { "propagation",   required_argument, NULL, OPT_PROPAGATION },
                { "setgroups",     required_argument, NULL, OPT_SETGROUPS   },
                { "keep-caps",     no_argument,       NULL, OPT_KEEPCAPS    },
@@ -980,7 +988,10 @@ int main(int argc, char *argv[])
                        unshare_flags |= CLONE_NEWUSER;
                        if (!strcmp(optarg, "auto"))
                                insert_map_range(&usermap,
-                                       read_subid_range(_PATH_SUBUID, real_euid));
+                                                read_subid_range(_PATH_SUBUID, real_euid, 0));
+                       else if (!strcmp(optarg, "subids"))
+                               insert_map_range(&usermap,
+                                                read_subid_range(_PATH_SUBUID, real_euid, 1));
                        else if (!strcmp(optarg, "all"))
                                read_kernel_map(&usermap, _PATH_PROC_UIDMAP);
                        else
@@ -990,7 +1001,10 @@ int main(int argc, char *argv[])
                        unshare_flags |= CLONE_NEWUSER;
                        if (!strcmp(optarg, "auto"))
                                insert_map_range(&groupmap,
-                                       read_subid_range(_PATH_SUBGID, real_euid));
+                                                read_subid_range(_PATH_SUBGID, real_euid, 0));
+                       else if (!strcmp(optarg, "subids"))
+                               insert_map_range(&usermap,
+                                                read_subid_range(_PATH_SUBGID, real_euid, 1));
                        else if (!strcmp(optarg, "all"))
                                read_kernel_map(&groupmap, _PATH_PROC_GIDMAP);
                        else
@@ -998,8 +1012,13 @@ int main(int argc, char *argv[])
                        break;
                case OPT_MAPAUTO:
                        unshare_flags |= CLONE_NEWUSER;
-                       insert_map_range(&usermap, read_subid_range(_PATH_SUBUID, real_euid));
-                       insert_map_range(&groupmap, read_subid_range(_PATH_SUBGID, real_euid));
+                       insert_map_range(&usermap, read_subid_range(_PATH_SUBUID, real_euid, 0));
+                       insert_map_range(&groupmap, read_subid_range(_PATH_SUBGID, real_euid, 0));
+                       break;
+               case OPT_MAPSUBIDS:
+                       unshare_flags |= CLONE_NEWUSER;
+                       insert_map_range(&usermap, read_subid_range(_PATH_SUBUID, real_euid, 1));
+                       insert_map_range(&groupmap, read_subid_range(_PATH_SUBGID, real_euid, 1));
                        break;
                case OPT_SETGROUPS:
                        setgrpcmd = setgroups_str2id(optarg);