From: Stéphane Graber Date: Wed, 26 Feb 2014 18:00:36 +0000 (-0500) Subject: Fix unprivileged containers started by root X-Git-Tag: lxc-1.1.0.alpha1~258 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0e6e3a41089c86447fef18e54c2796b312a57a94;p=thirdparty%2Flxc.git Fix unprivileged containers started by root 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 Acked-by: Serge E. Hallyn --- diff --git a/src/lxc/conf.c b/src/lxc/conf.c index b6bc4142b..d99659a68 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -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); diff --git a/src/lxc/lxc_usernsexec.c b/src/lxc/lxc_usernsexec.c index 2efc687ea..f5b7fe06a 100644 --- a/src/lxc/lxc_usernsexec.c +++ b/src/lxc/lxc_usernsexec.c @@ -41,6 +41,7 @@ #include #include +#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; ilxc_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; diff --git a/src/lxc/utils.c b/src/lxc/utils.c index da7c3b470..fc24985ca 100644 --- a/src/lxc/utils.c +++ b/src/lxc/utils.c @@ -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; +} diff --git a/src/lxc/utils.h b/src/lxc/utils.h index dcf0e3445..978f5860a 100644 --- a/src/lxc/utils.h +++ b/src/lxc/utils.h @@ -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);