From: Christian Brauner Date: Thu, 6 May 2021 16:59:28 +0000 (+0200) Subject: libmisc: retain setfcap when mapping uid 0 X-Git-Tag: v4.9~27^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fpull%2F334%2Fhead;p=thirdparty%2Fshadow.git libmisc: retain setfcap when mapping uid 0 When uid 0 maps host uid 0 into the child userns newer kernels require CAP_SETFCAP be retained as this allows the caller to create fscaps that are valid in the ancestor userns. This was a security issue (in very rare circumstances). So whenever host uid 0 is mapped, retain CAP_SETFCAP if the caller had it. Userspace won't need to set CAP_SETFCAP on newuidmap as this is really only a scenario that real root should be doing which always has CAP_SETFCAP. And if they don't then they are in a locked-down userns. (LXC sometimes maps host uid 0 during chown operations in a helper userns but will not rely on newuidmap for that. But we don't want to risk regressing callers that want to rely on this behavior.) Signed-off-by: Christian Brauner --- diff --git a/libmisc/idmapping.c b/libmisc/idmapping.c index be3a50053..8697bd544 100644 --- a/libmisc/idmapping.c +++ b/libmisc/idmapping.c @@ -123,6 +123,25 @@ struct map_range *get_map_ranges(int ranges, int argc, char **argv) */ #define ULONG_DIGITS ((((sizeof(unsigned long) * CHAR_BIT) + 9)/10)*3) +#if HAVE_SYS_CAPABILITY_H +static inline bool maps_lower_root(int cap, int ranges, struct map_range *mappings) +{ + int idx; + struct map_range *mapping; + + if (cap != CAP_SETUID) + return false; + + mapping = mappings; + for (idx = 0; idx < ranges; idx++, mapping++) { + if (mapping->lower == 0) + return true; + } + + return false; +} +#endif + /* * The ruid refers to the caller's uid and is used to reset the effective uid * back to the callers real uid. @@ -177,6 +196,12 @@ void write_mapping(int proc_dir_fd, int ranges, struct map_range *mappings, /* Lockdown new{g,u}idmap by dropping all unneeded capabilities. */ memset(data, 0, sizeof(data)); data[0].effective = CAP_TO_MASK(cap); + /* + * When uid 0 from the ancestor userns is supposed to be mapped into + * the child userns we need to retain CAP_SETFCAP. + */ + if (maps_lower_root(cap, ranges, mappings)) + data[0].effective |= CAP_TO_MASK(CAP_SETFCAP); data[0].permitted = data[0].effective; if (capset(&hdr, data) < 0) { fprintf(stderr, _("%s: Could not set caps\n"), Prog);