Commit 93caf971 by Serge Hallyn Committed by GitHub

Merge pull request #1509 from brauner/2017-04-15/improve_lxc_id_map

idmap improvements
parents 1a35a746 91c3e281
...@@ -209,27 +209,56 @@ int lxc_caps_last_cap(void) ...@@ -209,27 +209,56 @@ int lxc_caps_last_cap(void)
return last_cap; return last_cap;
} }
bool lxc_cap_is_set(cap_value_t cap, cap_flag_t flag) static bool lxc_cap_is_set(cap_t caps, cap_value_t cap, cap_flag_t flag)
{ {
int ret; int ret;
cap_t caps;
cap_flag_value_t flagval; cap_flag_value_t flagval;
caps = cap_get_proc(); ret = cap_get_flag(caps, cap, flag, &flagval);
if (ret < 0) {
ERROR("Failed to perform cap_get_flag(): %s.", strerror(errno));
return false;
}
return flagval == CAP_SET;
}
bool lxc_file_cap_is_set(const char *path, cap_value_t cap, cap_flag_t flag)
{
bool cap_is_set;
cap_t caps;
caps = cap_get_file(path);
if (!caps) { if (!caps) {
ERROR("Failed to perform cap_get_proc(): %s.", strerror(errno)); /* This is undocumented in the manpage but the source code show
* that cap_get_file() may return NULL when successful for the
* case where it didn't detect any file capabilities. In this
* case errno will be set to ENODATA.
*/
if (errno != ENODATA)
ERROR("Failed to perform cap_get_file(): %s.\n", strerror(errno));
return false; return false;
} }
ret = cap_get_flag(caps, cap, flag, &flagval); cap_is_set = lxc_cap_is_set(caps, cap, flag);
if (ret < 0) {
ERROR("Failed to perform cap_get_flag(): %s.", strerror(errno));
cap_free(caps); cap_free(caps);
return cap_is_set;
}
bool lxc_proc_cap_is_set(cap_value_t cap, cap_flag_t flag)
{
bool cap_is_set;
cap_t caps;
caps = cap_get_proc();
if (!caps) {
ERROR("Failed to perform cap_get_proc(): %s.\n", strerror(errno));
return false; return false;
} }
cap_is_set = lxc_cap_is_set(caps, cap, flag);
cap_free(caps); cap_free(caps);
return flagval == CAP_SET; return cap_is_set;
} }
#endif #endif
...@@ -36,7 +36,8 @@ extern int lxc_caps_init(void); ...@@ -36,7 +36,8 @@ extern int lxc_caps_init(void);
extern int lxc_caps_last_cap(void); extern int lxc_caps_last_cap(void);
extern bool lxc_cap_is_set(cap_value_t cap, cap_flag_t flag); extern bool lxc_proc_cap_is_set(cap_value_t cap, cap_flag_t flag);
extern bool lxc_file_cap_is_set(const char *path, cap_value_t cap, cap_flag_t flag);
#else #else
static inline int lxc_caps_down(void) { static inline int lxc_caps_down(void) {
return 0; return 0;
...@@ -54,7 +55,11 @@ static inline int lxc_caps_last_cap(void) { ...@@ -54,7 +55,11 @@ static inline int lxc_caps_last_cap(void) {
typedef int cap_value_t; typedef int cap_value_t;
typedef int cap_flag_t; typedef int cap_flag_t;
static inline bool lxc_cap_is_set(cap_value_t cap, cap_flag_t flag) { static inline bool lxc_proc_cap_is_set(cap_value_t cap, cap_flag_t flag) {
return true;
}
static inline bool lxc_file_cap_is_set(const char *path, cap_value_t cap, cap_flag_t flag) {
return true; return true;
} }
#endif #endif
......
...@@ -919,29 +919,28 @@ static int mount_rootfs(const char *rootfs, const char *target, const char *opti ...@@ -919,29 +919,28 @@ static int mount_rootfs(const char *rootfs, const char *target, const char *opti
}; };
if (!realpath(rootfs, absrootfs)) { if (!realpath(rootfs, absrootfs)) {
SYSERROR("failed to get real path for '%s'", rootfs); SYSERROR("Failed to get real path for \"%s\".", rootfs);
return -1; return -1;
} }
if (access(absrootfs, F_OK)) { if (access(absrootfs, F_OK)) {
SYSERROR("'%s' is not accessible", absrootfs); SYSERROR("Th rootfs \"%s\" is not accessible.", absrootfs);
return -1; return -1;
} }
if (stat(absrootfs, &s)) { if (stat(absrootfs, &s)) {
SYSERROR("failed to stat '%s'", absrootfs); SYSERROR("Failed to stat the rootfs \"%s\".", absrootfs);
return -1; return -1;
} }
for (i = 0; i < sizeof(rtfs_type)/sizeof(rtfs_type[0]); i++) { for (i = 0; i < sizeof(rtfs_type)/sizeof(rtfs_type[0]); i++) {
if (!__S_ISTYPE(s.st_mode, rtfs_type[i].type)) if (!__S_ISTYPE(s.st_mode, rtfs_type[i].type))
continue; continue;
return rtfs_type[i].cb(absrootfs, target, options); return rtfs_type[i].cb(absrootfs, target, options);
} }
ERROR("unsupported rootfs type for '%s'", absrootfs); ERROR("Unsupported rootfs type for rootfs \"%s\".", absrootfs);
return -1; return -1;
} }
...@@ -1307,38 +1306,45 @@ static int fill_autodev(const struct lxc_rootfs *rootfs, bool mount_console) ...@@ -1307,38 +1306,45 @@ static int fill_autodev(const struct lxc_rootfs *rootfs, bool mount_console)
static int setup_rootfs(struct lxc_conf *conf) static int setup_rootfs(struct lxc_conf *conf)
{ {
const struct lxc_rootfs *rootfs = &conf->rootfs; struct bdev *bdev;
const struct lxc_rootfs *rootfs;
rootfs = &conf->rootfs;
if (!rootfs->path) { if (!rootfs->path) {
if (mount("", "/", NULL, MS_SLAVE|MS_REC, 0)) { if (mount("", "/", NULL, MS_SLAVE | MS_REC, 0)) {
SYSERROR("Failed to make / rslave"); SYSERROR("Failed to make / rslave.");
return -1; return -1;
} }
return 0; return 0;
} }
if (access(rootfs->mount, F_OK)) { if (access(rootfs->mount, F_OK)) {
SYSERROR("failed to access to '%s', check it is present", SYSERROR("Failed to access to \"%s\". Check it is present.",
rootfs->mount); rootfs->mount);
return -1; return -1;
} }
// First try mounting rootfs using a bdev /* First try mounting rootfs using a bdev. */
struct bdev *bdev = bdev_init(conf, rootfs->path, rootfs->mount, rootfs->options); bdev = bdev_init(conf, rootfs->path, rootfs->mount, rootfs->options);
if (bdev && bdev->ops->mount(bdev) == 0) { if (bdev && !bdev->ops->mount(bdev)) {
bdev_put(bdev); bdev_put(bdev);
DEBUG("mounted '%s' on '%s'", rootfs->path, rootfs->mount); DEBUG("Mounted rootfs \"%s\" onto \"%s\" with options \"%s\".",
rootfs->path, rootfs->mount,
rootfs->options ? rootfs->options : "(null)");
return 0; return 0;
} }
if (bdev) if (bdev)
bdev_put(bdev); bdev_put(bdev);
if (mount_rootfs(rootfs->path, rootfs->mount, rootfs->options)) { if (mount_rootfs(rootfs->path, rootfs->mount, rootfs->options)) {
ERROR("failed to mount rootfs"); ERROR("Failed to mount rootfs \"%s\" onto \"%s\" with options \"%s\".",
rootfs->path, rootfs->mount,
rootfs->options ? rootfs->options : "(null)");
return -1; return -1;
} }
DEBUG("mounted '%s' on '%s'", rootfs->path, rootfs->mount); DEBUG("Mounted rootfs \"%s\" onto \"%s\" with options \"%s\".",
rootfs->path, rootfs->mount,
rootfs->options ? rootfs->options : "(null)");
return 0; return 0;
} }
...@@ -3322,75 +3328,137 @@ static int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf, ...@@ -3322,75 +3328,137 @@ static int write_id_mapping(enum idtype idtype, pid_t pid, const char *buf,
return ret < 0 ? ret : closeret; return ret < 0 ? ret : closeret;
} }
/* Check whether a binary exist and has either CAP_SETUID, CAP_SETGID or both. */
static int idmaptool_on_path_and_privileged(const char *binary, cap_value_t cap)
{
char *path;
int ret;
struct stat st;
int fret = 0;
path = on_path(binary, NULL);
if (!path)
return -ENOENT;
ret = stat(path, &st);
if (ret < 0) {
fret = -errno;
goto cleanup;
}
/* Check if the binary is setuid. */
if (st.st_mode & S_ISUID) {
DEBUG("The binary \"%s\" does have the setuid bit set.", path);
fret = 1;
goto cleanup;
}
#if HAVE_LIBCAP
/* Check if it has the CAP_SETUID capability. */
if ((cap & CAP_SETUID) &&
lxc_file_cap_is_set(path, CAP_SETUID, CAP_EFFECTIVE) &&
lxc_file_cap_is_set(path, CAP_SETUID, CAP_PERMITTED)) {
DEBUG("The binary \"%s\" has CAP_SETUID in its CAP_EFFECTIVE "
"and CAP_PERMITTED sets.", path);
fret = 1;
goto cleanup;
}
/* Check if it has the CAP_SETGID capability. */
if ((cap & CAP_SETGID) &&
lxc_file_cap_is_set(path, CAP_SETGID, CAP_EFFECTIVE) &&
lxc_file_cap_is_set(path, CAP_SETGID, CAP_PERMITTED)) {
DEBUG("The binary \"%s\" has CAP_SETGID in its CAP_EFFECTIVE "
"and CAP_PERMITTED sets.", path);
fret = 1;
goto cleanup;
}
#endif
cleanup:
free(path);
return fret;
}
int lxc_map_ids(struct lxc_list *idmap, pid_t pid) int lxc_map_ids(struct lxc_list *idmap, pid_t pid)
{ {
struct lxc_list *iterator;
struct id_map *map; struct id_map *map;
int ret = 0, use_shadow = 0; struct lxc_list *iterator;
enum idtype type; enum idtype type;
char *buf = NULL, *pos, *cmdpath = NULL; char *pos;
int euid;
int ret = 0, use_shadow = 0;
int uidmap = 0, gidmap = 0;
char *buf = NULL;
/* euid = geteuid();
* If newuidmap exists, that is, if shadow is handing out subuid
/* If new{g,u}idmap exists, that is, if shadow is handing out subuid
* ranges, then insist that root also reserve ranges in subuid. This * ranges, then insist that root also reserve ranges in subuid. This
* will protected it by preventing another user from being handed the * will protected it by preventing another user from being handed the
* range by shadow. * range by shadow.
*/ */
cmdpath = on_path("newuidmap", NULL); uidmap = idmaptool_on_path_and_privileged("newuidmap", CAP_SETUID);
if (cmdpath) { gidmap = idmaptool_on_path_and_privileged("newgidmap", CAP_SETGID);
use_shadow = 1; if (uidmap > 0 && gidmap > 0) {
free(cmdpath); DEBUG("Functional newuidmap and newgidmap binary found.");
} use_shadow = true;
} else if (uidmap == -ENOENT && gidmap == -ENOENT && !euid) {
if (!use_shadow && geteuid()) { DEBUG("No newuidmap and newgidmap binary found. Trying to "
ERROR("Missing newuidmap/newgidmap"); "write directly with euid 0.");
use_shadow = false;
} else {
DEBUG("Either one or both of the newuidmap and newgidmap "
"binaries do not exist or are missing necessary "
"privilege.");
return -1; return -1;
} }
for(type = ID_TYPE_UID; type <= ID_TYPE_GID; type++) { for (type = ID_TYPE_UID; type <= ID_TYPE_GID; type++) {
int left, fill; int left, fill;
int had_entry = 0; bool had_entry = false;
if (!buf) { if (!buf) {
buf = pos = malloc(4096); buf = pos = malloc(LXC_IDMAPLEN);
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
} }
pos = buf; pos = buf;
if (use_shadow) if (use_shadow)
pos += sprintf(buf, "new%cidmap %d", pos += sprintf(buf, "new%cidmap %d", type == ID_TYPE_UID ? 'u' : 'g', pid);
type == ID_TYPE_UID ? 'u' : 'g',
pid);
lxc_list_for_each(iterator, idmap) { lxc_list_for_each(iterator, idmap) {
/* The kernel only takes <= 4k for writes to /proc/<nr>/[ug]id_map */ /* The kernel only takes <= 4k for writes to
* /proc/<nr>/[ug]id_map
*/
map = iterator->elem; map = iterator->elem;
if (map->idtype != type) if (map->idtype != type)
continue; continue;
had_entry = 1; had_entry = true;
left = 4096 - (pos - buf);
left = LXC_IDMAPLEN - (pos - buf);
fill = snprintf(pos, left, "%s%lu %lu %lu%s", fill = snprintf(pos, left, "%s%lu %lu %lu%s",
use_shadow ? " " : "", use_shadow ? " " : "", map->nsid,
map->nsid, map->hostid, map->range, map->hostid, map->range,
use_shadow ? "" : "\n"); use_shadow ? "" : "\n");
if (fill <= 0 || fill >= left) if (fill <= 0 || fill >= left)
SYSERROR("snprintf failed, too many mappings"); SYSERROR("Too many {g,u}id mappings defined.");
pos += fill; pos += fill;
} }
if (!had_entry) if (!had_entry)
continue; continue;
if (!use_shadow) { if (!use_shadow) {
ret = write_id_mapping(type, pid, buf, pos-buf); ret = write_id_mapping(type, pid, buf, pos - buf);
} else { } else {
left = 4096 - (pos - buf); left = LXC_IDMAPLEN - (pos - buf);
fill = snprintf(pos, left, "\n"); fill = snprintf(pos, left, "\n");
if (fill <= 0 || fill >= left) if (fill <= 0 || fill >= left)
SYSERROR("snprintf failed, too many mappings"); SYSERROR("Too many {g,u}id mappings defined.");
pos += fill; pos += fill;
ret = system(buf); ret = system(buf);
} }
if (ret) if (ret)
break; break;
} }
......
...@@ -899,7 +899,7 @@ static int do_start(void *data) ...@@ -899,7 +899,7 @@ static int do_start(void *data)
* have necessary privilege. * have necessary privilege.
*/ */
#if HAVE_LIBCAP #if HAVE_LIBCAP
have_cap_setgid = lxc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE); have_cap_setgid = lxc_proc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE);
#else #else
have_cap_setgid = false; have_cap_setgid = false;
#endif #endif
......
...@@ -1199,7 +1199,7 @@ bool detect_ramfs_rootfs(void) ...@@ -1199,7 +1199,7 @@ bool detect_ramfs_rootfs(void)
return false; return false;
} }
char *on_path(char *cmd, const char *rootfs) { char *on_path(const char *cmd, const char *rootfs) {
char *path = NULL; char *path = NULL;
char *entry = NULL; char *entry = NULL;
char *saveptr = NULL; char *saveptr = NULL;
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
/* Maximum number for 64 bit integer is a string with 21 digits: 2^64 - 1 = 21 */ /* Maximum number for 64 bit integer is a string with 21 digits: 2^64 - 1 = 21 */
#define LXC_NUMSTRLEN64 21 #define LXC_NUMSTRLEN64 21
#define LXC_LINELEN 4096 #define LXC_LINELEN 4096
#define LXC_IDMAPLEN 4096
/* returns 1 on success, 0 if there were any failures */ /* returns 1 on success, 0 if there were any failures */
extern int lxc_rmdir_onedev(char *path, const char *exclude); extern int lxc_rmdir_onedev(char *path, const char *exclude);
...@@ -301,7 +302,7 @@ uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval); ...@@ -301,7 +302,7 @@ uint64_t fnv_64a_buf(void *buf, size_t len, uint64_t hval);
int detect_shared_rootfs(void); int detect_shared_rootfs(void);
bool detect_ramfs_rootfs(void); bool detect_ramfs_rootfs(void);
char *on_path(char *cmd, const char *rootfs); char *on_path(const char *cmd, const char *rootfs);
bool file_exists(const char *f); bool file_exists(const char *f);
bool cgns_supported(void); bool cgns_supported(void);
char *choose_init(const char *rootfs); char *choose_init(const char *rootfs);
......
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