[lxc-devel] [PATCH 2/3] fix chowning of tty and console uids

Stéphane Graber stgraber at ubuntu.com
Wed Oct 23 23:11:52 UTC 2013


On Wed, Oct 23, 2013 at 01:02:58AM +0000, Serge Hallyn wrote:
> From: Serge Hallyn <serge.hallyn at ubuntu.com>
> 
> 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 at ubuntu.com>

Acked-by: Stéphane Graber <stgraber at ubuntu.com>

> Changelog:
>     Jul 22: remove hardcoded path for /bin/chown
>     Jul 22: use new lxc-usernsexec
> 
> Conflicts:
> 	src/lxc/lxccontainer.c
> ---
>  src/lxc/conf.c         | 126 +++++++++++++++++++++++--------------------------
>  src/lxc/conf.h         |   6 +--
>  src/lxc/lxccontainer.c |  54 +--------------------
>  src/lxc/start.c        |  10 ++--
>  4 files changed, 69 insertions(+), 127 deletions(-)
> 
> diff --git a/src/lxc/conf.c b/src/lxc/conf.c
> index 3f7f0ef..bba6379 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;
> +	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;
>  }
>  
> diff --git a/src/lxc/conf.h b/src/lxc/conf.h
> index 445867d..71399b9 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 816eb39..38b67ca 100644
> --- a/src/lxc/lxccontainer.c
> +++ b/src/lxc/lxccontainer.c
> @@ -694,49 +694,6 @@ 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 e46f3a0..1cadc09 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;
>  
> -- 
> 1.8.1.2
> 
> 
> ------------------------------------------------------------------------------
> October Webinars: Code for Performance
> Free Intel webinars can help you accelerate application performance.
> Explore tips for MPI, OpenMP, advanced profiling, and more. Get the most from 
> the latest Intel processors and coprocessors. See abstracts and register >
> http://pubads.g.doubleclick.net/gampad/clk?id=60135991&iu=/4140/ostg.clktrk
> _______________________________________________
> Lxc-devel mailing list
> Lxc-devel at lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/lxc-devel

-- 
Stéphane Graber
Ubuntu developer
http://www.ubuntu.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20131023/17987124/attachment.pgp>


More information about the lxc-devel mailing list