Commit ff3381e1 by Stéphane Graber Committed by GitHub

Merge pull request #1609 from brauner/2017-06-01/unit_test_idmap_parser

unit test idmap parser + userns_exec_1() rework
parents 60025244 28a2d9e7
......@@ -20,6 +20,8 @@ noinst_HEADERS = \
cgroups/cgroup.h \
caps.h \
conf.h \
confile.h \
confile_utils.h \
console.h \
error.h \
initutils.h \
......@@ -101,6 +103,7 @@ liblxc_la_SOURCES = \
namespace.h namespace.c \
conf.c conf.h \
confile.c confile.h \
confile_utils.c confile_utils.h \
list.h \
state.c state.h \
log.c log.h \
......
......@@ -3618,7 +3618,7 @@ int mapped_hostid(unsigned id, struct lxc_conf *conf, enum idtype idtype)
return -1;
}
int find_unmapped_nsuid(struct lxc_conf *conf, enum idtype idtype)
int find_unmapped_nsid(struct lxc_conf *conf, enum idtype idtype)
{
struct lxc_list *it;
struct id_map *map;
......@@ -4684,7 +4684,7 @@ static int run_userns_fn(void *data)
return d->fn(d->arg);
}
static struct id_map *mapped_hostid_entry(unsigned id, struct lxc_conf *conf,
static struct id_map *mapped_hostid_entry(struct lxc_conf *conf, unsigned id,
enum idtype idtype)
{
struct lxc_list *it;
......@@ -4717,83 +4717,33 @@ static struct id_map *mapped_hostid_entry(unsigned id, struct lxc_conf *conf,
* 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 lxc_list *idmap_add_id(struct lxc_conf *conf, uid_t uid,
gid_t gid)
static struct id_map *idmap_add(struct lxc_conf *conf, uid_t id, enum idtype type)
{
int hostuid_mapped, hostgid_mapped;
struct id_map *hostuid_idmap, *hostgid_idmap;
int hostid_mapped;
struct id_map *entry = NULL;
struct lxc_list *new = NULL;
struct lxc_list *tmp = NULL;
hostuid_idmap = mapped_hostid_entry(uid, conf, ID_TYPE_UID);
hostgid_idmap = mapped_hostid_entry(gid, conf, ID_TYPE_GID);
/* Allocate new {g,u}id map list. */
new = malloc(sizeof(*new));
if (!new)
goto on_error;
lxc_list_init(new);
tmp = malloc(sizeof(*tmp));
if (!tmp)
goto on_error;
entry = hostuid_idmap;
if (!hostuid_idmap) {
hostuid_mapped = find_unmapped_nsuid(conf, ID_TYPE_UID);
if (hostuid_mapped < 0)
goto on_error;
entry = malloc(sizeof(*entry));
if (!entry)
goto on_error;
/* Reuse existing mapping. */
entry = mapped_hostid_entry(conf, id, type);
if (entry)
return entry;
tmp->elem = entry;
entry->idtype = ID_TYPE_UID;
entry->nsid = hostuid_mapped;
entry->hostid = (unsigned long)uid;
entry->range = 1;
DEBUG("adding uid mapping: nsid %lu hostid %lu range %lu",
entry->nsid, entry->hostid, entry->range);
/* Find new mapping. */
hostid_mapped = find_unmapped_nsid(conf, type);
if (hostid_mapped < 0) {
DEBUG("failed to find free mapping for id %d", id);
return NULL;
}
lxc_list_add_tail(new, tmp);
entry = NULL;
tmp = NULL;
tmp = malloc(sizeof(*tmp));
if (!tmp)
goto on_error;
entry = hostgid_idmap;
if (!hostgid_idmap) {
hostgid_mapped = find_unmapped_nsuid(conf, ID_TYPE_GID);
if (hostgid_mapped < 0)
goto on_error;
entry = malloc(sizeof(*entry));
if (!entry)
goto on_error;
tmp->elem = entry;
entry->idtype = ID_TYPE_GID;
entry->nsid = hostgid_mapped;
entry->hostid = (unsigned long)gid;
entry->range = 1;
DEBUG("adding gid mapping: nsid %lu hostid %lu range %lu",
entry->nsid, entry->hostid, entry->range);
}
lxc_list_add_tail(new, tmp);
entry = malloc(sizeof(*entry));
if (!entry)
return NULL;
return new;
entry->idtype = type;
entry->nsid = hostid_mapped;
entry->hostid = (unsigned long)id;
entry->range = 1;
on_error:
ERROR("failed to allocate memory for new id map");
if (new)
lxc_free_idmap(new);
free(new);
free(tmp);
if (entry)
free(entry);
return NULL;
return entry;
}
/* Run a function in a new user namespace.
......@@ -4818,7 +4768,8 @@ int userns_exec_1(struct lxc_conf *conf, int (*fn)(void *), void *data)
char c = '1';
int ret = -1;
struct lxc_list *idmap = NULL, *tmplist = NULL;
struct id_map *container_root_uid = NULL, *container_root_gid = NULL;
struct id_map *container_root_uid = NULL, *container_root_gid = NULL,
*host_uid_map = NULL, *host_gid_map = NULL;
ret = pipe(p);
if (ret < 0) {
......@@ -4879,31 +4830,74 @@ int userns_exec_1(struct lxc_conf *conf, int (*fn)(void *), void *data)
/* Check whether the {g,u}id of the user has a mapping. */
euid = geteuid();
egid = getegid();
idmap = idmap_add_id(conf, euid, egid);
if (!idmap) {
ERROR("failed to prepare id mapping for uid %d and gid %d",
euid, egid);
if (euid == container_root_uid->hostid)
host_uid_map = container_root_uid;
else
host_uid_map = idmap_add(conf, euid, ID_TYPE_UID);
if (egid == container_root_gid->hostid)
host_gid_map = container_root_gid;
else
host_gid_map = idmap_add(conf, egid, ID_TYPE_GID);
if (!host_uid_map) {
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);
goto on_error;
}
/* Allocate new {g,u}id map list. */
idmap = malloc(sizeof(*idmap));
if (!idmap)
goto on_error;
lxc_list_init(idmap);
/* Add container root to the map. */
tmplist = malloc(sizeof(*tmplist));
if (!tmplist)
goto on_error;
lxc_list_add_elem(tmplist, container_root_uid);
lxc_list_add_tail(idmap, tmplist);
/* idmap will now keep track of that memory. */
container_root_uid = NULL;
if (host_uid_map != container_root_uid) {
/* idmap will now keep track of that memory. */
container_root_uid = NULL;
/* Add container root to the map. */
tmplist = malloc(sizeof(*tmplist));
if (!tmplist)
goto on_error;
lxc_list_add_elem(tmplist, host_uid_map);
lxc_list_add_tail(idmap, tmplist);
/* idmap will now keep track of that memory. */
host_uid_map = NULL;
}
tmplist = malloc(sizeof(*tmplist));
if (!tmplist)
goto on_error;
lxc_list_add_elem(tmplist, container_root_gid);
lxc_list_add_tail(idmap, tmplist);
/* idmap will now keep track of that memory. */
container_root_gid = NULL;
if (lxc_log_get_level() == LXC_LOG_PRIORITY_TRACE) {
if (host_gid_map != container_root_gid) {
/* idmap will now keep track of that memory. */
container_root_gid = NULL;
tmplist = malloc(sizeof(*tmplist));
if (!tmplist)
goto on_error;
lxc_list_add_elem(tmplist, host_gid_map);
lxc_list_add_tail(idmap, tmplist);
/* idmap will now keep track of that memory. */
host_gid_map = NULL;
}
if (lxc_log_get_level() == LXC_LOG_PRIORITY_TRACE ||
conf->loglevel == LXC_LOG_PRIORITY_TRACE) {
lxc_list_for_each(it, idmap) {
map = it->elem;
TRACE("establishing %cid mapping for \"%d\" in new "
......@@ -4936,6 +4930,8 @@ on_error:
lxc_free_idmap(idmap);
free(container_root_uid);
free(container_root_gid);
free(host_uid_map);
free(host_gid_map);
if (p[0] != -1)
close(p[0]);
......
......@@ -469,7 +469,7 @@ extern int setup_resource_limits(struct lxc_list *limits, pid_t pid);
extern void lxc_restore_phys_nics_to_netns(int netnsfd, struct lxc_conf *conf);
extern int find_unmapped_nsuid(struct lxc_conf *conf, enum idtype idtype);
extern int find_unmapped_nsid(struct lxc_conf *conf, enum idtype idtype);
extern int mapped_hostid(unsigned id, struct lxc_conf *conf, enum idtype idtype);
extern int chown_mapped_root(char *path, struct lxc_conf *conf);
extern int ttys_shift_ids(struct lxc_conf *c);
......
......@@ -45,6 +45,7 @@
#include "parse.h"
#include "config.h"
#include "confile.h"
#include "confile_utils.h"
#include "utils.h"
#include "log.h"
#include "conf.h"
......@@ -1984,8 +1985,7 @@ static int set_config_idmaps(const char *key, const char *value,
{
unsigned long hostid, nsid, range;
char type;
char *window, *slide;
char *dup = NULL;
int ret;
struct lxc_list *idmaplist = NULL;
struct id_map *idmap = NULL;
......@@ -2001,109 +2001,10 @@ static int set_config_idmaps(const char *key, const char *value,
goto on_error;
memset(idmap, 0, sizeof(*idmap));
/* Duplicate string. */
dup = strdup(value);
if (!dup)
ret = parse_idmaps(value, &type, &nsid, &hostid, &range);
if (ret < 0)
goto on_error;
/* A prototypical idmap entry would be: "u 1000 1000000 65536" */
/* align */
slide = window = dup;
/* skip whitespace */
slide += strspn(slide, " \t\r");
if (slide != window && *slide == '\0')
goto on_error;
/* Validate type. */
if (*slide != 'u' && *slide != 'g')
goto on_error;
/* Assign type. */
type = *slide;
/* move beyond type */
slide++;
/* align */
window = slide;
/* Validate that only whitespace follows. */
slide += strspn(slide, " \t\r");
/* There must be whitespace. */
if (slide == window)
goto on_error;
/* Mark beginning of nsuid. */
window = slide;
/* Validate that non-whitespace follows. */
slide += strcspn(slide, " \t\r");
/* There must be non-whitespace. */
if (slide == window || *slide == '\0')
goto on_error;
/* Mark end of nsuid. */
*slide = '\0';
/* Parse nsuid. */
if (lxc_safe_ulong(window, &nsid) < 0)
goto on_error;
/* Move beyond \0. */
slide++;
/* align */
window = slide;
/* Validate that only whitespace follows. */
slide += strspn(slide, " \t\r");
/* If there was only one whitespace then we whiped it with our \0 above.
* So only ensure that we're not at the end of the string.
*/
if (*slide == '\0')
goto on_error;
/* Mark beginning of hostid. */
window = slide;
/* Validate that non-whitespace follows. */
slide += strcspn(slide, " \t\r");
/* There must be non-whitespace. */
if (slide == window || *slide == '\0')
goto on_error;
/* Mark end of nsuid. */
*slide = '\0';
/* Parse hostid. */
if (lxc_safe_ulong(window, &hostid) < 0)
goto on_error;
/* Move beyond \0. */
slide++;
/* align */
window = slide;
/* Validate that only whitespace follows. */
slide += strspn(slide, " \t\r");
/* If there was only one whitespace then we whiped it with our \0 above.
* So only ensure that we're not at the end of the string.
*/
if (*slide == '\0')
goto on_error;
/* Mark beginning of range. */
window = slide;
/* Validate that non-whitespace follows. */
slide += strcspn(slide, " \t\r");
/* There must be non-whitespace. */
if (slide == window)
goto on_error;
/* The range is the last valid entry we expect. So make sure that there
* is not trailing garbage and if there is, error out.
*/
if (*(slide + strspn(slide, " \t\r\n")) != '\0')
goto on_error;
/* Mark end of range. */
*slide = '\0';
/* Parse range. */
if (lxc_safe_ulong(window, &range) < 0)
goto on_error;
/* Yay, we survived. */
INFO("read uid map: type %c nsid %lu hostid %lu range %lu", type, nsid, hostid, range);
if (type == 'u')
idmap->idtype = ID_TYPE_UID;
......@@ -2124,7 +2025,6 @@ static int set_config_idmaps(const char *key, const char *value,
on_error:
free(idmaplist);
free(idmap);
free(dup);
return -1;
}
......
/* liblxcapi
*
* Copyright © 2017 Christian Brauner <christian.brauner@ubuntu.com>.
* Copyright © 2017 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <string.h>
#include "utils.h"
int parse_idmaps(const char *idmap, char *type, unsigned long *nsid,
unsigned long *hostid, unsigned long *range)
{
int ret = -1;
unsigned long tmp_hostid, tmp_nsid, tmp_range;
char tmp_type;
char *window, *slide;
char *dup = NULL;
/* Duplicate string. */
dup = strdup(idmap);
if (!dup)
goto on_error;
/* A prototypical idmap entry would be: "u 1000 1000000 65536" */
/* align */
slide = window = dup;
/* skip whitespace */
slide += strspn(slide, " \t\r");
if (slide != window && *slide == '\0')
goto on_error;
/* Validate type. */
if (*slide != 'u' && *slide != 'g')
goto on_error;
/* Assign type. */
tmp_type = *slide;
/* move beyond type */
slide++;
/* align */
window = slide;
/* Validate that only whitespace follows. */
slide += strspn(slide, " \t\r");
/* There must be whitespace. */
if (slide == window)
goto on_error;
/* Mark beginning of nsuid. */
window = slide;
/* Validate that non-whitespace follows. */
slide += strcspn(slide, " \t\r");
/* There must be non-whitespace. */
if (slide == window || *slide == '\0')
goto on_error;
/* Mark end of nsuid. */
*slide = '\0';
/* Parse nsuid. */
if (lxc_safe_ulong(window, &tmp_nsid) < 0)
goto on_error;
/* Move beyond \0. */
slide++;
/* align */
window = slide;
/* Validate that only whitespace follows. */
slide += strspn(slide, " \t\r");
/* If there was only one whitespace then we whiped it with our \0 above.
* So only ensure that we're not at the end of the string.
*/
if (*slide == '\0')
goto on_error;
/* Mark beginning of hostid. */
window = slide;
/* Validate that non-whitespace follows. */
slide += strcspn(slide, " \t\r");
/* There must be non-whitespace. */
if (slide == window || *slide == '\0')
goto on_error;
/* Mark end of nsuid. */
*slide = '\0';
/* Parse hostid. */
if (lxc_safe_ulong(window, &tmp_hostid) < 0)
goto on_error;
/* Move beyond \0. */
slide++;
/* align */
window = slide;
/* Validate that only whitespace follows. */
slide += strspn(slide, " \t\r");
/* If there was only one whitespace then we whiped it with our \0 above.
* So only ensure that we're not at the end of the string.
*/
if (*slide == '\0')
goto on_error;
/* Mark beginning of range. */
window = slide;
/* Validate that non-whitespace follows. */
slide += strcspn(slide, " \t\r");
/* There must be non-whitespace. */
if (slide == window)
goto on_error;
/* The range is the last valid entry we expect. So make sure that there
* is not trailing garbage and if there is, error out.
*/
if (*(slide + strspn(slide, " \t\r\n")) != '\0')
goto on_error;
/* Mark end of range. */
*slide = '\0';
/* Parse range. */
if (lxc_safe_ulong(window, &tmp_range) < 0)
goto on_error;
*type = tmp_type;
*nsid = tmp_nsid;
*hostid = tmp_hostid;
*range = tmp_range;
/* Yay, we survived. */
ret = 0;
on_error:
free(dup);
return ret;
}
/* liblxcapi
*
* Copyright © 2017 Christian Brauner <christian.brauner@ubuntu.com>.
* Copyright © 2017 Canonical Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef __LXC_CONFILE_UTILS_H
#define __LXC_CONFILE_UTILS_H
extern int parse_idmaps(const char *idmap, char *type, unsigned long *nsid,
unsigned long *hostid, unsigned long *range);
#endif /* __LXC_CONFILE_UTILS_H */
......@@ -1220,7 +1220,7 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool need_
if (!n2)
exit(1);
if (hostid_mapped < 0) {
hostid_mapped = find_unmapped_nsuid(conf, ID_TYPE_UID);
hostid_mapped = find_unmapped_nsid(conf, ID_TYPE_UID);
n2[n2args++] = "-m";
if (hostid_mapped < 0) {
ERROR("Could not find free uid to map");
......@@ -1244,7 +1244,7 @@ static bool create_run_template(struct lxc_container *c, char *tpath, bool need_
if (!n2)
exit(1);
if (hostgid_mapped < 0) {
hostgid_mapped = find_unmapped_nsuid(conf, ID_TYPE_GID);
hostgid_mapped = find_unmapped_nsid(conf, ID_TYPE_GID);
n2[n2args++] = "-m";
if (hostgid_mapped < 0) {
ERROR("Could not find free uid to map");
......
......@@ -28,6 +28,7 @@
#include <errno.h>
#include <string.h>
#include "confile_utils.h"
#include "lxc/state.h"
#include "lxctest.h"
......@@ -103,6 +104,57 @@ static int set_get_compare_clear_save_load(struct lxc_container *c,
return 0;
}
int test_idmap_parser(void)
{
size_t i;
struct idmap_check {
bool is_valid;
const char *idmap;
};
static struct idmap_check idmaps[] = {
/* valid idmaps */
{ true, "u 0 0 1" },
{ true, "g 0 0 1" },
{ true, "u 1 100001 999999999" },
{ true, "g 1 100001 999999999" },
{ true, "u 0 0 0" },
{ true, "g 0 0 0" },
{ true, "u 1000 165536 65536" },
{ true, "g 999 999 1" },
{ true, "u 0 5000 100000" },
{ true, "g 577 789 5" },
{ true, "u 65536 65536 1 " },
/* invalid idmaps */
{ false, "1u 0 0 0" },
{ false, "1g 0 0 0a" },
{ false, "1 u 0 0 0" },
{ false, "1g 0 0 0 1" },
{ false, "1u a0 b0 c0 d1" },
{ false, "1g 0 b0 0 d1" },
{ false, "1u a0 0 c0 1" },
{ false, "g -1 0 -10" },
{ false, "a 1 0 10" },
{ false, "u 1 1 0 10" },
{ false, "g 1 0 10 z " },
};
for (i = 0; i < sizeof(idmaps) / sizeof(struct idmap_check); i++) {
unsigned long hostid, nsid, range;
char type;
int ret;
ret = parse_idmaps(idmaps[i].idmap, &type, &nsid, &hostid,
&range);
if ((ret < 0 && idmaps[i].is_valid) ||
(ret == 0 && !idmaps[i].is_valid)) {
lxc_error("failed to parse idmap \"%s\"\n",
idmaps[i].idmap);
return -1;
}
}
return 0;
}
int main(int argc, char *argv[])
{
struct lxc_container *c;
......@@ -489,6 +541,11 @@ int main(int argc, char *argv[])
goto non_test_error;
}
if (test_idmap_parser() < 0) {
lxc_error("%s\n", "failed to test parser for \"lxc.id_map\"");
goto non_test_error;
}
ret = EXIT_SUCCESS;
non_test_error:
c->destroy(c);
......
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