Unverified Commit b5daeddc by Christian Brauner Committed by GitHub

Merge pull request #3657 from brauner/2021-02-05/init_groups

conf: implement lxc.init.groups
parents d71e4764 7fe8120e
......@@ -277,11 +277,6 @@ static int userns_setup_ids(struct attach_context *ctx,
if (ctx->setup_ns_gid == LXC_INVALID_UID)
ctx->setup_ns_gid = init_ns_gid;
/*
* TODO: we should also parse supplementary groups and use
* setgroups() to set them.
*/
return 0;
}
......@@ -360,11 +355,6 @@ static int parse_init_status(struct attach_context *ctx, lxc_attach_options_t *o
return log_error_errno(ret, errno, "Failed to get setup ids");
userns_target_ids(ctx, options);
/*
* TODO: we should also parse supplementary groups and use
* setgroups() to set them.
*/
return 0;
}
......@@ -1214,8 +1204,14 @@ __noreturn static void do_attach(struct attach_payload *ap)
goto on_error;
}
if (!lxc_setgroups(0, NULL) && errno != EPERM)
goto on_error;
if ((options->attach_flags & LXC_ATTACH_SETGROUPS) &&
options->groups.size > 0) {
if (!lxc_setgroups(options->groups.list, options->groups.size))
goto on_error;
} else {
if (!lxc_drop_groups() && errno != EPERM)
goto on_error;
}
if (options->namespaces & CLONE_NEWUSER)
if (!lxc_switch_uid_gid(ctx->setup_ns_uid, ctx->setup_ns_gid))
......
......@@ -31,6 +31,7 @@ enum {
LXC_ATTACH_NO_NEW_PRIVS = 0x00040000, /*!< PR_SET_NO_NEW_PRIVS */
LXC_ATTACH_TERMINAL = 0x00080000, /*!< Allocate new terminal for attached process. */
LXC_ATTACH_LSM_LABEL = 0x00100000, /*!< Set custom LSM label specified in @lsm_label. */
LXC_ATTACH_SETGROUPS = 0x00200000, /*!< Set additional group ids specified in @groups. */
/* We have 16 bits for things that are on by default and 16 bits that
* are off by default, that should be sufficient to keep binary
......@@ -52,6 +53,11 @@ enum {
*/
typedef int (*lxc_attach_exec_t)(void* payload);
typedef struct lxc_groups_t {
size_t size;
gid_t *list;
} lxc_groups_t;
/*!
* LXC attach options for \ref lxc_container \c attach().
*/
......@@ -72,7 +78,7 @@ typedef struct lxc_attach_options_t {
* If the current directory does not exist in the container, the root
* directory will be used instead because of kernel defaults.
*/
char* initial_cwd;
char *initial_cwd;
/*! The user-id to run as.
*
......@@ -92,12 +98,12 @@ typedef struct lxc_attach_options_t {
lxc_attach_env_policy_t env_policy;
/*! Extra environment variables to set in the container environment */
char** extra_env_vars;
char **extra_env_vars;
/*! Names of environment variables in existing environment to retain
* in container environment.
*/
char** extra_keep_env;
char **extra_keep_env;
/**@{*/
/*! File descriptors for stdin, stdout and stderr,
......@@ -117,33 +123,40 @@ typedef struct lxc_attach_options_t {
/*! lsm label to set. */
char *lsm_label;
/*! The additional group GIDs to run with.
*
* If unset all additional groups are dropped.
*/
lxc_groups_t groups;
} lxc_attach_options_t;
/*! Default attach options to use */
#define LXC_ATTACH_OPTIONS_DEFAULT \
{ \
/* .attach_flags = */ LXC_ATTACH_DEFAULT, \
/* .namespaces = */ -1, \
/* .personality = */ 0xffffffff, \
/* .initial_cwd = */ NULL, \
/* .uid = */ (uid_t)-1, \
/* .gid = */ (gid_t)-1, \
/* .env_policy = */ LXC_ATTACH_KEEP_ENV, \
/* .extra_env_vars = */ NULL, \
/* .extra_keep_env = */ NULL, \
/* .stdin_fd = */ 0, \
/* .stdout_fd = */ 1, \
/* .stderr_fd = */ 2, \
/* .log_fd = */ -EBADF, \
/* .lsm_label = */ NULL, \
#define LXC_ATTACH_OPTIONS_DEFAULT \
{ \
.attach_flags = LXC_ATTACH_DEFAULT, \
.namespaces = -1, \
.personality = 0xffffffff, \
.initial_cwd = NULL, \
.uid = (uid_t)-1, \
.gid = (gid_t)-1, \
.env_policy = LXC_ATTACH_KEEP_ENV, \
.extra_env_vars = NULL, \
.extra_keep_env = NULL, \
.stdin_fd = 0, \
.stdout_fd = 1, \
.stderr_fd = 2, \
.log_fd = -EBADF, \
.lsm_label = NULL, \
.groups = {}, \
}
/*!
* Representation of a command to run in a container.
*/
typedef struct lxc_attach_command_t {
char* program; /*!< The program to run (passed to execvp) */
char** argv; /*!< The argv pointer of that program, including the program itself in argv[0] */
char *program; /*!< The program to run (passed to execvp) */
char **argv; /*!< The argv pointer of that program, including the program itself in argv[0] */
} lxc_attach_command_t;
/*!
......
......@@ -965,7 +965,7 @@ static int cgroup_tree_remove_wrapper(void *data)
gid_t nsgid = (arg->conf->root_nsgid_map != NULL) ? 0 : arg->conf->init_gid;
int ret;
if (!lxc_setgroups(0, NULL) && errno != EPERM)
if (!lxc_drop_groups() && errno != EPERM)
return log_error_errno(-1, errno, "Failed to setgroups(0, NULL)");
ret = setresgid(nsgid, nsgid, nsgid);
......@@ -1574,7 +1574,7 @@ static int chown_cgroup_wrapper(void *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 (!lxc_setgroups(0, NULL) && errno != EPERM)
if (!lxc_drop_groups() && errno != EPERM)
return log_error_errno(-1, errno, "Failed to setgroups(0, NULL)");
ret = setresgid(nsgid, nsgid, nsgid);
......
......@@ -88,7 +88,7 @@ static int do_child(void *vargv)
int ret;
char **argv = (char **)vargv;
if (!lxc_setgroups(0, NULL) && errno != EPERM)
if (!lxc_drop_groups() && errno != EPERM)
return -1;
/* Assume we want to become root */
......
......@@ -2671,6 +2671,7 @@ struct lxc_conf *lxc_conf_init(void)
* default to running as UID/GID 0 when using lxc-execute */
new->init_uid = 0;
new->init_gid = 0;
memset(&new->init_groups, 0, sizeof(lxc_groups_t));
memset(&new->cgroup_meta, 0, sizeof(struct lxc_cgroup));
memset(&new->ns_share, 0, sizeof(char *) * LXC_NS_MAX);
memset(&new->timens, 0, sizeof(struct timens_offsets));
......@@ -3919,6 +3920,7 @@ void lxc_conf_free(struct lxc_conf *conf)
free(conf->rcfile);
free(conf->execute_cmd);
free(conf->init_cmd);
free(conf->init_groups.list);
free(conf->init_cwd);
free(conf->unexpanded_config);
free(conf->syslog);
......@@ -4282,7 +4284,7 @@ int userns_exec_minimal(const struct lxc_conf *conf,
close_prot_errno_disarm(sock_fds[0]);
if (!lxc_setgroups(0, NULL) && errno != EPERM)
if (!lxc_drop_groups() && errno != EPERM)
_exit(EXIT_FAILURE);
ret = setresgid(resgid, resgid, resgid);
......@@ -4700,7 +4702,7 @@ int userns_exec_mapped_root(const char *path, int path_fd,
if (!lxc_switch_uid_gid(0, 0))
_exit(EXIT_FAILURE);
if (!lxc_setgroups(0, NULL))
if (!lxc_drop_groups())
_exit(EXIT_FAILURE);
ret = fchown(target_fd, 0, st.st_gid);
......
......@@ -15,6 +15,7 @@
#include <sys/types.h>
#include <sys/vfs.h>
#include "attach_options.h"
#include "caps.h"
#include "compiler.h"
#include "config.h"
......@@ -413,10 +414,12 @@ struct lxc_conf {
/* init command */
char *init_cmd;
/* if running in a new user namespace, the UID/GID that init and COMMAND
* should run under when using lxc-execute */
/* The uid to use for the container. */
uid_t init_uid;
/* The gid to use for the container. */
gid_t init_gid;
/* The groups to use for the container. */
lxc_groups_t init_groups;
/* indicator if the container will be destroyed on shutdown */
unsigned int ephemeral;
......
......@@ -94,6 +94,7 @@ lxc_config_define(init_cmd);
lxc_config_define(init_cwd);
lxc_config_define(init_gid);
lxc_config_define(init_uid);
lxc_config_define(init_groups);
lxc_config_define(keyring_session);
lxc_config_define(log_file);
lxc_config_define(log_level);
......@@ -211,6 +212,7 @@ static struct lxc_config_t config_jump_table[] = {
{ "lxc.include", set_config_includefiles, get_config_includefiles, clr_config_includefiles, },
{ "lxc.init.cmd", set_config_init_cmd, get_config_init_cmd, clr_config_init_cmd, },
{ "lxc.init.gid", set_config_init_gid, get_config_init_gid, clr_config_init_gid, },
{ "lxc.init.groups", set_config_init_groups, get_config_init_groups, clr_config_init_groups, },
{ "lxc.init.uid", set_config_init_uid, get_config_init_uid, clr_config_init_uid, },
{ "lxc.init.cwd", set_config_init_cwd, get_config_init_cwd, clr_config_init_cwd, },
{ "lxc.keyring.session", set_config_keyring_session, get_config_keyring_session, clr_config_keyring_session },
......@@ -1178,6 +1180,64 @@ static int set_config_init_gid(const char *key, const char *value,
return 0;
}
static int set_config_init_groups(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
__do_free char *value_dup = NULL;
gid_t *init_groups = NULL;
size_t num_groups = 0;
size_t idx;
char *token;
if (lxc_config_value_empty(value))
return clr_config_init_groups(key, lxc_conf, NULL);
value_dup = strdup(value);
if (!value_dup)
return -ENOMEM;
lxc_iterate_parts(token, value_dup, ",")
num_groups++;
if (num_groups == INT_MAX)
return log_error_errno(-ERANGE, ERANGE, "Excessive number of supplementary groups specified");
/* This means the string wasn't empty and all we found was garbage. */
if (num_groups == 0)
return log_error_errno(-EINVAL, EINVAL, "No valid groups specified %s", value);
idx = lxc_conf->init_groups.size;
init_groups = realloc(lxc_conf->init_groups.list, sizeof(gid_t) * (idx + num_groups));
if (!init_groups)
return ret_errno(ENOMEM);
/*
* Once the realloc() succeeded we need to hand control of the memory
* back to the config otherwise we risk a double-free when
* lxc_conf_free() is called.
*/
lxc_conf->init_groups.list = init_groups;
/* Restore duplicated value so we can call lxc_iterate_parts() again. */
strcpy(value_dup, value);
lxc_iterate_parts(token, value_dup, ",") {
int ret;
gid_t group;
ret = lxc_safe_uint(token, &group);
if (ret)
return log_error_errno(ret, -ret, "Failed to parse group %s", token);
init_groups[idx++] = group;
}
lxc_conf->init_groups.size += num_groups;
return 0;
}
static int set_config_hooks(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
......@@ -4278,6 +4338,26 @@ static int get_config_init_gid(const char *key, char *retv, int inlen,
return lxc_get_conf_int(c, retv, inlen, c->init_gid);
}
static int get_config_init_groups(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data)
{
int fulllen = 0, len;
if (!retv)
inlen = 0;
else
memset(retv, 0, inlen);
if (c->init_groups.size == 0)
return 0;
for (int i = 0; i < c->init_groups.size; i++)
strprint(retv, inlen, "%s%d", (i > 0) ? "," : "",
c->init_groups.list[i]);
return fulllen;
}
static int get_config_ephemeral(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data)
{
......@@ -4964,6 +5044,14 @@ static inline int clr_config_init_gid(const char *key, struct lxc_conf *c,
return 0;
}
static inline int clr_config_init_groups(const char *key, struct lxc_conf *c,
void *data)
{
c->init_groups.size = 0;
free_disarm(c->init_groups.list);
return 0;
}
static inline int clr_config_ephemeral(const char *key, struct lxc_conf *c,
void *data)
{
......
......@@ -3660,7 +3660,7 @@ static int clone_update_rootfs(struct clone_update_data *data)
/* update hostname in rootfs */
/* we're going to mount, so run in a clean namespace to simplify cleanup */
(void)lxc_setgroups(0, NULL);
(void)lxc_drop_groups();
if (setgid(0) < 0) {
ERROR("Failed to setgid to 0");
......
......@@ -1111,7 +1111,7 @@ static int do_start(void *data)
/* Drop groups only after we switched to a valid gid in the new
* user namespace.
*/
if (!lxc_setgroups(0, NULL) &&
if (!lxc_drop_groups() &&
(handler->am_root || errno != EPERM))
goto out_warn_father;
......@@ -1406,12 +1406,21 @@ static int do_start(void *data)
* we switched to root in the new user namespace further above. Only
* drop groups if we can, so ensure that we have necessary privilege.
*/
if (lxc_list_empty(&handler->conf->id_map))
if (lxc_list_empty(&handler->conf->id_map)) {
#if HAVE_LIBCAP
if (lxc_proc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE))
#endif
if (!lxc_setgroups(0, NULL))
goto out_warn_father;
{
if (handler->conf->init_groups.size > 0) {
if (!lxc_setgroups(handler->conf->init_groups.list,
handler->conf->init_groups.size))
goto out_warn_father;
} else {
if (!lxc_drop_groups())
goto out_warn_father;
}
}
}
if (!lxc_switch_uid_gid(new_uid, new_gid))
goto out_warn_father;
......
......@@ -374,7 +374,7 @@ int btrfs_snapshot_wrapper(void *data)
const char *src;
struct rsync_data_char *arg = data;
(void)lxc_setgroups(0, NULL);
(void)lxc_drop_groups();
if (setgid(0) < 0) {
ERROR("Failed to setgid to 0");
......
......@@ -35,7 +35,7 @@ int lxc_rsync_exec_wrapper(void *data)
if (!lxc_switch_uid_gid(0, 0))
return -1;
if (!lxc_setgroups(0, NULL))
if (!lxc_drop_groups())
return -1;
return lxc_rsync_exec(args->src, args->dest);
......@@ -96,7 +96,7 @@ int lxc_rsync(struct rsync_data *data)
if (!lxc_switch_uid_gid(0, 0))
return -1;
if (!lxc_setgroups(0, NULL))
if (!lxc_drop_groups())
return -1;
src = lxc_storage_get_path(orig->dest, orig->type);
......
......@@ -461,7 +461,7 @@ int storage_destroy_wrapper(void *data)
{
struct lxc_conf *conf = data;
(void)lxc_setgroups(0, NULL);
(void)lxc_drop_groups();
if (setgid(0) < 0) {
SYSERROR("Failed to setgid to 0");
......
......@@ -1444,14 +1444,32 @@ bool lxc_switch_uid_gid(uid_t uid, gid_t gid)
}
/* Simple convenience function which enables uniform logging. */
bool lxc_setgroups(int size, gid_t list[])
bool lxc_drop_groups(void)
{
if (setgroups(size, list) < 0) {
SYSERROR("Failed to setgroups()");
return false;
int ret;
ret = setgroups(0, NULL);
if (ret)
return log_error_errno(false, errno, "Failed to drop supplimentary groups");
NOTICE("Dropped supplimentary groups");
return ret == 0;
}
bool lxc_setgroups(gid_t list[], size_t size)
{
int ret;
ret = setgroups(size, list);
if (ret)
return log_error_errno(false, errno, "Failed to set supplimentary groups");
if (size > 0 && lxc_log_trace()) {
for (size_t i = 0; i < size; i++)
TRACE("Setting supplimentary group %d", list[i]);
}
NOTICE("Dropped additional groups");
NOTICE("Set supplimentary groups");
return true;
}
......
......@@ -156,7 +156,8 @@ __hidden extern bool task_blocks_signal(pid_t pid, int signal);
* If LXC_INVALID_{G,U}ID is passed then the set{g,u}id() will not be called.
*/
__hidden extern bool lxc_switch_uid_gid(uid_t uid, gid_t gid);
__hidden extern bool lxc_setgroups(int size, gid_t list[]);
__hidden extern bool lxc_setgroups(gid_t list[], size_t size);
__hidden extern bool lxc_drop_groups(void);
/* Find an unused loop device and associate it with source. */
__hidden extern int lxc_prepare_loop_dev(const char *source, char *loop_dev, int flags);
......
......@@ -324,7 +324,54 @@ if ENABLE_SECCOMP
lxc_test_device_add_remove_SOURCES += ../lxc/seccomp.c ../lxc/lxcseccomp.h
endif
lxc_test_getkeys_SOURCES = getkeys.c
lxc_test_get_item_SOURCES = get_item.c
lxc_test_get_item_SOURCES = get_item.c \
../lxc/af_unix.c ../lxc/af_unix.h \
../lxc/caps.c ../lxc/caps.h \
../lxc/cgroups/cgfsng.c \
../lxc/cgroups/cgroup.c ../lxc/cgroups/cgroup.h \
../lxc/cgroups/cgroup2_devices.c ../lxc/cgroups/cgroup2_devices.h \
../lxc/cgroups/cgroup_utils.c ../lxc/cgroups/cgroup_utils.h \
../lxc/commands.c ../lxc/commands.h \
../lxc/commands_utils.c ../lxc/commands_utils.h \
../lxc/conf.c ../lxc/conf.h \
../lxc/confile.c ../lxc/confile.h \
../lxc/confile_utils.c ../lxc/confile_utils.h \
../lxc/error.c ../lxc/error.h \
../lxc/file_utils.c ../lxc/file_utils.h \
../include/netns_ifaddrs.c ../include/netns_ifaddrs.h \
../lxc/initutils.c ../lxc/initutils.h \
../lxc/log.c ../lxc/log.h \
../lxc/lxclock.c ../lxc/lxclock.h \
../lxc/mainloop.c ../lxc/mainloop.h \
../lxc/monitor.c ../lxc/monitor.h \
../lxc/namespace.c ../lxc/namespace.h \
../lxc/network.c ../lxc/network.h \
../lxc/nl.c ../lxc/nl.h \
../lxc/parse.c ../lxc/parse.h \
../lxc/process_utils.c ../lxc/process_utils.h \
../lxc/ringbuf.c ../lxc/ringbuf.h \
../lxc/start.c ../lxc/start.h \
../lxc/state.c ../lxc/state.h \
../lxc/storage/btrfs.c ../lxc/storage/btrfs.h \
../lxc/storage/dir.c ../lxc/storage/dir.h \
../lxc/storage/loop.c ../lxc/storage/loop.h \
../lxc/storage/lvm.c ../lxc/storage/lvm.h \
../lxc/storage/nbd.c ../lxc/storage/nbd.h \
../lxc/storage/overlay.c ../lxc/storage/overlay.h \
../lxc/storage/rbd.c ../lxc/storage/rbd.h \
../lxc/storage/rsync.c ../lxc/storage/rsync.h \
../lxc/storage/storage.c ../lxc/storage/storage.h \
../lxc/storage/storage_utils.c ../lxc/storage/storage_utils.h \
../lxc/storage/zfs.c ../lxc/storage/zfs.h \
../lxc/sync.c ../lxc/sync.h \
../lxc/string_utils.c ../lxc/string_utils.h \
../lxc/terminal.c ../lxc/terminal.h \
../lxc/utils.c ../lxc/utils.h \
../lxc/uuid.c ../lxc/uuid.h \
$(LSM_SOURCES)
if ENABLE_SECCOMP
lxc_test_get_item_SOURCES += ../lxc/seccomp.c ../lxc/lxcseccomp.h
endif
lxc_test_list_SOURCES = list.c
lxc_test_locktests_SOURCES = locktests.c \
../lxc/af_unix.c ../lxc/af_unix.h \
......
......@@ -29,15 +29,38 @@
#include "lxc/state.h"
#include "lxctest.h"
#include "utils.h"
#ifndef HAVE_STRLCPY
#include "include/strlcpy.h"
#endif
#define MYNAME "lxctest1"
int main(int argc, char *argv[])
{
int ret;
struct lxc_container *c;
int fd_log, ret;
struct lxc_container *c = NULL;
int fret = EXIT_FAILURE;
char v1[2], v2[256], v3[2048];
struct lxc_log log = {};
char template[sizeof(P_tmpdir"/attach_XXXXXX")];
(void)strlcpy(template, P_tmpdir"/attach_XXXXXX", sizeof(template));
fd_log = lxc_make_tmpfile(template, false);
if (fd_log < 0) {
lxc_error("Failed to create temporary log file for container %s\n", MYNAME);
exit(EXIT_FAILURE);
}
log.name = MYNAME;
log.file = template;
log.level = "TRACE";
log.prefix = "get_item";
log.quiet = false;
log.lxcpath = NULL;
if (lxc_log_init(&log))
goto out;
if ((c = lxc_container_new("testxyz", NULL)) == NULL) {
fprintf(stderr, "%d: error opening lxc_container %s\n", __LINE__, MYNAME);
......@@ -136,6 +159,59 @@ int main(int argc, char *argv[])
}
printf("lxc.init_gid returned %d %s\n", ret, v2);
if (c->set_config_item(c, "lxc.init.groups", "10,20,foo,40")) {
fprintf(stderr, "%d: managed to set lxc.init.groups to '10,20,foo,40'\n", __LINE__);
goto out;
}
if (!c->set_config_item(c, "lxc.init.groups", "10,20,30,40")) {
fprintf(stderr, "%d: managed to set lxc.init.groups to '10,20,30,40'\n", __LINE__);
goto out;
}
ret = c->get_config_item(c, "lxc.init.groups", v2, 255);
if (ret < 0) {
fprintf(stderr, "%d: failed to get lxc.init.groups\n", __LINE__);
goto out;
}
ret = strcmp("10,20,30,40", v2);
printf("%d: lxc.init.groups returned %d %s\n", __LINE__, ret, v2);
if (ret != 0) {
goto out;
}
if (!c->set_config_item(c, "lxc.init.groups", "50,60,70,80")) {
fprintf(stderr, "%d: failed to set lxc.init.groups to '50,60,70,80'\n", __LINE__);
goto out;
}
ret = c->get_config_item(c, "lxc.init.groups", v2, 255);
if (ret < 0) {
fprintf(stderr, "%d: failed to get lxc.init.groups\n", __LINE__);
goto out;
}
ret = strcmp("10,20,30,40,50,60,70,80", v2);
printf("%d: lxc.init.groups returned %d %s\n", __LINE__, ret, v2);
if (ret != 0) {
goto out;
}
if (!c->set_config_item(c, "lxc.init.groups", "")) {
fprintf(stderr, "%d: failed to set lxc.init.groups to ''\n", __LINE__);
goto out;
}
ret = c->get_config_item(c, "lxc.init.groups", v2, 255);
if (ret < 0) {
fprintf(stderr, "%d: failed to get lxc.init.groups\n", __LINE__);
goto out;
}
ret = strcmp("", v2);
printf("%d: lxc.init.groups returned %d %s\n", __LINE__, ret, v2);
if (ret != 0) {
goto out;
}
#define HNAME "hostname1"
// demonstrate proper usage:
char *alloced;
......@@ -619,6 +695,17 @@ out:
c->destroy(c);
lxc_container_put(c);
}
if (fret != EXIT_SUCCESS) {
char buf[4096];
ssize_t buflen;
while ((buflen = read(fd_log, buf, 1024)) > 0) {
buflen = write(STDERR_FILENO, buf, buflen);
if (buflen <= 0)
break;
}
close(fd_log);
}
(void)unlink(template);
exit(fret);
}
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