mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-07-14 17:38:04 +00:00
fix chowning of tty and console uids
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
This commit is contained in:
parent
cf3ef16dc4
commit
c4d10a05c3
126
src/lxc/conf.c
126
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
|
* return the host uid to which the container root is mapped, or -1 on
|
||||||
* error
|
* error
|
||||||
*/
|
*/
|
||||||
int get_mapped_rootid(struct lxc_conf *conf)
|
uid_t get_mapped_rootid(struct lxc_conf *conf)
|
||||||
{
|
{
|
||||||
struct lxc_list *it;
|
struct lxc_list *it;
|
||||||
struct id_map *map;
|
struct id_map *map;
|
||||||
@ -2869,9 +2869,9 @@ int get_mapped_rootid(struct lxc_conf *conf)
|
|||||||
continue;
|
continue;
|
||||||
if (map->nsid != 0)
|
if (map->nsid != 0)
|
||||||
continue;
|
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)
|
bool hostid_is_mapped(int id, struct lxc_conf *conf)
|
||||||
@ -3020,88 +3020,80 @@ void lxc_delete_tty(struct lxc_tty_info *tty_info)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* given a host uid, return the ns uid if it is mapped.
|
* chown_mapped_root: for an unprivileged user with uid X to chown a dir
|
||||||
* if it is not mapped, return the original host id.
|
* 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;
|
uid_t rootid;
|
||||||
struct id_map *map;
|
pid_t pid;
|
||||||
int low, high;
|
|
||||||
|
|
||||||
lxc_list_for_each(iterator, &c->id_map) {
|
if ((rootid = get_mapped_rootid(conf)) <= 0) {
|
||||||
map = iterator->elem;
|
ERROR("No mapping for container root");
|
||||||
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);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
if (geteuid() == 0) {
|
||||||
newuid = shiftid(c, statbuf.st_uid, ID_TYPE_UID);
|
if (chown(path, rootid, -1) < 0) {
|
||||||
newgid = shiftid(c, statbuf.st_gid, ID_TYPE_GID);
|
ERROR("Error chowning %s", path);
|
||||||
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);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return 0;
|
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};
|
||||||
|
|
||||||
int uid_shift_ttys(int pid, struct lxc_conf *conf)
|
// "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 wait_for_pid(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ttys_shift_ids(struct lxc_conf *c)
|
||||||
{
|
{
|
||||||
int i, ret;
|
int i;
|
||||||
struct lxc_tty_info *tty_info = &conf->tty_info;
|
|
||||||
char path[MAXPATHLEN];
|
|
||||||
char *ttydir = conf->ttydir;
|
|
||||||
|
|
||||||
if (!conf->rootfs.path)
|
if (lxc_list_empty(&c->id_map))
|
||||||
return 0;
|
return 0;
|
||||||
/* first the console */
|
|
||||||
ret = snprintf(path, sizeof(path), "/proc/%d/root/dev/%s/console", pid, ttydir ? ttydir : "");
|
for (i = 0; i < c->tty_info.nbtty; i++) {
|
||||||
if (ret < 0 || ret >= sizeof(path)) {
|
struct lxc_pty_info *pty_info = &c->tty_info.pty_info[i];
|
||||||
ERROR("console path too long\n");
|
|
||||||
|
if (chown_mapped_root(pty_info->name, c) < 0) {
|
||||||
|
ERROR("Failed to chown %s", pty_info->name);
|
||||||
return -1;
|
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",
|
if (chown_mapped_root(c->console.name, c) < 0) {
|
||||||
pid, ttydir ? ttydir : "", i + 1);
|
ERROR("Failed to chown %s", c->console.name);
|
||||||
if (ret < 0 || ret >= sizeof(path)) {
|
|
||||||
ERROR("pathname too long for ttys");
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (uid_shift_file(path, conf)) {
|
|
||||||
DEBUG("Failed to chown pty %s.\n", path);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -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_mount_entries(struct lxc_conf *c);
|
||||||
extern int lxc_clear_hooks(struct lxc_conf *c, const char *key);
|
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
|
* 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 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 int find_unmapped_nsuid(struct lxc_conf *conf);
|
||||||
extern bool hostid_is_mapped(int id, 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
|
#endif
|
||||||
|
@ -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 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);
|
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(),
|
* do_bdev_create: thin wrapper around bdev_create(). Like bdev_create(),
|
||||||
* it returns a mounted bdev on success, NULL on error.
|
* 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 */
|
* target uidmap */
|
||||||
|
|
||||||
if (geteuid() != 0) {
|
if (geteuid() != 0) {
|
||||||
int rootid;
|
if (chown_mapped_root(bdev->dest, c->lxc_conf) < 0) {
|
||||||
if ((rootid = get_mapped_rootid(c->lxc_conf)) <= 0) {
|
ERROR("Error chowning %s to container root\n", bdev->dest);
|
||||||
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);
|
|
||||||
bdev_put(bdev);
|
bdev_put(bdev);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -353,6 +353,11 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char
|
|||||||
goto out_restore_sigmask;
|
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);
|
INFO("'%s' is initialized", name);
|
||||||
return handler;
|
return handler;
|
||||||
|
|
||||||
@ -784,11 +789,6 @@ int lxc_spawn(struct lxc_handler *handler)
|
|||||||
if (detect_shared_rootfs())
|
if (detect_shared_rootfs())
|
||||||
umount2(handler->conf->rootfs.mount, MNT_DETACH);
|
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))
|
if (handler->ops->post_start(handler, handler->data))
|
||||||
goto out_abort;
|
goto out_abort;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user