diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 8c48377ff..043719cde 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -2858,7 +2858,7 @@ int lxc_map_ids(struct lxc_list *idmap, pid_t pid) * return the host uid to which the container root is mapped, or -1 on * error */ -int get_mapped_rootid(struct lxc_conf *conf) +uid_t get_mapped_rootid(struct lxc_conf *conf) { struct lxc_list *it; struct id_map *map; @@ -2869,9 +2869,9 @@ int get_mapped_rootid(struct lxc_conf *conf) continue; if (map->nsid != 0) continue; - return map->hostid; + return (uid_t) map->hostid; } - return -1; + return (uid_t)-1; } bool hostid_is_mapped(int id, struct lxc_conf *conf) @@ -3020,89 +3020,81 @@ void lxc_delete_tty(struct lxc_tty_info *tty_info) } /* - * given a host uid, return the ns uid if it is mapped. - * if it is not mapped, return the original host id. + * chown_mapped_root: for an unprivileged user with uid X to chown a dir + * to subuid Y, he needs to run chown as root in a userns where + * nsid 0 is mapped to hostuid Y, and nsid Y is mapped to hostuid + * X. That way, the container root is privileged with respect to + * hostuid X, allowing him to do the chown. */ -static int shiftid(struct lxc_conf *c, int uid, enum idtype w) +int chown_mapped_root(char *path, struct lxc_conf *conf) { - struct lxc_list *iterator; - struct id_map *map; - int low, high; + uid_t rootid; + pid_t pid; - lxc_list_for_each(iterator, &c->id_map) { - map = iterator->elem; - if (map->idtype != w) - continue; - - low = map->nsid; - high = map->nsid + map->range; - if (uid < low || uid >= high) - continue; - - return uid - low + map->hostid; - } - - return uid; -} - -/* - * Take a pathname for a file created on the host, and map the uid and gid - * into the container if needed. (Used for ttys) - */ -static int uid_shift_file(char *path, struct lxc_conf *c) -{ - struct stat statbuf; - int newuid, newgid; - - if (stat(path, &statbuf)) { - SYSERROR("stat(%s)", path); + if ((rootid = get_mapped_rootid(conf)) <= 0) { + ERROR("No mapping for container root"); return -1; } - - newuid = shiftid(c, statbuf.st_uid, ID_TYPE_UID); - newgid = shiftid(c, statbuf.st_gid, ID_TYPE_GID); - if (newuid != statbuf.st_uid || newgid != statbuf.st_gid) { - DEBUG("chowning %s from %d:%d to %d:%d\n", path, (int)statbuf.st_uid, (int)statbuf.st_gid, newuid, newgid); - if (chown(path, newuid, newgid)) { - SYSERROR("chown(%s)", path); + if (geteuid() == 0) { + if (chown(path, rootid, -1) < 0) { + ERROR("Error chowning %s", path); return -1; } - } - return 0; -} - -int uid_shift_ttys(int pid, struct lxc_conf *conf) -{ - int i, ret; - struct lxc_tty_info *tty_info = &conf->tty_info; - char path[MAXPATHLEN]; - char *ttydir = conf->ttydir; - - if (!conf->rootfs.path) return 0; - /* first the console */ - ret = snprintf(path, sizeof(path), "/proc/%d/root/dev/%s/console", pid, ttydir ? ttydir : ""); - if (ret < 0 || ret >= sizeof(path)) { - ERROR("console path too long\n"); + } + pid = fork(); + if (pid < 0) { + SYSERROR("Failed forking"); return -1; } - if (uid_shift_file(path, conf)) { - DEBUG("Failed to chown the console %s.\n", path); - return -1; + if (!pid) { + int hostuid = geteuid(), ret; + char map1[100], map2[100]; + char *args[] = {"lxc-usernsexec", "-m", map1, "-m", map2, "--", "chown", + "0", path, NULL}; + + // "b:0:rootid:1" + ret = snprintf(map1, 100, "b:0:%d:1", rootid); + if (ret < 0 || ret >= 100) { + ERROR("Error uid printing map string"); + return -1; + } + + // "b:hostuid:hostuid:1" + ret = snprintf(map2, 100, "b:%d:%d:1", hostuid, hostuid); + if (ret < 0 || ret >= 100) { + ERROR("Error uid printing map string"); + return -1; + } + + ret = execvp("lxc-usernsexec", args); + SYSERROR("Failed executing usernsexec"); + exit(1); } - for (i=0; i< tty_info->nbtty; i++) { - ret = snprintf(path, sizeof(path), "/proc/%d/root/dev/%s/tty%d", - pid, ttydir ? ttydir : "", i + 1); - if (ret < 0 || ret >= sizeof(path)) { - ERROR("pathname too long for ttys"); - return -1; - } - if (uid_shift_file(path, conf)) { - DEBUG("Failed to chown pty %s.\n", path); + return wait_for_pid(pid); +} + +int ttys_shift_ids(struct lxc_conf *c) +{ + int i; + + if (lxc_list_empty(&c->id_map)) + return 0; + + for (i = 0; i < c->tty_info.nbtty; i++) { + struct lxc_pty_info *pty_info = &c->tty_info.pty_info[i]; + + if (chown_mapped_root(pty_info->name, c) < 0) { + ERROR("Failed to chown %s", pty_info->name); return -1; } } + if (chown_mapped_root(c->console.name, c) < 0) { + ERROR("Failed to chown %s", c->console.name); + return -1; + } + return 0; } diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 445867d22..71399b920 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -350,8 +350,6 @@ extern int lxc_clear_cgroups(struct lxc_conf *c, const char *key); extern int lxc_clear_mount_entries(struct lxc_conf *c); extern int lxc_clear_hooks(struct lxc_conf *c, const char *key); -extern int uid_shift_ttys(int pid, struct lxc_conf *conf); - /* * Configure the container from inside */ @@ -362,7 +360,9 @@ extern int lxc_setup(const char *name, struct lxc_conf *lxc_conf, extern void lxc_rename_phys_nics_on_shutdown(struct lxc_conf *conf); -extern int get_mapped_rootid(struct lxc_conf *conf); +extern uid_t get_mapped_rootid(struct lxc_conf *conf); extern int find_unmapped_nsuid(struct lxc_conf *conf); extern bool hostid_is_mapped(int id, struct lxc_conf *conf); +extern int chown_mapped_root(char *path, struct lxc_conf *conf); +extern int ttys_shift_ids(struct lxc_conf *c); #endif diff --git a/src/lxc/lxccontainer.c b/src/lxc/lxccontainer.c index fca0da2fe..9aea6145e 100644 --- a/src/lxc/lxccontainer.c +++ b/src/lxc/lxccontainer.c @@ -693,49 +693,6 @@ static bool create_container_dir(struct lxc_container *c) static const char *lxcapi_get_config_path(struct lxc_container *c); static bool lxcapi_set_config_item(struct lxc_container *c, const char *key, const char *v); -/* - * chown_mapped: for an unprivileged user with uid X to chown a dir - * to subuid Y, he needs to run chown as root in a userns where - * nsid 0 is mapped to hostuid Y, and nsid Y is mapped to hostuid - * X. That way, the container root is privileged with respect to - * hostuid X, allowing him to do the chown. - */ -static int chown_mapped(int nsrootid, char *path) -{ - if (nsrootid < 0) - return nsrootid; - pid_t pid = fork(); - if (pid < 0) { - SYSERROR("Failed forking"); - return -1; - } - if (!pid) { - int hostuid = geteuid(), ret; - char map1[100], map2[100]; - char *args[] = {"lxc-usernsexec", "-m", map1, "-m", map2, "--", "chown", - "0", path, NULL}; - - // "b:0:nsrootid:1" - ret = snprintf(map1, 100, "b:0:%d:1", nsrootid); - if (ret < 0 || ret >= 100) { - ERROR("Error uid printing map string"); - return -1; - } - - // "b:hostuid:hostuid:1" - ret = snprintf(map2, 100, "b:%d:%d:1", hostuid, hostuid); - if (ret < 0 || ret >= 100) { - ERROR("Error uid printing map string"); - return -1; - } - - ret = execvp("lxc-usernsexec", args); - SYSERROR("Failed executing lxc-usernsexec"); - exit(1); - } - return wait_for_pid(pid); -} - /* * do_bdev_create: thin wrapper around bdev_create(). Like bdev_create(), * it returns a mounted bdev on success, NULL on error. @@ -768,15 +725,8 @@ static struct bdev *do_bdev_create(struct lxc_container *c, const char *type, * target uidmap */ if (geteuid() != 0) { - int rootid; - if ((rootid = get_mapped_rootid(c->lxc_conf)) <= 0) { - ERROR("No mapping for container root"); - bdev_put(bdev); - return NULL; - } - ret = chown_mapped(rootid, bdev->dest); - if (ret < 0) { - ERROR("Error chowning %s to %d\n", bdev->dest, rootid); + if (chown_mapped_root(bdev->dest, c->lxc_conf) < 0) { + ERROR("Error chowning %s to container root\n", bdev->dest); bdev_put(bdev); return NULL; } diff --git a/src/lxc/start.c b/src/lxc/start.c index e46f3a0ac..1cadc0963 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -353,6 +353,11 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char goto out_restore_sigmask; } + if (ttys_shift_ids(conf) < 0) { + ERROR("Failed to shift tty into container"); + goto out_restore_sigmask; + } + INFO("'%s' is initialized", name); return handler; @@ -784,11 +789,6 @@ int lxc_spawn(struct lxc_handler *handler) if (detect_shared_rootfs()) umount2(handler->conf->rootfs.mount, MNT_DETACH); - /* If child is in a fresh user namespace, chown his ptys for - * it */ - if (uid_shift_ttys(handler->pid, handler->conf)) - DEBUG("Failed to chown ptys.\n"); - if (handler->ops->post_start(handler, handler->data)) goto out_abort;