]> granicus.if.org Git - shadow/commitdiff
new[ug]idmap: not require CAP_SYS_ADMIN in the parent userNS
authorGiuseppe Scrivano <gscrivan@redhat.com>
Mon, 8 Oct 2018 16:18:18 +0000 (18:18 +0200)
committerGiuseppe Scrivano <gscrivan@redhat.com>
Mon, 22 Oct 2018 14:57:50 +0000 (16:57 +0200)
if the euid!=owner of the userns, the kernel returns EPERM when trying
to write the uidmap and there is no CAP_SYS_ADMIN in the parent
namespace.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
configure.ac
libmisc/idmapping.c
libmisc/idmapping.h
src/Makefile.am
src/newgidmap.c
src/newuidmap.c

index 41068a5d5b0555702d7e54f744a7630110d7e1a9..53a30579d1c5a894fda35172fa2b8f777a50ec99 100644 (file)
@@ -34,7 +34,7 @@ AC_HEADER_STDBOOL
 
 AC_CHECK_HEADERS(errno.h fcntl.h limits.h unistd.h sys/time.h utmp.h \
        utmpx.h termios.h termio.h sgtty.h sys/ioctl.h syslog.h paths.h \
-       utime.h ulimit.h sys/resource.h gshadow.h lastlog.h \
+       utime.h ulimit.h sys/capability.h sys/resource.h gshadow.h lastlog.h \
        locale.h rpc/key_prot.h netdb.h acl/libacl.h attr/libattr.h \
        attr/error_context.h)
 
index 20f2d9c7784bd1fcb5a9ab5cc514964c122482ee..f54c934169e3f30557728b67d286eef7c3a3a4c6 100644 (file)
 #include <stdio.h>
 #include "prototypes.h"
 #include "idmapping.h"
+#include <sys/prctl.h>
+#if HAVE_SYS_CAPABILITY_H
+# include <sys/capability.h>
+#endif
 
 struct map_range *get_map_ranges(int ranges, int argc, char **argv)
 {
@@ -121,17 +125,56 @@ struct map_range *get_map_ranges(int ranges, int argc, char **argv)
 
 
 void write_mapping(int proc_dir_fd, int ranges, struct map_range *mappings,
-       const char *map_file)
+       const char *map_file, uid_t uid)
 {
        int idx;
        struct map_range *mapping;
        size_t bufsize;
        char *buf, *pos;
        int fd;
+#if HAVE_SYS_CAPABILITY_H
+       struct __user_cap_header_struct hdr = { _LINUX_CAPABILITY_VERSION_3, 0 };
+       struct __user_cap_data_struct data[2] = { { 0 } };
+#endif
 
        bufsize = ranges * ((ULONG_DIGITS  + 1) * 3);
        pos = buf = xmalloc(bufsize);
 
+#if HAVE_SYS_CAPABILITY_H
+       if (capget(&hdr, data) < 0) {
+               fprintf(stderr, _("%s: Could not get capabilities\n"), Prog);
+               exit(EXIT_FAILURE);
+       }
+       if (!(data[0].effective & CAP_TO_MASK(CAP_SYS_ADMIN)) &&
+           uid != geteuid()) {
+               bool uid_map;
+
+               if (strcmp(map_file, "uid_map") == 0) {
+                       uid_map = true;
+               } else if (strcmp(map_file, "gid_map") == 0) {
+                       uid_map = false;
+               } else {
+                       fprintf(stderr, _("%s: Invalid map file %s specified\n"), Prog, map_file);
+                       exit(EXIT_FAILURE);
+               }
+               if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
+                       fprintf(stderr, _("%s: Could not prctl(PR_SET_KEEPCAPS)\n"), Prog);
+                       exit(EXIT_FAILURE);
+               }
+               if (seteuid(uid) < 0) {
+                       fprintf(stderr, _("%s: Could not seteuid to %d\n"), Prog, uid);
+                       exit(EXIT_FAILURE);
+               }
+
+               memset(data, 0, sizeof(data));
+               data[0].effective = data[0].permitted = CAP_TO_MASK(uid_map ? CAP_SETUID : CAP_SETGID);
+               if (capset(&hdr, data) < 0) {
+                       fprintf(stderr, _("%s: Could not set caps\n"), Prog);
+                       exit(EXIT_FAILURE);
+               }
+       }
+#endif
+
        /* Build the mapping command */
        mapping = mappings;
        for (idx = 0; idx < ranges; idx++, mapping++) {
index 58d000f2aa83902bfdc73ef1b6fa8832d21a9e54..4bfffa63218433358391fa49ca98c94d92dd36e0 100644 (file)
@@ -38,7 +38,7 @@ struct map_range {
 
 extern struct map_range *get_map_ranges(int ranges, int argc, char **argv);
 extern void write_mapping(int proc_dir_fd, int ranges,
-       struct map_range *mappings, const char *map_file);
+       struct map_range *mappings, const char *map_file, uid_t uid);
 
 #endif /* _ID_MAPPING_H_ */
 
index 3c98a8d3636762f1826d5aaae8ac41dd784c68b4..277ef00861e27cb2df49233fa2f5b594f3069519 100644 (file)
@@ -86,8 +86,8 @@ LIBCRYPT_NOPAM = $(LIBCRYPT)
 endif
 
 chage_LDADD    = $(LDADD) $(LIBPAM_SUID) $(LIBAUDIT) $(LIBSELINUX)
-newuidmap_LDADD    = $(LDADD) $(LIBSELINUX)
-newgidmap_LDADD    = $(LDADD) $(LIBSELINUX)
+newuidmap_LDADD    = $(LDADD) $(LIBSELINUX) $(LIBCAP)
+newgidmap_LDADD    = $(LDADD) $(LIBSELINUX) $(LIBCAP)
 chfn_LDADD     = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD)
 chgpasswd_LDADD = $(LDADD) $(LIBPAM_SUID) $(LIBSELINUX) $(LIBCRYPT)
 chsh_LDADD     = $(LDADD) $(LIBPAM) $(LIBSELINUX) $(LIBCRYPT_NOPAM) $(LIBSKEY) $(LIBMD)
index 59a2e75c90e303f63cf75f02529760abef6fb5c7..70b87888dcd70f89000b4a5fcad7de31b5eb3dee 100644 (file)
@@ -250,7 +250,7 @@ int main(int argc, char **argv)
        verify_ranges(pw, ranges, mappings, &allow_setgroups);
 
        write_setgroups(proc_dir_fd, allow_setgroups);
-       write_mapping(proc_dir_fd, ranges, mappings, "gid_map");
+       write_mapping(proc_dir_fd, ranges, mappings, "gid_map", pw->pw_uid);
        sub_gid_close();
 
        return EXIT_SUCCESS;
index 1ba25e7ae7f7cfe25665420957a53034b7cabcea..45636a3c35b02dd216309b6549ea3f375c324be5 100644 (file)
@@ -179,7 +179,7 @@ int main(int argc, char **argv)
 
        verify_ranges(pw, ranges, mappings);
 
-       write_mapping(proc_dir_fd, ranges, mappings, "uid_map");
+       write_mapping(proc_dir_fd, ranges, mappings, "uid_map", pw->pw_uid);
        sub_uid_close();
 
        return EXIT_SUCCESS;