]> git.ipfire.org Git - thirdparty/shadow.git/commitdiff
libmisc: retain setfcap when mapping uid 0 334/head
authorChristian Brauner <christian.brauner@ubuntu.com>
Thu, 6 May 2021 16:59:28 +0000 (18:59 +0200)
committerChristian Brauner <christian.brauner@ubuntu.com>
Thu, 6 May 2021 17:04:42 +0000 (19:04 +0200)
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 <christian.brauner@ubuntu.com>
libmisc/idmapping.c

index be3a50053bf502bbb82dcf71ecd9bc54d530b538..8697bd5445b7a66b1ea26cabe565eb43539ee2cb 100644 (file)
@@ -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);