]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
conf: check for {filecaps,setuid} on new{g,u}idmap
authorChristian Brauner <christian.brauner@ubuntu.com>
Sat, 15 Apr 2017 11:50:27 +0000 (13:50 +0200)
committerChristian Brauner <christian.brauner@ubuntu.com>
Sat, 15 Apr 2017 22:59:46 +0000 (00:59 +0200)
The new{g,u}idmap binaries where a source of trouble for users when they lacked
sufficient privileges. This commit adds code to check for sufficient privilege.
It checks whether new{g,u}idmap is root owned and has the setuid bit set and if
it doesn't it checks whether new{g,u}idmap is root owned and has CAP_SETUID in
its CAP_PERMITTED and CAP_EFFECTIVE set.

Closes #296.

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
src/lxc/conf.c
src/lxc/utils.c
src/lxc/utils.h

index afae3ca5d9fd65b545b7f4ca9caeafdd051912d7..2bc2098025804a18f1c13528f23eaed1d71c8224 100644 (file)
@@ -3319,30 +3319,89 @@ static int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf,
        return ret < 0 ? ret : closeret;
 }
 
+/* Check whether a binary exist and has either CAP_SETUID, CAP_SETGID or both. */
+static int idmaptool_on_path_and_privileged(const char *binary, cap_value_t cap)
+{
+       char *path;
+       int ret;
+       struct stat st;
+       int fret = 0;
+
+       path = on_path(binary, NULL);
+       if (!path)
+               return -ENOENT;
+
+       ret = stat(path, &st);
+       if (ret < 0) {
+               fret = -errno;
+               goto cleanup;
+       }
+
+       /* Check if the binary is setuid. */
+       if (st.st_mode & S_ISUID) {
+               DEBUG("The binary \"%s\" does have the setuid bit set.", path);
+               fret = 1;
+               goto cleanup;
+       }
+
+       #if HAVE_LIBCAP
+       /* Check if it has the CAP_SETUID capability. */
+       if ((cap & CAP_SETUID) &&
+           lxc_file_cap_is_set(path, CAP_SETUID, CAP_EFFECTIVE) &&
+           lxc_file_cap_is_set(path, CAP_SETUID, CAP_PERMITTED)) {
+               DEBUG("The binary \"%s\" has CAP_SETUID in its CAP_EFFECTIVE "
+                     "and CAP_PERMITTED sets.", path);
+               fret = 1;
+               goto cleanup;
+       }
+
+       /* Check if it has the CAP_SETGID capability. */
+       if ((cap & CAP_SETGID) &&
+           lxc_file_cap_is_set(path, CAP_SETGID, CAP_EFFECTIVE) &&
+           lxc_file_cap_is_set(path, CAP_SETGID, CAP_PERMITTED)) {
+               DEBUG("The binary \"%s\" has CAP_SETGID in its CAP_EFFECTIVE "
+                     "and CAP_PERMITTED sets.", path);
+               fret = 1;
+               goto cleanup;
+       }
+       #endif
+
+cleanup:
+       free(path);
+       return fret;
+}
+
 int lxc_map_ids(struct lxc_list *idmap, pid_t pid)
 {
        struct id_map *map;
        struct lxc_list *iterator;
        enum idtype type;
        char *pos;
-       char *buf = NULL, *cmdpath = NULL;
-       bool use_shadow = false;
-       int ret = 0;
+       int euid;
+       int ret = 0, use_shadow = 0;
+       int uidmap = 0, gidmap = 0;
+       char *buf = NULL;
 
-       /*
-        * If newuidmap exists, that is, if shadow is handing out subuid
-        * ranges, then insist that root also reserve ranges in subuid.  This
+       euid = geteuid();
+
+       /* If new{g,u}idmap exists, that is, if shadow is handing out subuid
+        * ranges, then insist that root also reserve ranges in subuid. This
         * will protected it by preventing another user from being handed the
         * range by shadow.
         */
-       cmdpath = on_path("newuidmap", NULL);
-       if (cmdpath) {
+       uidmap = idmaptool_on_path_and_privileged("newuidmap", CAP_SETUID);
+       gidmap = idmaptool_on_path_and_privileged("newgidmap", CAP_SETGID);
+       if (uidmap > 0 && gidmap > 0) {
+               DEBUG("Functional newuidmap and newgidmap binary found.");
                use_shadow = true;
-               free(cmdpath);
-       }
-
-       if (!use_shadow && geteuid()) {
-               ERROR("Missing newuidmap/newgidmap");
+       } else if (uidmap == -ENOENT && gidmap == -ENOENT && !euid) {
+               DEBUG("No newuidmap and newgidmap binary found. Trying to "
+                     "write directly with euid 0.");
+               use_shadow = false;
+       } else {
+               DEBUG("Either one or both of the newuidmap and newgidmap "
+                     "binaries do not exist or are missing necessary "
+                     "privilege.");
                return -1;
        }
 
index fbdc13b976cc22c6c6c6baed634f54d418697fe1..1154d415ab36f0bc1154981a7e9c5907223bc729 100644 (file)
@@ -1199,7 +1199,7 @@ bool detect_ramfs_rootfs(void)
        return false;
 }
 
-char *on_path(char *cmd, const char *rootfs) {
+char *on_path(const char *cmd, const char *rootfs) {
        char *path = NULL;
        char *entry = NULL;
        char *saveptr = NULL;
index d1966ff75a88cab3dc7b53665796cc858afa1c5d..19caa6dad936503c0c174c8f37d506f0cd01a389 100644 (file)
@@ -302,7 +302,7 @@ uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval);
 
 int detect_shared_rootfs(void);
 bool detect_ramfs_rootfs(void);
-char *on_path(char *cmd, const char *rootfs);
+char *on_path(const char *cmd, const char *rootfs);
 bool file_exists(const char *f);
 bool cgns_supported(void);
 char *choose_init(const char *rootfs);