* container can't remount it read-write.
*/
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)
- 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
- 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)
return fret;
}
-static int parse_cap(const char *cap)
+int parse_cap(const char *cap)
{
size_t i;
int capid = -1;
return capid;
}
-int in_caplist(int cap, struct lxc_list *caps)
+bool has_cap(int cap, struct lxc_conf *conf)
{
- int capid;
- struct lxc_list *iterator;
+ bool cap_in_list = false;
+ struct cap_entry *cap_entry;
- lxc_list_for_each (iterator, caps) {
- capid = parse_cap(iterator->elem);
- if (capid == cap)
- return 1;
+ list_for_each_entry(cap_entry, &conf->caps.list, head) {
+ if (cap_entry->cap != cap)
+ continue;
+
+ 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;
- char *drop_entry;
- struct lxc_list *iterator;
+ struct cap_entry *cap;
- lxc_list_for_each (iterator, caps) {
+ list_for_each_entry(cap, &conf->caps.list, head) {
int ret;
- drop_entry = iterator->elem;
-
- 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),
+ ret = prctl(PR_CAPBSET_DROP, prctl_arg(cap->cap), prctl_arg(0),
prctl_arg(0), prctl_arg(0));
if (ret < 0)
- return log_error_errno(-1, errno, "Failed to remove %s capability", drop_entry);
- DEBUG("Dropped %s (%d) capability", drop_entry, capid);
+ return log_error_errno(-1, errno, "Failed to remove %s capability", cap->cap_name);
+
+ DEBUG("Dropped %s (%d) capability", cap->cap_name, cap->cap);
}
DEBUG("Capabilities have been setup");
return 0;
}
-static int dropcaps_except(struct lxc_list *caps)
+static int dropcaps_except(struct lxc_conf *conf)
{
- __do_free int *caplist = NULL;
- int i, capid, numcaps;
- char *keep_entry;
- struct lxc_list *iterator;
+ int numcaps;
+ struct cap_entry *cap;
numcaps = lxc_caps_last_cap() + 1;
if (numcaps <= 0 || numcaps > 200)
- return -1;
- TRACE("Found %d capabilities", numcaps);
-
- /* caplist[i] is 1 if we keep capability i */
- 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);
+ return ret_errno(EINVAL);
- DEBUG("Keep capability %s (%d)", keep_entry, capid);
- caplist[capid] = 1;
- }
+ TRACE("Found %d capabilities", numcaps);
- for (i = 0; i < numcaps; i++) {
+ list_for_each_entry(cap, &conf->caps.list, head) {
int ret;
- if (caplist[i])
+ if (cap->cap >= numcaps)
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));
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");
new->bpf_devices.list_type = LXC_BPF_DEVICE_CGROUP_ALLOWLIST;
INIT_LIST_HEAD(&(new->bpf_devices).devices);
lxc_list_init(&new->mount_list);
- lxc_list_init(&new->caps);
- lxc_list_init(&new->keepcaps);
+ INIT_LIST_HEAD(&new->caps.list);
INIT_LIST_HEAD(&new->id_map);
new->root_nsuid_map = NULL;
new->root_nsgid_map = NULL;
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 ret;
if (ret < 0)
return log_error(-1, "Failed to setup sysctl parameters");
- if (!lxc_list_empty(&lxc_conf->keepcaps)) {
- if (!lxc_list_empty(&lxc_conf->caps))
- return log_error(-1, "Container requests lxc.cap.drop and lxc.cap.keep: either use lxc.cap.drop or lxc.cap.keep, not both");
-
- 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");
- }
+ ret = setcup_capabilities(lxc_conf);
+ if (ret < 0)
+ return log_error(-1, "Failed to setup capabilities");
put_lxc_rootfs(&handler->conf->rootfs, true);
NOTICE("The container \"%s\" is set up", name);
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) {
- lxc_list_del(it);
- free(it->elem);
- free(it);
+ list_for_each_entry_safe(cap, ncap, &c->caps.list, head) {
+ list_del(&cap->head);
+ free(cap->cap_name);
+ free(cap);
}
- lxc_list_init(&c->caps);
+ c->caps.keep = false;
+ INIT_LIST_HEAD(&c->caps.list);
return 0;
}
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)
{
for (int i = 0; i < LXC_NS_MAX; i++)
free(conf->lsm_se_keyring_context);
lxc_seccomp_free(&conf->seccomp);
lxc_clear_config_caps(conf);
- lxc_clear_config_keepcaps(conf);
lxc_clear_cgroups(conf, "lxc.cgroup", CGROUP_SUPER_MAGIC);
lxc_clear_cgroups(conf, "lxc.cgroup2", CGROUP2_SUPER_MAGIC);
lxc_clear_cgroups_devices(conf);
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 {
/* Pointer to the name of the container. Do not free! */
const char *name;
struct lxc_list mount_list;
};
- struct lxc_list caps;
- struct lxc_list keepcaps;
+ struct caps caps;
/* /dev/tty<idx> devices */
struct lxc_tty_info ttys;
__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 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_mount_entries(struct lxc_conf *c);
__hidden extern int lxc_clear_automounts(struct lxc_conf *c);
__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,
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)
{
if (lxc_caps_last_cap() < cap)
return false;
- if (!lxc_list_empty(&conf->keepcaps))
- return in_caplist(cap, &conf->keepcaps);
-
- return !in_caplist(cap, &conf->caps);
+ return has_cap(cap, conf);
}
__hidden extern int setup_sysctl_parameters(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 */
return set_config_mount(NULL, value, lxc_conf, NULL);
}
-static int set_config_cap_keep(const char *key, const char *value,
- struct lxc_conf *lxc_conf, void *data)
+static int add_cap_entry(struct lxc_conf *conf, char *caps, bool keep)
{
- __do_free char *keepcaps = NULL;
- __do_free struct lxc_list *keeplist = NULL;
char *token;
- if (lxc_config_value_empty(value))
- return lxc_clear_config_keepcaps(lxc_conf);
+ /*
+ * 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;
- keepcaps = strdup(value);
- if (!keepcaps)
- return ret_errno(ENOMEM);
+ if (strequal(token, "none")) {
+ if (!keep)
+ return syserror_set(-EINVAL, "The \"none\" keyword is only valid when keeping caps");
- /* 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, keepcaps, " \t") {
- if (strequal(token, "none"))
- lxc_clear_config_keepcaps(lxc_conf);
+ 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;
+ }
- keeplist = lxc_list_new();
- if (!keeplist)
+ new_cap = zalloc(sizeof(struct cap_entry));
+ if (!new_cap)
return ret_errno(ENOMEM);
- keeplist->elem = strdup(token);
- if (!keeplist->elem)
+ new_cap->cap_name = strdup(token);
+ if (!new_cap->cap_name)
return ret_errno(ENOMEM);
+ new_cap->cap = cap;
- lxc_list_add_tail(&lxc_conf->keepcaps, move_ptr(keeplist));
+ list_add_tail(&new_cap->head, &conf->caps.list);
+ move_ptr(new_cap);
}
return 0;
}
-static int set_config_cap_drop(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)
{
- __do_free char *dropcaps = NULL;
- __do_free struct lxc_list *droplist = NULL;
- char *token;
+ __do_free char *caps = NULL;
+ int ret;
if (lxc_config_value_empty(value))
return lxc_clear_config_caps(lxc_conf);
- dropcaps = strdup(value);
- if (!dropcaps)
+ caps = strdup(value);
+ if (!caps)
return ret_errno(ENOMEM);
- /* In case several capability drop is specified in a single line
- * split these caps in a single element for the list.
- */
- lxc_iterate_parts(token, dropcaps, " \t") {
- droplist = lxc_list_new();
- if (!droplist)
- return ret_errno(ENOMEM);
+ if (!lxc_conf->caps.keep && !list_empty(&lxc_conf->caps.list))
+ return syserror_set(-EINVAL, "Keeping and dropping capabilities are mutually exclusive");
- droplist->elem = strdup(token);
- if (!droplist->elem)
- return ret_errno(ENOMEM);
+ ret = add_cap_entry(lxc_conf, caps, true);
+ if (ret < 0)
+ return ret;
- lxc_list_add_tail(&lxc_conf->caps, move_ptr(droplist));
- }
+ lxc_conf->caps.keep = true;
+ return 0;
+}
+
+static int set_config_cap_drop(const char *key, const char *value,
+ struct lxc_conf *lxc_conf, void *data)
+{
+ __do_free char *caps = NULL;
+ int ret;
+
+ if (lxc_config_value_empty(value))
+ return lxc_clear_config_caps(lxc_conf);
+ if (lxc_conf->caps.keep)
+ return syserror_set(-EINVAL, "Keeping and dropping capabilities are mutually exclusive");
+
+ caps = strdup(value);
+ if (!caps)
+ return ret_errno(ENOMEM);
+
+ ret = add_cap_entry(lxc_conf, caps, false);
+ if (ret < 0)
+ return ret;
+
+ lxc_conf->caps.keep = false;
return 0;
}
struct lxc_conf *c, void *data)
{
int len, fulllen = 0;
- struct lxc_list *it;
+ struct cap_entry *cap;
if (!retv)
inlen = 0;
else
memset(retv, 0, inlen);
- lxc_list_for_each(it, &c->caps) {
- strprint(retv, inlen, "%s\n", (char *)it->elem);
+ list_for_each_entry(cap, &c->caps.list, head) {
+ strprint(retv, inlen, "%s\n", cap->cap_name);
}
return fulllen;
struct lxc_conf *c, void *data)
{
int len, fulllen = 0;
- struct lxc_list *it;
+ struct cap_entry *cap;
if (!retv)
inlen = 0;
else
memset(retv, 0, inlen);
- lxc_list_for_each(it, &c->keepcaps) {
- strprint(retv, inlen, "%s\n", (char *)it->elem);
+ list_for_each_entry(cap, &c->caps.list, head) {
+ strprint(retv, inlen, "%s\n", cap->cap_name);
}
return fulllen;
static inline int clr_config_cap_keep(const char *key, struct lxc_conf *c,
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,