]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
Fix unprivileged containers started by root
authorStéphane Graber <stgraber@ubuntu.com>
Wed, 26 Feb 2014 18:00:36 +0000 (13:00 -0500)
committerStéphane Graber <stgraber@ubuntu.com>
Thu, 27 Feb 2014 22:47:31 +0000 (17:47 -0500)
This change makes it possible to create unprivileged containers as root.
They will be stored in the usual system wide location, use the usual
system wide cache but will be running using a uid/gid map.

This also updates lxc_usernsexec to use the same function as the rest of
LXC, centralizing all the userns switch in a single function.

That function now detects the presence of newuidmap and newgidmap on the
system, if they are present, they will be used for containers created as
either user or root. If they're not and the user isn't root, an error is
shown. If they're not and the user is root, LXC will directly set the
uid_map and gid_map values.

All that should allow for a consistent experience as well as supporting
distributions that don't yet ship newuidmap/newgidmap.

To make things simpler in the future, an helper function "on_path" is
also introduced and used to detect the presence of newuidmap and
newgidmap.

Signed-off-by: Stéphane Graber <stgraber@ubuntu.com>
Acked-by: Serge E. Hallyn <serge.hallyn@ubuntu.com>
src/lxc/conf.c
src/lxc/lxc_usernsexec.c
src/lxc/lxccontainer.c
src/lxc/utils.c
src/lxc/utils.h

index b6bc4142b4452c2dfe2bd46cc3d2188c83896bb3..d99659a68735d1e9046b67a15dafddf7923bd092 100644 (file)
@@ -3164,7 +3164,12 @@ int lxc_map_ids(struct lxc_list *idmap, pid_t pid)
        int ret = 0;
        enum idtype type;
        char *buf = NULL, *pos;
-       int am_root = (getuid() == 0);
+       int use_shadow = (on_path("newuidmap") && on_path("newuidmap"));
+
+       if (!use_shadow && geteuid()) {
+               ERROR("Missing newuidmap/newgidmap");
+               return -1;
+       }
 
        for(type = ID_TYPE_UID; type <= ID_TYPE_GID; type++) {
                int left, fill;
@@ -3175,7 +3180,7 @@ int lxc_map_ids(struct lxc_list *idmap, pid_t pid)
                                return -ENOMEM;
                }
                pos = buf;
-               if (!am_root)
+               if (use_shadow)
                        pos += sprintf(buf, "new%cidmap %d",
                                type == ID_TYPE_UID ? 'u' : 'g',
                                pid);
@@ -3189,9 +3194,9 @@ int lxc_map_ids(struct lxc_list *idmap, pid_t pid)
                        had_entry = 1;
                        left = 4096 - (pos - buf);
                        fill = snprintf(pos, left, "%s%lu %lu %lu%s",
-                                       am_root ? "" : " ",
+                                       use_shadow ? " " : "",
                                        map->nsid, map->hostid, map->range,
-                                       am_root ? "\n" : "");
+                                       use_shadow ? "" : "\n");
                        if (fill <= 0 || fill >= left)
                                SYSERROR("snprintf failed, too many mappings");
                        pos += fill;
@@ -3199,7 +3204,7 @@ int lxc_map_ids(struct lxc_list *idmap, pid_t pid)
                if (!had_entry)
                        continue;
 
