aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-x.github/workflows/cibuild.sh2
-rw-r--r--include/audit-arch.h2
-rw-r--r--libblkid/docs/libblkid-sections.txt1
-rw-r--r--libblkid/src/blkid.h.in2
-rw-r--r--libblkid/src/libblkid.sym4
-rw-r--r--libblkid/src/probe.c42
-rw-r--r--libfdisk/src/wipe.c13
-rw-r--r--libmount/src/hook_mount.c21
-rw-r--r--login-utils/su-common.c10
-rw-r--r--sys-utils/unshare.1.adoc8
-rw-r--r--sys-utils/unshare.c360
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,