conf: simplify and port caps to new list type

Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
This commit is contained in:
Christian Brauner 2021-08-26 15:25:28 +02:00
parent badf09ec16
commit 20ab75789e
No known key found for this signature in database
GPG Key ID: 8EB056D53EECB12D
3 changed files with 155 additions and 148 deletions

View File

@ -828,17 +828,14 @@ static int lxc_mount_auto_mounts(struct lxc_handler *handler, int flags)
* container can't remount it read-write. * container can't remount it read-write.
*/ */
if ((cg_flags == LXC_AUTO_CGROUP_NOSPEC) || (cg_flags == LXC_AUTO_CGROUP_FULL_NOSPEC)) { if ((cg_flags == LXC_AUTO_CGROUP_NOSPEC) || (cg_flags == LXC_AUTO_CGROUP_FULL_NOSPEC)) {
int has_sys_admin = 0;
if (!lxc_list_empty(&conf->keepcaps))
has_sys_admin = in_caplist(CAP_SYS_ADMIN, &conf->keepcaps);
else
has_sys_admin = !in_caplist(CAP_SYS_ADMIN, &conf->caps);
if (cg_flags == LXC_AUTO_CGROUP_NOSPEC) if (cg_flags == LXC_AUTO_CGROUP_NOSPEC)
cg_flags = has_sys_admin ? LXC_AUTO_CGROUP_RW : LXC_AUTO_CGROUP_MIXED; cg_flags = has_cap(CAP_SYS_ADMIN, conf)
? LXC_AUTO_CGROUP_RW
: LXC_AUTO_CGROUP_MIXED;
else else
cg_flags = has_sys_admin ? LXC_AUTO_CGROUP_FULL_RW : LXC_AUTO_CGROUP_FULL_MIXED; cg_flags = has_cap(CAP_SYS_ADMIN, conf)
? LXC_AUTO_CGROUP_FULL_RW
: LXC_AUTO_CGROUP_FULL_MIXED;
} }
if (flags & LXC_AUTO_CGROUP_FORCE) if (flags & LXC_AUTO_CGROUP_FORCE)
@ -3105,7 +3102,7 @@ out:
return fret; return fret;
} }
static int parse_cap(const char *cap) int parse_cap(const char *cap)
{ {
size_t i; size_t i;
int capid = -1; int capid = -1;
@ -3142,86 +3139,70 @@ static int parse_cap(const char *cap)
return capid; return capid;
} }
int in_caplist(int cap, struct lxc_list *caps) bool has_cap(int cap, struct lxc_conf *conf)
{ {
int capid; bool cap_in_list = false;
struct lxc_list *iterator; struct cap_entry *cap_entry;
lxc_list_for_each (iterator, caps) { list_for_each_entry(cap_entry, &conf->caps.list, head) {
capid = parse_cap(iterator->elem); if (cap_entry->cap != cap)
if (capid == cap) continue;
return 1;
cap_in_list = true;
} }
return 0; /* The capability is kept. */
if (conf->caps.keep)
return cap_in_list;
/* The capability is not dropped. */
return !cap_in_list;
} }
static int setup_caps(struct lxc_list *caps) static int setup_caps(struct lxc_conf *conf)
{ {
int capid; struct cap_entry *cap;
char *drop_entry;
struct lxc_list *iterator;
lxc_list_for_each (iterator, caps) { list_for_each_entry(cap, &conf->caps.list, head) {
int ret; int ret;
drop_entry = iterator->elem; ret = prctl(PR_CAPBSET_DROP, prctl_arg(cap->cap), prctl_arg(0),
capid = parse_cap(drop_entry);
if (capid < 0)
return log_error(-1, "unknown capability %s", drop_entry);
ret = prctl(PR_CAPBSET_DROP, prctl_arg(capid), prctl_arg(0),
prctl_arg(0), prctl_arg(0)); prctl_arg(0), prctl_arg(0));
if (ret < 0) if (ret < 0)
return log_error_errno(-1, errno, "Failed to remove %s capability", drop_entry); return log_error_errno(-1, errno, "Failed to remove %s capability", cap->cap_name);
DEBUG("Dropped %s (%d) capability", drop_entry, capid);
DEBUG("Dropped %s (%d) capability", cap->cap_name, cap->cap);
} }
DEBUG("Capabilities have been setup"); DEBUG("Capabilities have been setup");
return 0; return 0;
} }
static int dropcaps_except(struct lxc_list *caps) static int dropcaps_except(struct lxc_conf *conf)
{ {
__do_free int *caplist = NULL; int numcaps;
int i, capid, numcaps; struct cap_entry *cap;
char *keep_entry;
struct lxc_list *iterator;
numcaps = lxc_caps_last_cap() + 1; numcaps = lxc_caps_last_cap() + 1;
if (numcaps <= 0 || numcaps > 200) if (numcaps <= 0 || numcaps > 200)
return -1; return ret_errno(EINVAL);
TRACE("Found %d capabilities", numcaps); TRACE("Found %d capabilities", numcaps);
/* caplist[i] is 1 if we keep capability i */ list_for_each_entry(cap, &conf->caps.list, head) {
caplist = must_realloc(NULL, numcaps * sizeof(int));
memset(caplist, 0, numcaps * sizeof(int));
lxc_list_for_each (iterator, caps) {
keep_entry = iterator->elem;
capid = parse_cap(keep_entry);
if (capid == -2)
continue;
if (capid < 0)
return log_error(-1, "Unknown capability %s", keep_entry);
DEBUG("Keep capability %s (%d)", keep_entry, capid);
caplist[capid] = 1;
}
for (i = 0; i < numcaps; i++) {
int ret; int ret;
if (caplist[i]) if (cap->cap >= numcaps)
continue; continue;
ret = prctl(PR_CAPBSET_DROP, prctl_arg(i), prctl_arg(0), ret = prctl(PR_CAPBSET_DROP, prctl_arg(cap->cap), prctl_arg(0),
prctl_arg(0), prctl_arg(0)); prctl_arg(0), prctl_arg(0));
if (ret < 0) if (ret < 0)
return log_error_errno(-1, errno, "Failed to remove capability %d", i); return log_error_errno(-1, errno,
"Failed to remove capability %s (%d)",
cap->cap_name, cap->cap);
DEBUG("Keep capability %s (%d)", cap->cap_name, cap->cap);
} }
DEBUG("Capabilities have been setup"); DEBUG("Capabilities have been setup");
@ -3385,8 +3366,7 @@ struct lxc_conf *lxc_conf_init(void)
new->bpf_devices.list_type = LXC_BPF_DEVICE_CGROUP_ALLOWLIST; new->bpf_devices.list_type = LXC_BPF_DEVICE_CGROUP_ALLOWLIST;
INIT_LIST_HEAD(&(new->bpf_devices).devices); INIT_LIST_HEAD(&(new->bpf_devices).devices);
lxc_list_init(&new->mount_list); lxc_list_init(&new->mount_list);
lxc_list_init(&new->caps); INIT_LIST_HEAD(&new->caps.list);
lxc_list_init(&new->keepcaps);
INIT_LIST_HEAD(&new->id_map); INIT_LIST_HEAD(&new->id_map);
new->root_nsuid_map = NULL; new->root_nsuid_map = NULL;
new->root_nsgid_map = NULL; new->root_nsgid_map = NULL;
@ -4275,6 +4255,20 @@ int lxc_sync_fds_child(struct lxc_handler *handler)
return 0; return 0;
} }
static int setcup_capabilities(struct lxc_conf *conf)
{
int ret;
if (conf->caps.keep)
ret = dropcaps_except(conf);
else
ret = setup_caps(conf);
if (ret < 0)
return log_error(-1, "Failed to %s capabilities", conf->caps.keep ? "keep" : "drop");
return 0;
}
int lxc_setup(struct lxc_handler *handler) int lxc_setup(struct lxc_handler *handler)
{ {
int ret; int ret;
@ -4418,15 +4412,9 @@ int lxc_setup(struct lxc_handler *handler)
if (ret < 0) if (ret < 0)
return log_error(-1, "Failed to setup sysctl parameters"); return log_error(-1, "Failed to setup sysctl parameters");
if (!lxc_list_empty(&lxc_conf->keepcaps)) { ret = setcup_capabilities(lxc_conf);
if (!lxc_list_empty(&lxc_conf->caps)) if (ret < 0)
return log_error(-1, "Container requests lxc.cap.drop and lxc.cap.keep: either use lxc.cap.drop or lxc.cap.keep, not both"); return log_error(-1, "Failed to setup capabilities");
if (dropcaps_except(&lxc_conf->keepcaps))
return log_error(-1, "Failed to keep capabilities");
} else if (setup_caps(&lxc_conf->caps)) {
return log_error(-1, "Failed to drop capabilities");
}
put_lxc_rootfs(&handler->conf->rootfs, true); put_lxc_rootfs(&handler->conf->rootfs, true);
NOTICE("The container \"%s\" is set up", name); NOTICE("The container \"%s\" is set up", name);
@ -4463,15 +4451,16 @@ int run_lxc_hooks(const char *name, char *hookname, struct lxc_conf *conf,
int lxc_clear_config_caps(struct lxc_conf *c) int lxc_clear_config_caps(struct lxc_conf *c)
{ {
struct lxc_list *it, *next; struct cap_entry *cap, *ncap;
lxc_list_for_each_safe (it, &c->caps, next) { list_for_each_entry_safe(cap, ncap, &c->caps.list, head) {
lxc_list_del(it); list_del(&cap->head);
free(it->elem); free(cap->cap_name);
free(it); free(cap);
} }
lxc_list_init(&c->caps); c->caps.keep = false;
INIT_LIST_HEAD(&c->caps.list);
return 0; return 0;
} }
@ -4500,20 +4489,6 @@ int lxc_clear_idmaps(struct lxc_conf *c)
return lxc_free_idmap(&c->id_map); return lxc_free_idmap(&c->id_map);
} }
int lxc_clear_config_keepcaps(struct lxc_conf *c)
{
struct lxc_list *it, *next;
lxc_list_for_each_safe (it, &c->keepcaps, next) {
lxc_list_del(it);
free(it->elem);
free(it);
}
lxc_list_init(&c->keepcaps);
return 0;
}
int lxc_clear_namespace(struct lxc_conf *c) int lxc_clear_namespace(struct lxc_conf *c)
{ {
for (int i = 0; i < LXC_NS_MAX; i++) for (int i = 0; i < LXC_NS_MAX; i++)
@ -4789,7 +4764,6 @@ void lxc_conf_free(struct lxc_conf *conf)
free(conf->lsm_se_keyring_context); free(conf->lsm_se_keyring_context);
lxc_seccomp_free(&conf->seccomp); lxc_seccomp_free(&conf->seccomp);
lxc_clear_config_caps(conf); lxc_clear_config_caps(conf);
lxc_clear_config_keepcaps(conf);
lxc_clear_cgroups(conf, "lxc.cgroup", CGROUP_SUPER_MAGIC); lxc_clear_cgroups(conf, "lxc.cgroup", CGROUP_SUPER_MAGIC);
lxc_clear_cgroups(conf, "lxc.cgroup2", CGROUP2_SUPER_MAGIC); lxc_clear_cgroups(conf, "lxc.cgroup2", CGROUP2_SUPER_MAGIC);
lxc_clear_cgroups_devices(conf); lxc_clear_cgroups_devices(conf);

View File

@ -343,6 +343,17 @@ struct environment_entry {
struct list_head head; struct list_head head;
}; };
struct cap_entry {
char *cap_name;
int cap;
struct list_head head;
};
struct caps {
int keep;
struct list_head list;
};
struct lxc_conf { struct lxc_conf {
/* Pointer to the name of the container. Do not free! */ /* Pointer to the name of the container. Do not free! */
const char *name; const char *name;
@ -381,8 +392,7 @@ struct lxc_conf {
struct lxc_list mount_list; struct lxc_list mount_list;
}; };
struct lxc_list caps; struct caps caps;
struct lxc_list keepcaps;
/* /dev/tty<idx> devices */ /* /dev/tty<idx> devices */
struct lxc_tty_info ttys; struct lxc_tty_info ttys;
@ -530,7 +540,6 @@ __hidden extern int lxc_map_ids(struct list_head *idmap, pid_t pid);
__hidden extern int lxc_create_tty(const char *name, struct lxc_conf *conf); __hidden extern int lxc_create_tty(const char *name, struct lxc_conf *conf);
__hidden extern void lxc_delete_tty(struct lxc_tty_info *ttys); __hidden extern void lxc_delete_tty(struct lxc_tty_info *ttys);
__hidden extern int lxc_clear_config_caps(struct lxc_conf *c); __hidden extern int lxc_clear_config_caps(struct lxc_conf *c);
__hidden extern int lxc_clear_config_keepcaps(struct lxc_conf *c);
__hidden extern int lxc_clear_cgroups(struct lxc_conf *c, const char *key, int version); __hidden extern int lxc_clear_cgroups(struct lxc_conf *c, const char *key, int version);
__hidden extern int lxc_clear_mount_entries(struct lxc_conf *c); __hidden extern int lxc_clear_mount_entries(struct lxc_conf *c);
__hidden extern int lxc_clear_automounts(struct lxc_conf *c); __hidden extern int lxc_clear_automounts(struct lxc_conf *c);
@ -563,17 +572,14 @@ __hidden extern void sort_cgroup_settings(struct lxc_conf *conf);
__hidden extern int run_script(const char *name, const char *section, const char *script, ...); __hidden extern int run_script(const char *name, const char *section, const char *script, ...);
__hidden extern int run_script_argv(const char *name, unsigned int hook_version, const char *section, __hidden extern int run_script_argv(const char *name, unsigned int hook_version, const char *section,
const char *script, const char *hookname, char **argsin); const char *script, const char *hookname, char **argsin);
__hidden extern int in_caplist(int cap, struct lxc_list *caps);
__hidden extern bool has_cap(int cap, struct lxc_conf *conf);
static inline bool lxc_wants_cap(int cap, struct lxc_conf *conf) static inline bool lxc_wants_cap(int cap, struct lxc_conf *conf)
{ {
if (lxc_caps_last_cap() < cap) if (lxc_caps_last_cap() < cap)
return false; return false;
if (!lxc_list_empty(&conf->keepcaps)) return has_cap(cap, conf);
return in_caplist(cap, &conf->keepcaps);
return !in_caplist(cap, &conf->caps);
} }
__hidden extern int setup_sysctl_parameters(struct lxc_conf *conf); __hidden extern int setup_sysctl_parameters(struct lxc_conf *conf);
@ -650,5 +656,6 @@ static inline int lxc_personality(personality_t persona)
} }
__hidden extern int lxc_set_environment(const struct lxc_conf *conf); __hidden extern int lxc_set_environment(const struct lxc_conf *conf);
__hidden extern int parse_cap(const char *cap);
#endif /* __LXC_CONF_H */ #endif /* __LXC_CONF_H */

View File

@ -2400,70 +2400,96 @@ int add_elem_to_mount_list(const char *value, struct lxc_conf *lxc_conf) {
return set_config_mount(NULL, value, lxc_conf, NULL); return set_config_mount(NULL, value, lxc_conf, NULL);
} }
static int add_cap_entry(struct lxc_conf *conf, char *caps, bool keep)
{
char *token;
/*
* In case several capability keep is specified in a single line split
* these caps in a single element for the list.
*/
lxc_iterate_parts(token, caps, " \t") {
__do_free struct cap_entry *new_cap = NULL;
int cap;
if (strequal(token, "none")) {
if (!keep)
return syserror_set(-EINVAL, "The \"none\" keyword is only valid when keeping caps");
lxc_clear_config_caps(conf);
continue;
}
cap = parse_cap(token);
if (cap < 0) {
if (cap != -2)
return syserror_set(-EINVAL, "Invalid capability specified");
INFO("Ignoring unknown capability \"%s\"", token);
continue;
}
new_cap = zalloc(sizeof(struct cap_entry));
if (!new_cap)
return ret_errno(ENOMEM);
new_cap->cap_name = strdup(token);
if (!new_cap->cap_name)
return ret_errno(ENOMEM);
new_cap->cap = cap;
list_add_tail(&new_cap->head, &conf->caps.list);
move_ptr(new_cap);
}
return 0;
}
static int set_config_cap_keep(const char *key, const char *value, static int set_config_cap_keep(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data) struct lxc_conf *lxc_conf, void *data)
{ {
__do_free char *keepcaps = NULL; __do_free char *caps = NULL;
__do_free struct lxc_list *keeplist = NULL; int ret;
char *token;
if (lxc_config_value_empty(value)) if (lxc_config_value_empty(value))
return lxc_clear_config_keepcaps(lxc_conf); return lxc_clear_config_caps(lxc_conf);
keepcaps = strdup(value); caps = strdup(value);
if (!keepcaps) if (!caps)
return ret_errno(ENOMEM); return ret_errno(ENOMEM);
/* In case several capability keep is specified in a single line if (!lxc_conf->caps.keep && !list_empty(&lxc_conf->caps.list))
* split these caps in a single element for the list. return syserror_set(-EINVAL, "Keeping and dropping capabilities are mutually exclusive");
*/
lxc_iterate_parts(token, keepcaps, " \t") {
if (strequal(token, "none"))
lxc_clear_config_keepcaps(lxc_conf);
keeplist = lxc_list_new(); ret = add_cap_entry(lxc_conf, caps, true);
if (!keeplist) if (ret < 0)
return ret_errno(ENOMEM); return ret;
keeplist->elem = strdup(token);
if (!keeplist->elem)
return ret_errno(ENOMEM);
lxc_list_add_tail(&lxc_conf->keepcaps, move_ptr(keeplist));
}
lxc_conf->caps.keep = true;
return 0; return 0;
} }
static int set_config_cap_drop(const char *key, const char *value, static int set_config_cap_drop(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data) struct lxc_conf *lxc_conf, void *data)
{ {
__do_free char *dropcaps = NULL; __do_free char *caps = NULL;
__do_free struct lxc_list *droplist = NULL; int ret;
char *token;
if (lxc_config_value_empty(value)) if (lxc_config_value_empty(value))
return lxc_clear_config_caps(lxc_conf); return lxc_clear_config_caps(lxc_conf);
dropcaps = strdup(value); if (lxc_conf->caps.keep)
if (!dropcaps) return syserror_set(-EINVAL, "Keeping and dropping capabilities are mutually exclusive");
caps = strdup(value);
if (!caps)
return ret_errno(ENOMEM); return ret_errno(ENOMEM);
/* In case several capability drop is specified in a single line ret = add_cap_entry(lxc_conf, caps, false);
* split these caps in a single element for the list. if (ret < 0)
*/ return ret;
lxc_iterate_parts(token, dropcaps, " \t") {
droplist = lxc_list_new();
if (!droplist)
return ret_errno(ENOMEM);
droplist->elem = strdup(token);
if (!droplist->elem)
return ret_errno(ENOMEM);
lxc_list_add_tail(&lxc_conf->caps, move_ptr(droplist));
}
lxc_conf->caps.keep = false;
return 0; return 0;
} }
@ -4255,15 +4281,15 @@ static int get_config_cap_drop(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data) struct lxc_conf *c, void *data)
{ {
int len, fulllen = 0; int len, fulllen = 0;
struct lxc_list *it; struct cap_entry *cap;
if (!retv) if (!retv)
inlen = 0; inlen = 0;
else else
memset(retv, 0, inlen); memset(retv, 0, inlen);
lxc_list_for_each(it, &c->caps) { list_for_each_entry(cap, &c->caps.list, head) {
strprint(retv, inlen, "%s\n", (char *)it->elem); strprint(retv, inlen, "%s\n", cap->cap_name);
} }
return fulllen; return fulllen;
@ -4273,15 +4299,15 @@ static int get_config_cap_keep(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data) struct lxc_conf *c, void *data)
{ {
int len, fulllen = 0; int len, fulllen = 0;
struct lxc_list *it; struct cap_entry *cap;
if (!retv) if (!retv)
inlen = 0; inlen = 0;
else else
memset(retv, 0, inlen); memset(retv, 0, inlen);
lxc_list_for_each(it, &c->keepcaps) { list_for_each_entry(cap, &c->caps.list, head) {
strprint(retv, inlen, "%s\n", (char *)it->elem); strprint(retv, inlen, "%s\n", cap->cap_name);
} }
return fulllen; return fulllen;
@ -5024,7 +5050,7 @@ static inline int clr_config_cap_drop(const char *key, struct lxc_conf *c,
static inline int clr_config_cap_keep(const char *key, struct lxc_conf *c, static inline int clr_config_cap_keep(const char *key, struct lxc_conf *c,
void *data) void *data)
{ {
return lxc_clear_config_keepcaps(c); return lxc_clear_config_caps(c);
} }
static inline int clr_config_console_path(const char *key, struct lxc_conf *c, static inline int clr_config_console_path(const char *key, struct lxc_conf *c,