-               if (am_root) {
+               if (!use_shadow) {
                        ret = write_id_mapping(type, pid, buf, pos-buf);
                } else {
                        left = 4096 - (pos - buf);
index 2efc687eac328b2aa75b3a91ce28c31b53268bb0..f5b7fe06a41ebac91055dff9bd854ae107a3f0a6 100644 (file)
@@ -41,6 +41,7 @@
 #include <pwd.h>
 #include <grp.h>
 
+#include "conf.h"
 #include "namespace.h"
 #include "utils.h"
 
@@ -131,19 +132,7 @@ static int do_child(void *vargv)
        return -1;
 }
 
-struct id_map {
-       char which; // b or u or g
-       long host_id, ns_id, range;
-       struct id_map *next;
-};
-
-static struct id_map default_map = {
-       .which = 'b',
-       .host_id = 100000,
-       .ns_id = 0,
-       .range = 10000,
-};
-static struct id_map *active_map = &default_map;
+static struct lxc_list active_map;
 
 /*
  * given a string like "b:0:100000:10", map both uids and gids
@@ -152,28 +141,51 @@ static struct id_map *active_map = &default_map;
 static int parse_map(char *map)
 {
        struct id_map *newmap;
-    int ret;
+       struct lxc_list *tmp = NULL;
+       int ret;
+       int i;
+       char types[2] = {'u', 'g'};
+       char which;
+       long host_id, ns_id, range;
 
        if (!map)
                return -1;
-       newmap = malloc(sizeof(*newmap));
-       if (!newmap)
-               return -1;
-       ret = sscanf(map, "%c:%ld:%ld:%ld", &newmap->which, &newmap->ns_id, &newmap->host_id, &newmap->range);
+
+       ret = sscanf(map, "%c:%ld:%ld:%ld", &which, &ns_id, &host_id, &range);
        if (ret != 4)
-               goto out_free_map;
-       if (newmap->which != 'b' && newmap->which != 'u' && newmap->which != 'g')
-               goto out_free_map;
-       if (active_map != &default_map)
-               newmap->next = active_map;
-       else
-               newmap->next = NULL;
-       active_map = newmap;
-       return 0;
+               return -1;
 
-out_free_map:
-       free(newmap);
-       return -1;
+       if (which != 'b' && which != 'u' && which != 'g')
+               return -1;
+
+       for (i = 0; i < 2; i++) {
+               if (which != types[i] && which != 'b')
+                       continue;
+
+               newmap = malloc(sizeof(*newmap));
+               if (!newmap)
+                       return -1;
+
+               newmap->hostid = host_id;
+               newmap->nsid = ns_id;
+               newmap->range = range;
+
+               if (types[i] == 'u')
+                       newmap->idtype = ID_TYPE_UID;
+               else
+                       newmap->idtype = ID_TYPE_GID;
+
+               tmp = malloc(sizeof(*tmp));
+               if (!tmp) {
+                       free(newmap);
+                       return -1;
+               }
+
+               tmp->elem = newmap;
+               lxc_list_add_tail(&active_map, tmp);
+       }
+
+       return 0;
 }
 
 /*
@@ -185,12 +197,13 @@ out_free_map:
  * gid, because otherwise we're not sure which entries the user
  * wanted.
  */
-static int read_default_map(char *fnam, char which, char *username)
+static int read_default_map(char *fnam, int which, char *username)
 {
        FILE *fin;
        char *line = NULL;
        size_t sz = 0;
        struct id_map *newmap;
+       struct lxc_list *tmp = NULL;
        char *p1, *p2;
 
        fin = fopen(fnam, "r");
@@ -213,15 +226,21 @@ static int read_default_map(char *fnam, char which, char *username)
                        free(line);
                        return -1;
                }
-               newmap->host_id = atol(p1+1);
+               newmap->hostid = atol(p1+1);
                newmap->range = atol(p2+1);
-               newmap->ns_id = 0;
-               newmap->which = which;
-               if (active_map != &default_map)
-                       newmap->next = active_map;
-               else
-                       newmap->next = NULL;
-               active_map = newmap;
+               newmap->nsid = 0;
+               newmap->idtype = which;
+
+               tmp = malloc(sizeof(*tmp));
+               if (!tmp) {
+                       fclose(fin);
+                       free(line);
+                       free(newmap);
+                       return -1;
+               }
+
+               tmp->elem = newmap;
+               lxc_list_add_tail(&active_map, tmp);
                break;
        }
 
@@ -238,119 +257,13 @@ static int find_default_map(void)
        struct passwd *p = getpwuid(getuid());
        if (!p)
                return -1;
-       if (read_default_map(subuidfile, 'u', p->pw_name) < 0)
+       if (read_default_map(subuidfile, ID_TYPE_UID, p->pw_name) < 0)
                return -1;
-       if (read_default_map(subgidfile, 'g', p->pw_name) < 0)
+       if (read_default_map(subgidfile, ID_TYPE_GID, p->pw_name) < 0)
                return -1;
     return 0;
 }
 
