Unverified Commit fd7374db by Serge Hallyn Committed by GitHub

Merge pull request #2066 from brauner/2017-01-02/support_no_root_mappings

Support configurations without root mapping
parents 2b33c8bf f4900711
......@@ -1267,25 +1267,41 @@ next:
return r;
}
struct generic_userns_exec_data {
struct cgfsng_handler_data *d;
struct lxc_conf *conf;
uid_t origuid; /* target uid in parent namespace */
char *path;
};
static int rmdir_wrapper(void *data)
{
char *path = data;
struct generic_userns_exec_data *arg = data;
uid_t nsuid = (arg->conf->root_nsuid_map != NULL) ? 0 : arg->conf->init_uid;
gid_t nsgid = (arg->conf->root_nsgid_map != NULL) ? 0 : arg->conf->init_gid;
if (setresgid(0,0,0) < 0)
if (setresgid(nsgid, nsgid, nsgid) < 0)
SYSERROR("Failed to setgid to 0");
if (setresuid(0,0,0) < 0)
if (setresuid(nsuid, nsuid, nsuid) < 0)
SYSERROR("Failed to setuid to 0");
if (setgroups(0, NULL) < 0)
SYSERROR("Failed to clear groups");
return cgroup_rmdir(path);
return cgroup_rmdir(arg->path);
}
void recursive_destroy(char *path, struct lxc_conf *conf)
{
int r;
struct generic_userns_exec_data wrap;
wrap.origuid = 0;
wrap.d = NULL;
wrap.path = path;
wrap.conf = conf;
if (conf && !lxc_list_empty(&conf->id_map))
r = userns_exec_1(conf, rmdir_wrapper, path, "rmdir_wrapper");
r = userns_exec_1(conf, rmdir_wrapper, &wrap, "rmdir_wrapper");
else
r = cgroup_rmdir(path);
......@@ -1445,11 +1461,6 @@ static bool cgfsng_enter(void *hdata, pid_t pid)
return true;
}
struct chown_data {
struct cgfsng_handler_data *d;
uid_t origuid; /* target uid in parent namespace */
};
/*
* chgrp the container cgroups to container group. We leave
* the container owner as cgroup owner. So we must make the
......@@ -1460,13 +1471,15 @@ struct chown_data {
*/
static int chown_cgroup_wrapper(void *data)
{
struct chown_data *arg = data;
uid_t destuid;
int i;
uid_t destuid;
struct generic_userns_exec_data *arg = data;
uid_t nsuid = (arg->conf->root_nsuid_map != NULL) ? 0 : arg->conf->init_uid;
gid_t nsgid = (arg->conf->root_nsgid_map != NULL) ? 0 : arg->conf->init_gid;
if (setresgid(0,0,0) < 0)
if (setresgid(nsgid, nsgid, nsgid) < 0)
SYSERROR("Failed to setgid to 0");
if (setresuid(0,0,0) < 0)
if (setresuid(nsuid, nsuid, nsuid) < 0)
SYSERROR("Failed to setuid to 0");
if (setgroups(0, NULL) < 0)
SYSERROR("Failed to clear groups");
......@@ -1476,7 +1489,7 @@ static int chown_cgroup_wrapper(void *data)
for (i = 0; hierarchies[i]; i++) {
char *fullpath, *path = hierarchies[i]->fullcgpath;
if (chown(path, destuid, 0) < 0) {
if (chown(path, destuid, nsgid) < 0) {
SYSERROR("Error chowning %s to %d", path, (int) destuid);
return -1;
}
......@@ -1494,7 +1507,7 @@ static int chown_cgroup_wrapper(void *data)
* insists on doing)
*/
fullpath = must_make_path(path, "tasks", NULL);
if (chown(fullpath, destuid, 0) < 0 && errno != ENOENT)
if (chown(fullpath, destuid, nsgid) < 0 && errno != ENOENT)
WARN("Failed chowning %s to %d: %s", fullpath, (int) destuid,
strerror(errno));
if (chmod(fullpath, 0664) < 0)
......@@ -1513,7 +1526,7 @@ static int chown_cgroup_wrapper(void *data)
continue;
fullpath = must_make_path(path, "cgroup.subtree_control", NULL);
if (chown(fullpath, destuid, 0) < 0 && errno != ENOENT)
if (chown(fullpath, destuid, nsgid) < 0 && errno != ENOENT)
WARN("Failed chowning %s to %d: %s", fullpath, (int) destuid,
strerror(errno));
if (chmod(fullpath, 0664) < 0)
......@@ -1521,7 +1534,7 @@ static int chown_cgroup_wrapper(void *data)
free(fullpath);
fullpath = must_make_path(path, "cgroup.threads", NULL);
if (chown(fullpath, destuid, 0) < 0 && errno != ENOENT)
if (chown(fullpath, destuid, nsgid) < 0 && errno != ENOENT)
WARN("Failed chowning %s to %d: %s", fullpath, (int) destuid,
strerror(errno));
if (chmod(fullpath, 0664) < 0)
......@@ -1535,7 +1548,7 @@ static int chown_cgroup_wrapper(void *data)
static bool cgfsng_chown(void *hdata, struct lxc_conf *conf)
{
struct cgfsng_handler_data *d = hdata;
struct chown_data wrap;
struct generic_userns_exec_data wrap;
if (!d)
return false;
......@@ -1543,8 +1556,10 @@ static bool cgfsng_chown(void *hdata, struct lxc_conf *conf)
if (lxc_list_empty(&conf->id_map))
return true;
wrap.d = d;
wrap.origuid = geteuid();
wrap.path = NULL;
wrap.d = d;
wrap.conf = conf;
if (userns_exec_1(conf, chown_cgroup_wrapper, &wrap,
"chown_cgroup_wrapper") < 0) {
......
......@@ -1416,20 +1416,46 @@ static int setup_pivot_root(const struct lxc_rootfs *rootfs)
return 0;
}
static int lxc_setup_devpts(int num_pts)
static struct id_map *find_mapped_nsid_entry(struct lxc_conf *conf, unsigned id,
enum idtype idtype)
{
struct lxc_list *it;
struct id_map *map;
struct id_map *retmap = NULL;
lxc_list_for_each(it, &conf->id_map) {
map = it->elem;
if (map->idtype != idtype)
continue;
if (id >= map->nsid && id < map->nsid + map->range) {
retmap = map;
break;
}
}
return retmap;
}
static int lxc_setup_devpts(struct lxc_conf *conf)
{
int ret;
const char *default_devpts_mntopts = "newinstance,ptmxmode=0666,mode=0620,gid=5";
const char *default_devpts_mntopts;
char devpts_mntopts[256];
if (!num_pts) {
if (conf->pts <= 0) {
DEBUG("no new devpts instance will be mounted since no pts "
"devices are requested");
return 0;
}
if (!find_mapped_nsid_entry(conf, 5, ID_TYPE_GID))
default_devpts_mntopts = "newinstance,ptmxmode=0666,mode=0620";
else
default_devpts_mntopts = "newinstance,ptmxmode=0666,mode=0620,gid=5";
ret = snprintf(devpts_mntopts, sizeof(devpts_mntopts), "%s,max=%d",
default_devpts_mntopts, num_pts);
default_devpts_mntopts, conf->pts);
if (ret < 0 || (size_t)ret >= sizeof(devpts_mntopts))
return -1;
......@@ -1452,7 +1478,7 @@ static int lxc_setup_devpts(int num_pts)
}
/* Mount new devpts instance. */
ret = mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL, devpts_mntopts);
ret = mount("devpts", "/dev/pts", "devpts", MS_NOSUID | MS_NOEXEC, devpts_mntopts);
if (ret < 0) {
SYSERROR("failed to mount new devpts instance");
return -1;
......@@ -2573,6 +2599,8 @@ struct lxc_conf *lxc_conf_init(void)
lxc_list_init(&new->caps);
lxc_list_init(&new->keepcaps);
lxc_list_init(&new->id_map);
new->root_nsuid_map = NULL;
new->root_nsgid_map = NULL;
lxc_list_init(&new->includes);
lxc_list_init(&new->aliens);
lxc_list_init(&new->environment);
......@@ -2826,20 +2854,27 @@ int lxc_map_ids(struct lxc_list *idmap, pid_t pid)
* Return true if id was found, false otherwise.
*/
bool get_mapped_rootid(struct lxc_conf *conf, enum idtype idtype,
unsigned long *val)
unsigned long *val)
{
struct lxc_list *it;
struct id_map *map;
unsigned nsid;
if (idtype == ID_TYPE_UID)
nsid = (conf->root_nsuid_map != NULL) ? 0 : conf->init_uid;
else
nsid = (conf->root_nsgid_map != NULL) ? 0 : conf->init_gid;
lxc_list_for_each(it, &conf->id_map) {
map = it->elem;
if (map->idtype != idtype)
continue;
if (map->nsid != 0)
if (map->nsid != nsid)
continue;
*val = map->hostid;
return true;
}
return false;
}
......@@ -3314,7 +3349,7 @@ int lxc_setup(struct lxc_handler *handler)
return -1;
}
if (lxc_setup_devpts(lxc_conf->pts)) {
if (lxc_setup_devpts(lxc_conf)) {
ERROR("failed to setup the new pts instance");
return -1;
}
......@@ -3722,8 +3757,25 @@ static int run_userns_fn(void *data)
return d->fn(d->arg);
}
static struct id_map *mapped_hostid_entry(struct lxc_conf *conf, unsigned id,
enum idtype idtype)
static struct id_map *mapped_nsid_add(struct lxc_conf *conf, unsigned id,
enum idtype idtype)
{
struct id_map *map, *retmap;
map = find_mapped_nsid_entry(conf, id, idtype);
if (!map)
return NULL;
retmap = malloc(sizeof(*retmap));
if (!retmap)
return NULL;
memcpy(retmap, map, sizeof(*retmap));
return retmap;
}
static struct id_map *find_mapped_hostid_entry(struct lxc_conf *conf,
unsigned id, enum idtype idtype)
{
struct lxc_list *it;
struct id_map *map;
......@@ -3740,14 +3792,6 @@ static struct id_map *mapped_hostid_entry(struct lxc_conf *conf, unsigned id,
}
}
if (!retmap)
return NULL;
retmap = malloc(sizeof(*retmap));
if (!retmap)
return NULL;
memcpy(retmap, map, sizeof(*retmap));
return retmap;
}
......@@ -3755,27 +3799,28 @@ static struct id_map *mapped_hostid_entry(struct lxc_conf *conf, unsigned id,
* Allocate a new {g,u}id mapping for the given {g,u}id. Re-use an already
* existing one or establish a new one.
*/
static struct id_map *idmap_add(struct lxc_conf *conf, uid_t id, enum idtype type)
static struct id_map *mapped_hostid_add(struct lxc_conf *conf, uid_t id, enum idtype type)
{
int hostid_mapped;
struct id_map *entry = NULL;
struct id_map *entry = NULL, *tmp = NULL;
entry = malloc(sizeof(*entry));
if (!entry)
return NULL;
/* Reuse existing mapping. */
entry = mapped_hostid_entry(conf, id, type);
if (entry)
return entry;
tmp = find_mapped_hostid_entry(conf, id, type);
if (tmp)
return memcpy(entry, tmp, sizeof(*entry));
/* Find new mapping. */
hostid_mapped = find_unmapped_nsid(conf, type);
if (hostid_mapped < 0) {
DEBUG("failed to find free mapping for id %d", id);
DEBUG("Failed to find free mapping for id %d", id);
free(entry);
return NULL;
}
entry = malloc(sizeof(*entry));
if (!entry)
return NULL;
entry->idtype = type;
entry->nsid = hostid_mapped;
entry->hostid = (unsigned long)id;
......@@ -3806,6 +3851,8 @@ int userns_exec_1(struct lxc_conf *conf, int (*fn)(void *), void *data,
struct id_map *map;
char c = '1';
int ret = -1, status = -1;
uid_t nsuid = (conf->root_nsuid_map != NULL) ? 0 : conf->init_uid;
gid_t nsgid = (conf->root_nsgid_map != NULL) ? 0 : conf->init_gid;
struct lxc_list *idmap = NULL, *tmplist = NULL;
struct id_map *container_root_uid = NULL, *container_root_gid = NULL,
*host_uid_map = NULL, *host_gid_map = NULL;
......@@ -3831,71 +3878,39 @@ int userns_exec_1(struct lxc_conf *conf, int (*fn)(void *), void *data,
close(p[0]);
p[0] = -1;
/* Find container root mappings. */
euid = geteuid();
egid = getegid();
/* Find container root. */
lxc_list_for_each(it, &conf->id_map) {
map = it->elem;
if (map->nsid != 0)
continue;
if (map->idtype == ID_TYPE_UID && container_root_uid == NULL) {
container_root_uid = malloc(sizeof(*container_root_uid));
if (!container_root_uid)
goto on_error;
container_root_uid->idtype = map->idtype;
container_root_uid->hostid = map->hostid;
container_root_uid->nsid = 0;
container_root_uid->range = map->range;
/* Check if container root mapping contains a mapping
* for user's uid.
*/
if (euid >= map->hostid && euid < map->hostid + map->range)
host_uid_map = container_root_uid;
} else if (map->idtype == ID_TYPE_GID && container_root_gid == NULL) {
container_root_gid = malloc(sizeof(*container_root_gid));
if (!container_root_gid)
goto on_error;
container_root_gid->idtype = map->idtype;
container_root_gid->hostid = map->hostid;
container_root_gid->nsid = 0;
container_root_gid->range = map->range;
/* Check if container root mapping contains a mapping
* for user's gid.
*/
if (egid >= map->hostid && egid < map->hostid + map->range)
host_gid_map = container_root_gid;
}
/* Found container root. */
if (container_root_uid && container_root_gid)
break;
container_root_uid = mapped_nsid_add(conf, nsuid, ID_TYPE_UID);
if (!container_root_uid) {
DEBUG("Failed to find mapping for container root uid %d", 0);
goto on_error;
}
if (euid >= container_root_uid->hostid && euid < container_root_uid->hostid + container_root_uid->range)
host_uid_map = container_root_uid;
/* This is actually checked earlier but it can't hurt. */
if (!container_root_uid || !container_root_gid) {
ERROR("no mapping for container root found");
egid = getegid();
container_root_gid = mapped_nsid_add(conf, nsgid, ID_TYPE_GID);
if (!container_root_gid) {
DEBUG("Failed to find mapping for container root gid %d", 0);
goto on_error;
}
if (egid >= container_root_gid->hostid && egid < container_root_gid->hostid + container_root_gid->range)
host_gid_map = container_root_gid;
/* Check whether the {g,u}id of the user has a mapping. */
if (!host_uid_map)
host_uid_map = idmap_add(conf, euid, ID_TYPE_UID);
host_uid_map = mapped_hostid_add(conf, euid, ID_TYPE_UID);
if (!host_gid_map)
host_gid_map = idmap_add(conf, egid, ID_TYPE_GID);
host_gid_map = mapped_hostid_add(conf, egid, ID_TYPE_GID);
if (!host_uid_map) {
DEBUG("failed to find mapping for uid %d", euid);
DEBUG("Failed to find mapping for uid %d", euid);
goto on_error;
}
if (!host_gid_map) {
DEBUG("failed to find mapping for gid %d", egid);
DEBUG("Failed to find mapping for gid %d", egid);
goto on_error;
}
......@@ -4095,12 +4110,12 @@ int userns_exec_full(struct lxc_conf *conf, int (*fn)(void *), void *data,
/* Check whether the {g,u}id of the user has a mapping. */
if (!host_uid_map)
host_uid_map = idmap_add(conf, euid, ID_TYPE_UID);
host_uid_map = mapped_hostid_add(conf, euid, ID_TYPE_UID);
else
host_uid_map = container_root_uid;
if (!host_gid_map)
host_gid_map = idmap_add(conf, egid, ID_TYPE_GID);
host_gid_map = mapped_hostid_add(conf, egid, ID_TYPE_GID);
else
host_gid_map = container_root_gid;
......
......@@ -282,7 +282,15 @@ struct lxc_conf {
signed long personality;
struct utsname *utsname;
struct lxc_list cgroup;
struct lxc_list id_map;
struct {
struct lxc_list id_map;
/* Pointer to the idmap entry for the container's root uid in
* the id_map list. Do not free! */
struct id_map *root_nsuid_map;
/* Pointer to the idmap entry for the container's root gid in
* the id_map list. Do not free! */
struct id_map *root_nsgid_map;
};
struct lxc_list network;
int auto_mounts;
struct lxc_list mount_list;
......
......@@ -1681,6 +1681,16 @@ static int set_config_idmaps(const char *key, const char *value,
idmap->range = range;
idmaplist->elem = idmap;
lxc_list_add_tail(&lxc_conf->id_map, idmaplist);
if (!lxc_conf->root_nsuid_map && idmap->idtype == ID_TYPE_UID)
if (idmap->nsid == 0)
lxc_conf->root_nsuid_map = idmap;
if (!lxc_conf->root_nsgid_map && idmap->idtype == ID_TYPE_GID)
if (idmap->nsid == 0)
lxc_conf->root_nsgid_map = idmap;
idmap = NULL;
return 0;
......
......@@ -999,7 +999,14 @@ static int do_start(void *data)
* privilege over our namespace.
*/
if (!lxc_list_empty(&handler->conf->id_map)) {
ret = lxc_switch_uid_gid(0, 0);
uid_t nsuid = (handler->conf->root_nsuid_map != NULL)
? 0
: handler->conf->init_uid;
gid_t nsgid = (handler->conf->root_nsgid_map != NULL)
? 0
: handler->conf->init_gid;
ret = lxc_switch_uid_gid(nsuid, nsgid);
if (ret < 0)
goto out_warn_father;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment