diff options
| -rwxr-xr-x | .github/workflows/cibuild.sh | 2 | ||||
| -rw-r--r-- | include/audit-arch.h | 2 | ||||
| -rw-r--r-- | libblkid/docs/libblkid-sections.txt | 1 | ||||
| -rw-r--r-- | libblkid/src/blkid.h.in | 2 | ||||
| -rw-r--r-- | libblkid/src/libblkid.sym | 4 | ||||
| -rw-r--r-- | libblkid/src/probe.c | 42 | ||||
| -rw-r--r-- | libfdisk/src/wipe.c | 13 | ||||
| -rw-r--r-- | libmount/src/hook_mount.c | 21 | ||||
| -rw-r--r-- | login-utils/su-common.c | 10 | ||||
| -rw-r--r-- | sys-utils/unshare.1.adoc | 8 | ||||
| -rw-r--r-- | sys-utils/unshare.c | 360 |
11 files changed, 310 insertions, 155 deletions
diff --git a/.github/workflows/cibuild.sh b/.github/workflows/cibuild.sh index 0bd8d40df6..bd77c053d7 100755 --- a/.github/workflows/cibuild.sh +++ b/.github/workflows/cibuild.sh @@ -125,7 +125,7 @@ for phase in "${PHASES[@]}"; do make install DESTDIR=/tmp/dest ;; MESONCONF) - meson build + meson -Dwerror=true build ;; MESONBUILD) ninja -C build diff --git a/include/audit-arch.h b/include/audit-arch.h index 679964f240..ade1824174 100644 --- a/include/audit-arch.h +++ b/include/audit-arch.h @@ -65,6 +65,8 @@ # else # define SECCOMP_ARCH_NATIVE AUDIT_ARCH_PARISC64 # endif +#elif __alpha__ +# define SECCOMP_ARCH_NATIVE AUDIT_ARCH_ALPHA #else # error Unknown target architecture #endif diff --git a/libblkid/docs/libblkid-sections.txt b/libblkid/docs/libblkid-sections.txt index 9297002532..308f8effdc 100644 --- a/libblkid/docs/libblkid-sections.txt +++ b/libblkid/docs/libblkid-sections.txt @@ -73,6 +73,7 @@ BLKID_PROBE_AMBIGUOUS <FILE>lowprobe-tags</FILE> blkid_do_fullprobe blkid_do_wipe +blkid_wipe_all blkid_do_probe blkid_do_safeprobe <SUBSECTION> diff --git a/libblkid/src/blkid.h.in b/libblkid/src/blkid.h.in index c232d72fd1..8f6ec97d1a 100644 --- a/libblkid/src/blkid.h.in +++ b/libblkid/src/blkid.h.in @@ -458,6 +458,8 @@ extern int blkid_probe_has_value(blkid_probe pr, const char *name) __ul_attribute__((nonnull)); extern int blkid_do_wipe(blkid_probe pr, int dryrun) __ul_attribute__((nonnull)); +extern int blkid_wipe_all(blkid_probe pr) + __ul_attribute__((nonnull)); extern int blkid_probe_step_back(blkid_probe pr) __ul_attribute__((nonnull)); diff --git a/libblkid/src/libblkid.sym b/libblkid/src/libblkid.sym index 775b0246ca..7b2263a1c4 100644 --- a/libblkid/src/libblkid.sym +++ b/libblkid/src/libblkid.sym @@ -187,3 +187,7 @@ BLKID_2_37 { BLKID_2_39 { blkid_topology_get_diskseq; } BLKID_2_37; + +BLKID_2_40 { + blkid_wipe_all; +} BLKID_2_39; diff --git a/libblkid/src/probe.c b/libblkid/src/probe.c index 19b80ec6eb..da1cc75490 100644 --- a/libblkid/src/probe.c +++ b/libblkid/src/probe.c @@ -1482,6 +1482,8 @@ static inline int is_conventional(blkid_probe pr __attribute__((__unused__)), * See also blkid_probe_step_back() if you cannot use this built-in wipe * function, but you want to use libblkid probing as a source for wiping. * + * See also blkid_wipe_all() which works the same as the example above. + * * Returns: 0 on success, and -1 in case of error. */ int blkid_do_wipe(blkid_probe pr, int dryrun) @@ -1583,6 +1585,46 @@ int blkid_do_wipe(blkid_probe pr, int dryrun) } /** + * blkid_wipe_all: + * @pr: prober + * + * This function erases all detectable signatures from &pr. + * The @pr has to be open in O_RDWR mode. All other necessary configurations + * will be enabled automatically. + * + * <example> + * <title>wipe all filesystems or raids from the device</title> + * <programlisting> + * fd = open(devname, O_RDWR|O_CLOEXEC); + * blkid_probe_set_device(pr, fd, 0, 0); + * + * blkid_wipe_all(pr); + * </programlisting> + * </example> + * + * Returns: 0 on success, and -1 in case of error. + */ +int blkid_wipe_all(blkid_probe pr) +{ + DBG(LOWPROBE, ul_debug("wiping all signatures")); + + blkid_probe_enable_superblocks(pr, 1); + blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC | + BLKID_SUBLKS_BADCSUM); + + blkid_probe_enable_partitions(pr, 1); + blkid_probe_set_partitions_flags(pr, BLKID_PARTS_MAGIC | + BLKID_PARTS_FORCE_GPT); + + while (blkid_do_probe(pr) == 0) { + DBG(LOWPROBE, ul_debug("wiping one signature")); + blkid_do_wipe(pr, 0); + } + + return BLKID_PROBE_OK; +} + +/** * blkid_probe_step_back: * @pr: prober * diff --git a/libfdisk/src/wipe.c b/libfdisk/src/wipe.c index 53099fd444..bb5f1bb38b 100644 --- a/libfdisk/src/wipe.c +++ b/libfdisk/src/wipe.c @@ -134,17 +134,8 @@ int fdisk_do_wipe(struct fdisk_context *cxt) return rc; } - blkid_probe_enable_superblocks(pr, 1); - blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_MAGIC | - BLKID_SUBLKS_BADCSUM); - blkid_probe_enable_partitions(pr, 1); - blkid_probe_set_partitions_flags(pr, BLKID_PARTS_MAGIC | - BLKID_PARTS_FORCE_GPT); - - while (blkid_do_probe(pr) == 0) { - DBG(WIPE, ul_debugobj(wp, " wiping...")); - blkid_do_wipe(pr, FALSE); - } + DBG(WIPE, ul_debugobj(wp, " wiping...")); + blkid_wipe_all(pr); } blkid_free_probe(pr); diff --git a/libmount/src/hook_mount.c b/libmount/src/hook_mount.c index 7143450fb7..961cc8745d 100644 --- a/libmount/src/hook_mount.c +++ b/libmount/src/hook_mount.c @@ -150,22 +150,29 @@ static inline int fsconfig_set_value( const char *name, const char *value) { int rc; - char *p = NULL; + char *s = NULL; + /* "\," is a way to use comma in values, let's remove \ escape */ if (value && strstr(value, "\\,")) { - p = strdup(value); - if (!p) - return -EINVAL; + char *x, *p; - strrem(p, '\\'); - value = p; + s = strdup(value); + if (!s) + return -EINVAL; + for (x = p = s; *x; p++, x++) { + if (*x == '\\' && *(x + 1) == ',') + x++; + *p = *x; + } + *p = '\0'; + value = s; } DBG(HOOK, ul_debugobj(hs, " fsconfig(name=\"%s\" value=\"%s\")", name, value ? : "")); if (value) { rc = fsconfig(fd, FSCONFIG_SET_STRING, name, value, 0); - free(p); + free(s); } else rc = fsconfig(fd, FSCONFIG_SET_FLAG, name, NULL, 0); diff --git a/login-utils/su-common.c b/login-utils/su-common.c index c5c0102e5e..06ce5dba2e 100644 --- a/login-utils/su-common.c +++ b/login-utils/su-common.c @@ -26,6 +26,7 @@ #include <sys/types.h> #include <pwd.h> #include <grp.h> +#include <libgen.h> #include <security/pam_appl.h> #ifdef HAVE_SECURITY_PAM_MISC_H # include <security/pam_misc.h> @@ -840,17 +841,20 @@ static void run_shell( su->simulate_login ? " login" : "", su->fast_startup ? " fast-start" : "")); + char* tmp = xstrdup(shell); if (su->simulate_login) { char *arg0; char *shell_basename; - shell_basename = basename(shell); + shell_basename = basename(tmp); arg0 = xmalloc(strlen(shell_basename) + 2); arg0[0] = '-'; strcpy(arg0 + 1, shell_basename); args[0] = arg0; - } else - args[0] = basename(shell); + } else { + args[0] = basename(tmp); + } + free(tmp); if (su->fast_startup) args[argno++] = "-f"; diff --git a/sys-utils/unshare.1.adoc b/sys-utils/unshare.1.adoc index 670774a898..e6201e28ff 100644 --- a/sys-utils/unshare.1.adoc +++ b/sys-utils/unshare.1.adoc @@ -93,16 +93,16 @@ 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=**__inneruid:outeruid:count__|**auto**:: -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. 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. 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*. + 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. **--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=**__innergid:outergid:count__|**auto**:: -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. 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. If this option is specified multiple times, the last occurrence takes precedence. This option implies *--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*. + 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. diff --git a/sys-utils/unshare.c b/sys-utils/unshare.c index 6222c1421c..b03a0178aa 100644 --- a/sys-utils/unshare.c +++ b/sys-utils/unshare.c @@ -364,6 +364,7 @@ static gid_t get_group(const char *s, const char *err) * @outer: First ID mapped on the outside of the namespace * @inner: First ID mapped on the inside of the namespace * @count: Length of the inside and outside ranges + * @next: Next range of IDs in the chain * * A range of uids/gids to map using new[gu]idmap. */ @@ -371,9 +372,16 @@ struct map_range { unsigned int outer; unsigned int inner; unsigned int count; + struct map_range *next; }; -#define UID_BUFSIZ sizeof(stringify_value(ULONG_MAX)) +static void insert_map_range(struct map_range **chain, struct map_range map) +{ + struct map_range *tail = *chain; + *chain = xmalloc(sizeof(**chain)); + memcpy(*chain, &map, sizeof(**chain)); + (*chain)->next = tail; +} /** * get_map_range() - Parse a mapping range from a string @@ -382,20 +390,20 @@ struct map_range { * Parse a string of the form inner:outer:count or outer,inner,count into * a new mapping range. * - * Return: A new &struct map_range + * Return: A struct map_range */ -static struct map_range *get_map_range(const char *s) +static struct map_range get_map_range(const char *s) { int end; - struct map_range *ret; + struct map_range ret; - ret = xmalloc(sizeof(*ret)); + ret.next = NULL; - if (sscanf(s, "%u:%u:%u%n", &ret->inner, &ret->outer, &ret->count, + if (sscanf(s, "%u:%u:%u%n", &ret.inner, &ret.outer, &ret.count, &end) >= 3 && !s[end]) return ret; /* inner:outer:count */ - if (sscanf(s, "%u,%u,%u%n", &ret->outer, &ret->inner, &ret->count, + if (sscanf(s, "%u,%u,%u%n", &ret.outer, &ret.inner, &ret.count, &end) >= 3 && !s[end]) return ret; /* outer,inner,count */ @@ -410,16 +418,16 @@ static struct map_range *get_map_range(const char *s) * * 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) { char *line = NULL, *pwbuf; FILE *idmap; size_t n = 0; struct passwd *pw; - struct map_range *map; + struct map_range map; - map = xmalloc(sizeof(*map)); - map->inner = -1; + map.inner = -1; + map.next = NULL; pw = xgetpwuid(uid, &pwbuf); if (!pw) @@ -452,13 +460,13 @@ static struct map_range *read_subid_range(char *filename, uid_t uid) if (!rest) continue; *rest = '\0'; - map->outer = strtoul_or_err(s, _("failed to parse subid map")); + 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")); + map.count = strtoul_or_err(s, _("failed to parse subid map")); fclose(idmap); free(pw); @@ -472,129 +480,202 @@ static struct map_range *read_subid_range(char *filename, uid_t uid) } /** - * 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 + * read_kernel_map() - Read all available IDs from the kernel + * @chain: destination list to receive pass-through ID mappings + * @filename: either /proc/self/uid_map or /proc/self/gid_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). + * This is used by --map-users=all and --map-groups=all to construct + * pass-through mappings for all IDs available in the parent namespace. + */ +static void read_kernel_map(struct map_range **chain, char *filename) +{ + char *line = NULL; + size_t size = 0; + FILE *idmap; + + idmap = fopen(filename, "r"); + if (!idmap) + err(EXIT_FAILURE, _("could not open '%s'"), filename); + + while (getline(&line, &size, idmap) != -1) { + unsigned int start, count; + if (sscanf(line, " %u %*u %u", &start, &count) < 2) + continue; + insert_map_range(chain, (struct map_range) { + .inner = start, + .outer = start, + .count = count + }); + } + + fclose(idmap); + free(line); +} + +/** + * add_single_map_range() - Add a single-ID map into a list without overlap + * @chain: A linked list of ID range mappings + * @outer: ID outside the namespace for a single map. + * @inner: ID inside the namespace for a single map, or -1 for no map. * - * 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. + * Prepend a mapping to @chain for the single ID @outer to the single ID + * @inner. The tricky bit is that we cannot let existing mappings overlap it. + * We accomplish this by removing a "hole" from each existing range @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). * - * This function always exec()s or errors out and does not return. + * Most of the time, this function will be called with a single mapping range + * @map, @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. */ -static void __attribute__((__noreturn__)) -map_ids(const char *idmapper, int ppid, unsigned int outer, unsigned int inner, - struct map_range *map) + +static void add_single_map_range(struct map_range **chain, unsigned int outer, + unsigned int inner) { - /* idmapper + pid + 4 * map + NULL */ - char *argv[15]; - /* argv - idmapper - "1" - NULL */ - char 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], sizeof(args[j]), "%u", x); \ - push_str(args[j++]); \ -} while (0) - - push_str(xstrdup(idmapper)); - push_ul(ppid); - if ((int)inner == -1) { + struct map_range *map = *chain; + + if (inner + 1 == 0) + outer = (unsigned int) -1; + *chain = NULL; + + while (map) { + struct map_range lo, mid, hi, *next = map->next; + unsigned int inner_offset, outer_offset; + /* - * If we don't have a "single" mapping, then we can just use map - * directly, starting inner IDs from zero for an auto mapping + * Start inner IDs from zero for an auto mapping; otherwise, if + * the single mapping exists and overlaps the range, remove an ID */ - push_ul(map->inner + 1 ? map->inner : 0); - push_ul(map->outer); - push_ul(map->count); - push_str(NULL); + 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))) + 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); - execvp(idmapper, argv); - errexec(idmapper); + /* + * 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; + + /* Insert non-empty mappings into the output chain */ + if (hi.count) + insert_map_range(chain, hi); + if (mid.count) + insert_map_range(chain, mid); + if (lo.count) + insert_map_range(chain, lo); + + free(map); + map = next; } - /* - * Start inner IDs from zero for an auto mapping; otherwise, if the two - * fixed mappings overlap, remove an ID from map - */ - if (map->inner + 1 == 0) - map->inner = 0; - else 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 (inner + 1 != 0) { + /* Insert single ID mapping as the first entry in the chain */ + insert_map_range(chain, (struct map_range) { + .inner = inner, + .outer = outer, + .count = 1 + }); } - if (hi.count) { - push_ul(hi.inner); - push_ul(hi.outer); - push_ul(hi.count); +} + +/** + * map_ids_external() - Create a new uid/gid map using setuid helper + * @idmapper: Either newuidmap or newgidmap + * @ppid: Pid to set the map for + * @chain: A linked list of ID range mappings + * + * This creates a new uid/gid map for @ppid using @idmapper to set the + * mapping for each of the ranges in @chain. + * + * This function always exec()s or errors out and does not return. + */ +static void __attribute__((__noreturn__)) +map_ids_external(const char *idmapper, int ppid, struct map_range *chain) +{ + unsigned int i = 0, length = 3; + char **argv; + + for (struct map_range *map = chain; map; map = map->next) + length += 3; + argv = xcalloc(length, sizeof(*argv)); + argv[i++] = xstrdup(idmapper); + xasprintf(&argv[i++], "%u", ppid); + + for (struct map_range *map = chain; map; map = map->next) { + xasprintf(&argv[i++], "%u", map->inner); + xasprintf(&argv[i++], "%u", map->outer); + xasprintf(&argv[i++], "%u", map->count); } - push_str(NULL); + + argv[i] = NULL; execvp(idmapper, argv); errexec(idmapper); } /** + * map_ids_internal() - Create a new uid/gid map using root privilege + * @type: Either uid_map or gid_map + * @ppid: Pid to set the map for + * @chain: A linked list of ID range mappings + * + * This creates a new uid/gid map for @ppid using a privileged write to + * /proc/@ppid/@type to set a mapping for each of the ranges in @chain. + */ +static void map_ids_internal(const char *type, int ppid, struct map_range *chain) +{ + int count, fd; + unsigned int length = 0; + char buffer[4096], *path; + + xasprintf(&path, "/proc/%u/%s", ppid, type); + for (struct map_range *map = chain; map; map = map->next) { + count = snprintf(buffer + length, sizeof(buffer) - length, + "%u %u %u\n", + map->inner, map->outer, map->count); + if (count < 0 || count + length > sizeof(buffer)) + errx(EXIT_FAILURE, + _("%s too large for kernel 4k limit"), path); + length += count; + } + + fd = open(path, O_WRONLY | O_CLOEXEC | O_NOCTTY); + if (fd < 0) + err(EXIT_FAILURE, _("failed to open %s"), path); + if (write_all(fd, buffer, length) < 0) + err(EXIT_FAILURE, _("failed to write %s"), path); + close(fd); + free(path); +} + +/** * map_ids_from_child() - Set up a new uid/gid map * @fd: The eventfd to wait on * @mapuser: The user to map the current user to (or -1) @@ -619,6 +700,19 @@ static pid_t map_ids_from_child(int *fd, uid_t mapuser, if (child) return child; + if (usermap) + add_single_map_range(&usermap, geteuid(), mapuser); + if (groupmap) + add_single_map_range(&groupmap, getegid(), mapgroup); + + if (geteuid() == 0) { + if (usermap) + map_ids_internal("uid_map", ppid, usermap); + if (groupmap) + map_ids_internal("gid_map", ppid, groupmap); + exit(EXIT_SUCCESS); + } + /* Avoid forking more than we need to */ if (usermap && groupmap) { pid = fork(); @@ -629,9 +723,9 @@ static pid_t map_ids_from_child(int *fd, uid_t mapuser, } if (!pid && usermap) - map_ids("newuidmap", ppid, geteuid(), mapuser, usermap); + map_ids_external("newuidmap", ppid, usermap); if (groupmap) - map_ids("newgidmap", ppid, getegid(), mapgroup, groupmap); + map_ids_external("newgidmap", ppid, groupmap); exit(EXIT_SUCCESS); } @@ -844,21 +938,27 @@ int main(int argc, char *argv[]) case OPT_MAPUSERS: unshare_flags |= CLONE_NEWUSER; if (!strcmp(optarg, "auto")) - usermap = read_subid_range(_PATH_SUBUID, real_euid); + insert_map_range(&usermap, + read_subid_range(_PATH_SUBUID, real_euid)); + else if (!strcmp(optarg, "all")) + read_kernel_map(&usermap, _PATH_PROC_UIDMAP); else - usermap = get_map_range(optarg); + insert_map_range(&usermap, get_map_range(optarg)); break; case OPT_MAPGROUPS: unshare_flags |= CLONE_NEWUSER; if (!strcmp(optarg, "auto")) - groupmap = read_subid_range(_PATH_SUBGID, real_euid); + insert_map_range(&groupmap, + read_subid_range(_PATH_SUBGID, real_euid)); + else if (!strcmp(optarg, "all")) + read_kernel_map(&groupmap, _PATH_PROC_GIDMAP); else - groupmap = get_map_range(optarg); + insert_map_range(&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_euid); + insert_map_range(&usermap, read_subid_range(_PATH_SUBUID, real_euid)); + insert_map_range(&groupmap, read_subid_range(_PATH_SUBGID, real_euid)); break; case OPT_SETGROUPS: setgrpcmd = setgroups_str2id(optarg); @@ -994,8 +1094,10 @@ int main(int argc, char *argv[]) int termsig = WTERMSIG(status); - if (signal(termsig, SIG_DFL) == SIG_ERR || - sigemptyset(&sigset) != 0 || + if (termsig != SIGKILL && signal(termsig, SIG_DFL) == SIG_ERR) + err(EXIT_FAILURE, + _("signal handler reset failed")); + if (sigemptyset(&sigset) != 0 || sigaddset(&sigset, termsig) != 0 || sigprocmask(SIG_UNBLOCK, &sigset, NULL) != 0) err(EXIT_FAILURE, |
