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",