diff --git a/doc/lxc.conf.sgml.in b/doc/lxc.conf.sgml.in
index c4997a974..257c13467 100644
--- a/doc/lxc.conf.sgml.in
+++ b/doc/lxc.conf.sgml.in
@@ -771,6 +771,17 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+
+
+
+
+ Specify the capability to be kept in the container. All other
+ capabilities will be dropped.
+
+
+
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 12d01c2e2..83613ed99 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -1838,7 +1838,73 @@ static int setup_caps(struct lxc_list *caps)
}
- 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; inetwork);
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; ihooks[i]);
@@ -2926,7 +2993,16 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf, const char *lxcpath)
}
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;
}
@@ -3125,6 +3201,18 @@ int lxc_clear_idmaps(struct lxc_conf *c)
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;
@@ -3224,6 +3312,7 @@ void lxc_conf_free(struct lxc_conf *conf)
#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);
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index 8ff0efa9d..5febf126d 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -232,7 +232,8 @@ struct lxc_rootfs {
* @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
@@ -266,6 +267,7 @@ struct lxc_conf {
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;
@@ -323,6 +325,7 @@ extern void lxc_delete_tty(struct lxc_tty_info *tty_info);
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);
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index 1f170973b..7904db4a4 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -85,6 +85,7 @@ static int config_network_script(const char *, const char *, struct lxc_conf *);
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 *);
@@ -136,6 +137,7 @@ static struct lxc_config_t config[] = {
/* 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 },
@@ -1272,6 +1274,52 @@ static int config_mount(const char *key, const char *value,
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)
{
@@ -1638,6 +1686,22 @@ static int lxc_get_item_cap_drop(struct lxc_conf *c, char *retv, int inlen)
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;
@@ -1818,6 +1882,8 @@ int lxc_get_config_item(struct lxc_conf *c, const char *key, char *retv,
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)
@@ -1841,6 +1907,8 @@ int lxc_clear_config_item(struct lxc_conf *c, const char *key)
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)
@@ -1953,6 +2021,8 @@ void write_config(FILE *fout, struct lxc_conf *c)
}
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",