-static int run_cmd(char **argv)
-{
-    int status;
-       pid_t pid = fork();
-
-       if (pid < 0)
-               return pid;
-       if (pid == 0) {
-               execvp(argv[0], argv);
-               perror("exec failed");
-               exit(1);
-       }
-       if (waitpid(pid, &status, __WALL) < 0) {
-        perror("waitpid");
-               return -1;
-       }
-
-       return WEXITSTATUS(status);
-}
-
-static int map_child_uids(int pid, struct id_map *map)
-{
-       char **uidargs = NULL, **gidargs = NULL;
-       char **newuidargs = NULL, **newgidargs = NULL;
-       int i, nuargs = 2, ngargs = 2, ret = -1;
-       struct id_map *m;
-
-       uidargs = malloc(3 * sizeof(*uidargs));
-       if (uidargs == NULL)
-               return -1;
-       gidargs = malloc(3 * sizeof(*gidargs));
-       if (gidargs == NULL) {
-               free(uidargs);
-               return -1;
-       }
-       uidargs[0] = malloc(10);
-       gidargs[0] = malloc(10);
-       uidargs[1] = malloc(21);
-       gidargs[1] = malloc(21);
-       uidargs[2] = NULL;
-       gidargs[2] = NULL;
-       if (!uidargs[0] || !uidargs[1] || !gidargs[0] || !gidargs[1])
-               goto out;
-       sprintf(uidargs[0], "newuidmap");
-       sprintf(gidargs[0], "newgidmap");
-       sprintf(uidargs[1], "%d", pid);
-       sprintf(gidargs[1], "%d", pid);
-       for (m=map; m; m = m->next) {
-               if (m->which == 'b' || m->which == 'u') {
-                       nuargs += 3;
-                       newuidargs = realloc(uidargs, (nuargs+1) * sizeof(*uidargs));
-                       if (!newuidargs)
-                               goto out;
-                       uidargs = newuidargs;
-                       uidargs[nuargs - 3] = malloc(21);
-                       uidargs[nuargs - 2] = malloc(21);
-                       uidargs[nuargs - 1] = malloc(21);
-                       if (!uidargs[nuargs-3] || !uidargs[nuargs-2] || !uidargs[nuargs-1])
-                               goto out;
-                       sprintf(uidargs[nuargs - 3], "%ld", m->ns_id);
-                       sprintf(uidargs[nuargs - 2], "%ld", m->host_id);
-                       sprintf(uidargs[nuargs - 1], "%ld", m->range);
-                       uidargs[nuargs] = NULL;
-               }
-               if (m->which == 'b' || m->which == 'g') {
-                       ngargs += 3;
-                       newgidargs = realloc(gidargs, (ngargs+1) * sizeof(*gidargs));
-                       if (!newgidargs)
-                               goto out;
-                       gidargs = newgidargs;
-                       gidargs[ngargs - 3] = malloc(21);
-                       gidargs[ngargs - 2] = malloc(21);
-                       gidargs[ngargs - 1] = malloc(21);
-                       if (!gidargs[ngargs-3] || !gidargs[ngargs-2] || !gidargs[ngargs-1])
-                               goto out;
-                       sprintf(gidargs[ngargs - 3], "%ld", m->ns_id);
-                       sprintf(gidargs[ngargs - 2], "%ld", m->host_id);
-                       sprintf(gidargs[ngargs - 1], "%ld", m->range);
-                       gidargs[ngargs] = NULL;
-               }
-       }
-
-       ret = -2;
-       // exec newuidmap
-       if (nuargs > 2 && run_cmd(uidargs) != 0) {
-               fprintf(stderr, "Error mapping uids\n");
-               goto out;
-       }
-       // exec newgidmap
-       if (ngargs > 2 && run_cmd(gidargs) != 0) {
-               fprintf(stderr, "Error mapping gids\n");
-               goto out;
-       }
-       ret = 0;
-
-out:
-       for (i=0; i<nuargs; i++)
-               free(uidargs[i]);
-       for (i=0; i<ngargs; i++)
-               free(gidargs[i]);
-       free(uidargs);
-       free(gidargs);
-
-       return ret;
-}
-
 int main(int argc, char *argv[])
 {
        int c;
@@ -371,6 +284,8 @@ int main(int argc, char *argv[])
                exit(1);
        }
 
