}
- DEBUG("capabilities has been setup");
+ DEBUG("capabilities have been setup");
+
+ return 0;
+}
+
+static int dropcaps_except(struct lxc_list *caps)
+{
+ struct lxc_list *iterator;
+ char *keep_entry;
+ char *ptr;
+ int i, capid;
+ int numcaps = lxc_caps_last_cap() + 1;
+ INFO("found %d capabilities\n", numcaps);
+
+ // caplist[i] is 1 if we keep capability i
+ int *caplist = alloca(numcaps * sizeof(int));
+ memset(caplist, 0, numcaps * sizeof(int));
+
+ lxc_list_for_each(iterator, caps) {
+
+ keep_entry = iterator->elem;
+
+ capid = -1;
+
+ for (i = 0; i < sizeof(caps_opt)/sizeof(caps_opt[0]); i++) {
+
+ if (strcmp(keep_entry, caps_opt[i].name))
+ continue;
+
+ capid = caps_opt[i].value;
+ break;
+ }
+
+ if (capid < 0) {
+ /* try to see if it's numeric, so the user may specify
+ * capabilities that the running kernel knows about but
+ * we don't */
+ capid = strtol(keep_entry, &ptr, 10);
+ if (!ptr || *ptr != '\0' ||
+ capid == LONG_MIN || capid == LONG_MAX)
+ /* not a valid number */
+ capid = -1;
+ else if (capid > lxc_caps_last_cap())
+ /* we have a number but it's not a valid
+ * capability */
+ capid = -1;
+ }
+
+ if (capid < 0) {
+ ERROR("unknown capability %s", keep_entry);
+ return -1;
+ }
+
+ DEBUG("drop capability '%s' (%d)", keep_entry, capid);
+
+ caplist[capid] = 1;
+ }
+ for (i=0; i<numcaps; i++) {
+ if (caplist[i])
+ continue;
+ if (prctl(PR_CAPBSET_DROP, i, 0, 0, 0)) {
+ SYSERROR("failed to remove capability %d", i);
+ return -1;
+ }
+ }
+
+ DEBUG("capabilities have been setup");
return 0;
}
lxc_list_init(&new->network);
lxc_list_init(&new->mount_list);
lxc_list_init(&new->caps);
+ lxc_list_init(&new->keepcaps);
lxc_list_init(&new->id_map);
for (i=0; i<NUM_LXC_HOOKS; i++)
lxc_list_init(&new->hooks[i]);
}
if (lxc_list_empty(&lxc_conf->id_map)) {
- if (setup_caps(&lxc_conf->caps)) {
+ if (!lxc_list_empty(&lxc_conf->keepcaps)) {
+ if (!lxc_list_empty(&lxc_conf->caps)) {
+ ERROR("Simultaneously requested dropping and keeping caps");
+ return -1;
+ }
+ if (dropcaps_except(&lxc_conf->keepcaps)) {
+ ERROR("failed to keep requested caps\n");
+ return -1;
+ }
+ } else if (setup_caps(&lxc_conf->caps)) {
ERROR("failed to drop capabilities");
return -1;
}
return 0;
}
+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);
+ }
+ return 0;
+}
+
int lxc_clear_cgroups(struct lxc_conf *c, const char *key)
{
struct lxc_list *it,*next;
#endif
lxc_seccomp_free(conf);
lxc_clear_config_caps(conf);
+ lxc_clear_config_keepcaps(conf);
lxc_clear_cgroups(conf, "lxc.cgroup");
lxc_clear_hooks(conf, "lxc.hook");
lxc_clear_mount_entries(conf);
* @network : network configuration
* @utsname : container utsname
* @fstab : path to a fstab file format
- * @caps : list of the capabilities
+ * @caps : list of the capabilities to drop
+ * @keepcaps : list of the capabilities to keep
* @tty_info : tty data
* @console : console data
* @ttydir : directory (under /dev) in which to create console and ttys
int num_savednics;
struct lxc_list mount_list;
struct lxc_list caps;
+ struct lxc_list keepcaps;
struct lxc_tty_info tty_info;
struct lxc_console console;
struct lxc_rootfs rootfs;
extern int lxc_clear_config_network(struct lxc_conf *c);
extern int lxc_clear_nic(struct lxc_conf *c, const char *key);
extern int lxc_clear_config_caps(struct lxc_conf *c);
+extern int lxc_clear_config_keepcaps(struct lxc_conf *c);
extern int lxc_clear_cgroups(struct lxc_conf *c, const char *key);
extern int lxc_clear_mount_entries(struct lxc_conf *c);
extern int lxc_clear_hooks(struct lxc_conf *c, const char *key);
static int config_network_ipv6(const char *, const char *, struct lxc_conf *);
static int config_network_ipv6_gateway(const char *, const char *, struct lxc_conf *);
static int config_cap_drop(const char *, const char *, struct lxc_conf *);
+static int config_cap_keep(const char *, const char *, struct lxc_conf *);
static int config_console(const char *, const char *, struct lxc_conf *);
static int config_seccomp(const char *, const char *, struct lxc_conf *);
static int config_includefile(const char *, const char *, struct lxc_conf *);
/* config_network_nic must come after all other 'lxc.network.*' entries */
{ "lxc.network.", config_network_nic },
{ "lxc.cap.drop", config_cap_drop },
+ { "lxc.cap.keep", config_cap_keep },
{ "lxc.console", config_console },
{ "lxc.seccomp", config_seccomp },
{ "lxc.include", config_includefile },
return 0;
}
+static int config_cap_keep(const char *key, const char *value,
+ struct lxc_conf *lxc_conf)
+{
+ char *keepcaps, *keepptr, *sptr, *token;
+ struct lxc_list *keeplist;
+ int ret = -1;
+
+ if (!strlen(value))
+ return -1;
+
+ keepcaps = strdup(value);
+ if (!keepcaps) {
+ SYSERROR("failed to dup '%s'", value);
+ return -1;
+ }
+
+ /* in case several capability keep is specified in a single line
+ * split these caps in a single element for the list */
+ for (keepptr = keepcaps;;keepptr = NULL) {
+ token = strtok_r(keepptr, " \t", &sptr);
+ if (!token) {
+ ret = 0;
+ break;
+ }
+
+ keeplist = malloc(sizeof(*keeplist));
+ if (!keeplist) {
+ SYSERROR("failed to allocate keepcap list");
+ break;
+ }
+
+ keeplist->elem = strdup(token);
+ if (!keeplist->elem) {
+ SYSERROR("failed to dup '%s'", token);
+ free(keeplist);
+ break;
+ }
+
+ lxc_list_add_tail(&lxc_conf->keepcaps, keeplist);
+ }
+
+ free(keepcaps);
+
+ return ret;
+}
+
static int config_cap_drop(const char *key, const char *value,
struct lxc_conf *lxc_conf)
{
return fulllen;
}
+static int lxc_get_item_cap_keep(struct lxc_conf *c, char *retv, int inlen)
+{
+ int len, fulllen = 0;
+ struct lxc_list *it;
+
+ if (!retv)
+ inlen = 0;
+ else
+ memset(retv, 0, inlen);
+
+ lxc_list_for_each(it, &c->keepcaps) {
+ strprint(retv, inlen, "%s\n", (char *)it->elem);
+ }
+ return fulllen;
+}
+
static int lxc_get_mount_entries(struct lxc_conf *c, char *retv, int inlen)
{
int len, fulllen = 0;
v = c->rootfs.pivot;
else if (strcmp(key, "lxc.cap.drop") == 0)
return lxc_get_item_cap_drop(c, retv, inlen);
+ else if (strcmp(key, "lxc.cap.keep") == 0)
+ return lxc_get_item_cap_keep(c, retv, inlen);
else if (strncmp(key, "lxc.hook", 8) == 0)
return lxc_get_item_hooks(c, retv, inlen, key);
else if (strcmp(key, "lxc.network") == 0)
return lxc_clear_nic(c, key + 12);
else if (strcmp(key, "lxc.cap.drop") == 0)
return lxc_clear_config_caps(c);
+ else if (strcmp(key, "lxc.cap.keep") == 0)
+ return lxc_clear_config_keepcaps(c);
else if (strncmp(key, "lxc.cgroup", 10) == 0)
return lxc_clear_cgroups(c, key);
else if (strcmp(key, "lxc.mount.entries") == 0)
}
lxc_list_for_each(it, &c->caps)
fprintf(fout, "lxc.cap.drop = %s\n", (char *)it->elem);
+ lxc_list_for_each(it, &c->keepcaps)
+ fprintf(fout, "lxc.cap.keep = %s\n", (char *)it->elem);
lxc_list_for_each(it, &c->id_map) {
struct id_map *idmap = it->elem;
fprintf(fout, "lxc.id_map = %c %lu %lu %lu\n",