Skip to content

Commit

Permalink
fix chowning of tty and console uids
Browse files Browse the repository at this point in the history
It needs to be done from the handler, not the container, since
the container may not have the rights.

Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
Acked-by: Stéphane Graber <stgraber@ubuntu.com>

Changelog:
    Jul 22: remove hardcoded path for /bin/chown
    Jul 22: use new lxc-usernsexec

Conflicts:
	src/lxc/lxccontainer.c
  • Loading branch information
hallyn committed Oct 24, 2013
1 parent cf3ef16 commit c4d10a0
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 127 deletions.
126 changes: 59 additions & 67 deletions src/lxc/conf.c
Expand Up @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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;
if ((rootid = get_mapped_rootid(conf)) <= 0) {
ERROR("No mapping for container root");
return -1;
}

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 (geteuid() == 0) {
if (chown(path, rootid, -1) < 0) {
ERROR("Error chowning %s", path);
return -1;
}
return 0;
}
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};

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);
// "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);
}
return 0;
return wait_for_pid(pid);
}

int uid_shift_ttys(int pid, struct lxc_conf *conf)
int ttys_shift_ids(struct lxc_conf *c)
{
int i, ret;
struct lxc_tty_info *tty_info = &conf->tty_info;
char path[MAXPATHLEN];
char *ttydir = conf->ttydir;
int i;

if (!conf->rootfs.path)
if (lxc_list_empty(&c->id_map))
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");
return -1;
}
if (uid_shift_file(path, conf)) {
DEBUG("Failed to chown the console %s.\n", path);
return -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);

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;
}

Expand Down
6 changes: 3 additions & 3 deletions src/lxc/conf.h
Expand Up @@ -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
*/
Expand All @@ -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
54 changes: 2 additions & 52 deletions src/lxc/lxccontainer.c
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
}
Expand Down
10 changes: 5 additions & 5 deletions src/lxc/start.c
Expand Up @@ -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;

Expand Down Expand Up @@ -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;

Expand Down

0 comments on commit c4d10a0

Please sign in to comment.