+       lxc_list_init(&active_map);
+
        while ((c = getopt(argc, argv, "m:h")) != EOF) {
                switch (c) {
                        case 'm': if (parse_map(optarg)) usage(argv[0]); break;
@@ -380,7 +295,7 @@ int main(int argc, char *argv[])
                }
        };
 
-       if (active_map == &default_map) {
+       if (lxc_list_empty(&active_map)) {
                if (find_default_map()) {
                        fprintf(stderr, "You have no allocated subuids or subgids\n");
                        exit(1);
@@ -437,7 +352,8 @@ int main(int argc, char *argv[])
        }
 
        buf[0] = '1';
-       if (map_child_uids(pid, active_map)) {
+
+       if (lxc_map_ids(&active_map, pid)) {
                fprintf(stderr, "error mapping child\n");
                ret = 0;
        }
index 04277912fd09cd1ae58a208668a2bde3705fad57..d9aa973f47b3c1a3c330ada7b9167f9f4ac298c2 100644 (file)
@@ -806,7 +806,7 @@ static struct bdev *do_bdev_create(struct lxc_container *c, const char *type,
        /* if we are not root, chown the rootfs dir to root in the
         * target uidmap */
 
-       if (geteuid() != 0) {
+       if (geteuid() != 0 || (c->lxc_conf && !lxc_list_empty(&c->lxc_conf->id_map))) {
                if (chown_mapped_root(bdev->dest, c->lxc_conf) < 0) {
                        ERROR("Error chowning %s to container root", bdev->dest);
                        bdev_put(bdev);
@@ -992,7 +992,7 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool quiet
                 * and we append "--mapped-uid x", where x is the mapped uid
                 * for our geteuid()
                 */
-               if (geteuid() != 0 && !lxc_list_empty(&conf->id_map)) {
+               if (!lxc_list_empty(&conf->id_map)) {
                        int n2args = 1;
                        char txtuid[20];
                        char txtgid[20];
@@ -1450,7 +1450,7 @@ static inline bool enter_to_ns(struct lxc_container *c) {
        init_pid = c->init_pid(c);
 
        /* Switch to new userns */
-       if (geteuid() && access("/proc/self/ns/user", F_OK) == 0) {
+       if ((geteuid() != 0 || (c->lxc_conf && !lxc_list_empty(&c->lxc_conf->id_map))) && access("/proc/self/ns/user", F_OK) == 0) {
                ret = snprintf(new_userns_path, MAXPATHLEN, "/proc/%d/ns/user", init_pid);
                if (ret < 0 || ret >= MAXPATHLEN)
                        goto out;
index da7c3b4709dd63f7997f48344628dec4c5d91287..fc24985ca6024a65d15c6d3f82f2ba902e98e5f9 100644 (file)
@@ -1234,3 +1234,38 @@ int detect_shared_rootfs(void)
        fclose(f);
        return 0;
 }
+
+bool on_path(char *cmd) {
+       char *path = NULL;
+       char *entry = NULL;
+       char *saveptr = NULL;
+       char cmdpath[MAXPATHLEN];
+       int ret;
+
+       path = getenv("PATH");
+       if (!path)
+               return false;
+
+       path = strdup(path);
+       if (!path)
+               return false;
+
+       entry = strtok_r(path, ":", &saveptr);
+       while (entry) {
+               ret = snprintf(cmdpath, MAXPATHLEN, "%s/%s", entry, cmd);
+
+               if (ret < 0 || ret >= MAXPATHLEN)
+                       goto next_loop;
+
+               if (access(cmdpath, X_OK) == 0) {
+                       free(path);
+                       return true;
+               }
+
+next_loop:
+               entry = strtok(NULL, ":");
+       }
+
+       free(path);
+       return false;
+}
index dcf0e344590c0a095ff0475e2eab9fc3b7779e86..978f5860a4a2106ce84b4c5edf1c56f35ae9ce96 100644 (file)
@@ -278,3 +278,4 @@ uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval);
 #endif
 
 int detect_shared_rootfs(void);
+bool on_path(char *cmd);