[lxc-devel] [patch] [draft] Add capabilities drop via configuration file

Andrian Nord nightnord at gmail.com
Sat Nov 14 23:32:33 UTC 2009


This is a draft of patch for capabilities support in syntax of

lxc.capability.<cap name in lowercase, without initial cap_> = keep | drop

Reason why it could be only dropped, not assigned - it that prctl could
only drop capabilities, while my attempts to implement capabilities via
libcap or raw syscall failed - for some reason capability changes was
cleared away soon after init boot phase inside container. I dunno, why,
as I was changing all three sets and file capabilities for init was
cleared.

Anyway, assuming that root has all capabilites and user can't assign
capabities more than it have itself, it should be enough.

Disadvantages of such approach it that capabilities droped into
hardcoded order, so if you will try to drop cap_setpcap, it will most
probably fail on dropping next capability.

It also may not work with CAP_MAC_ADMIN/OVERRIDE capabilities, as they
have codes bigger than 31 and CAP_TO_MASK maps it to the first
capabilities (wondering if this is right behaviour...)

I'm posting this patch here as RFC, maybe someone has better idea of
implementation?

Signed-off-by: Andrian Nord <NightNord at gmail.com>

diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index 6840e15..ea47195 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -6,6 +6,7 @@ pkginclude_HEADERS = \
 		namespace.h \
 		lxc.h \
 		cgroup.h \
+		capability.h \
 		conf.h \
 		list.h \
 		log.h \
@@ -28,6 +29,7 @@ liblxc_so_SOURCES = \
 	error.h error.c \
 	parse.c parse.h \
 	cgroup.c cgroup.h \
+	capability.c capability.h \
 	lxc.h \
 	utils.c utils.h \
 	namespace.h namespace.c \
diff --git a/src/lxc/capability.c b/src/lxc/capability.c
new file mode 100644
index 0000000..9098767
--- /dev/null
+++ b/src/lxc/capability.c
@@ -0,0 +1,166 @@
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright IBM Corp. 2007, 2008
+ *
+ * Authors:
+ * Daniel Lezcano <dlezcano at fr.ibm.com>
+ * File by:
+ * Andrian Nord <nightnord at gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#undef _GNU_SOURCE
+#include <errno.h>
+#include <linux/types.h>
+#include <sys/prctl.h>
+#include <sys/capability.h>
+
+#include <lxc/lxc.h>
+#include <lxc/log.h>
+
+#if !HAVE_DECL_PR_CAPBSET_DROP
+#define PR_CAPBSET_DROP 24
+#endif
+
+lxc_log_define(lxc_capability, lxc);
+
+struct capability {
+	char		*name;
+	__u32		code;
+	lxc_cap_state	initial_state;
+};
+
+static struct capability capabilities[] = {
+	{ "chown",		CAP_CHOWN,		LXC_CAP_KEEP	},
+
+	{ "dac_override",	CAP_DAC_OVERRIDE,	LXC_CAP_KEEP	},
+	{ "dac_read_search",	CAP_DAC_READ_SEARCH,	LXC_CAP_KEEP	},
+
+	{ "fowner",		CAP_FOWNER,		LXC_CAP_KEEP	},
+	{ "fsetid",		CAP_FSETID,		LXC_CAP_KEEP	},
+
+	{ "kill",		CAP_KILL,		LXC_CAP_KEEP	},
+
+	{ "setgid",		CAP_SETGID,		LXC_CAP_KEEP	},
+	{ "setuid",		CAP_SETUID,		LXC_CAP_KEEP	},
+
+	{ "setpcap",		CAP_SETPCAP,		LXC_CAP_KEEP	},
+	{ "linux_immutable",	CAP_LINUX_IMMUTABLE,	LXC_CAP_KEEP	},
+
+	{ "net_bind_service",	CAP_NET_BIND_SERVICE,	LXC_CAP_KEEP	},
+	{ "net_broadcast",	CAP_NET_BROADCAST,	LXC_CAP_KEEP	},
+	{ "net_admin",		CAP_NET_ADMIN,		LXC_CAP_KEEP	},
+	{ "net_raw",		CAP_NET_RAW,		LXC_CAP_KEEP	},
+
+	{ "ipc_lock",		CAP_IPC_LOCK,		LXC_CAP_KEEP	},
+	{ "ipc_owner",		CAP_IPC_OWNER,		LXC_CAP_KEEP	},
+
+	{ "sys_module",		CAP_SYS_MODULE,		LXC_CAP_DROP	},
+
+	{ "sys_rawio",		CAP_SYS_RAWIO,		LXC_CAP_DROP	},
+
+	{ "sys_chroot",		CAP_SYS_CHROOT,		LXC_CAP_KEEP	},
+
+	{ "sys_ptrace",		CAP_SYS_PTRACE,		LXC_CAP_KEEP	},
+	{ "sys_pacct",		CAP_SYS_PACCT,		LXC_CAP_DROP	},
+
+	{ "sys_admin",		CAP_SYS_ADMIN,		LXC_CAP_KEEP	},
+	{ "sys_boot",		CAP_SYS_BOOT,		LXC_CAP_DROP	},
+	{ "sys_nice",		CAP_SYS_NICE,		LXC_CAP_KEEP	},
+	{ "sys_resource",	CAP_SYS_RESOURCE,	LXC_CAP_KEEP	},
+	{ "sys_time",		CAP_SYS_TIME,		LXC_CAP_DROP	},
+
+	{ "sys_tty_config",	CAP_SYS_TTY_CONFIG,	LXC_CAP_KEEP	},
+	{ "mknod",		CAP_MKNOD,		LXC_CAP_KEEP	},
+	{ "lease",		CAP_LEASE,		LXC_CAP_KEEP	},
+
+	{ "audit_write",	CAP_AUDIT_WRITE,	LXC_CAP_DROP	},
+	{ "audit_control",	CAP_AUDIT_CONTROL,	LXC_CAP_DROP	},
+
+	{ "setfcap",		CAP_SETFCAP,		LXC_CAP_KEEP	}
+};
+
+static const size_t cap_count = sizeof(capabilities)/sizeof(struct capability);
+
+static struct capability *find_name(const char *cap_name)
+{
+	int i;
+
+	for (i = 0; i < cap_count; i++) {
+		if (!strncmp(capabilities[i].name, cap_name, strlen(cap_name)))
+			return &capabilities[i];
+	}
+
+	return NULL;
+}
+
+int lxc_capabilities_init(__u32 *mask)
+{
+	int i;
+	*mask = 0;
+
+	for (i = 0; i < cap_count; i++) {
+		if(capabilities[i].initial_state == LXC_CAP_KEEP)
+			*mask |= CAP_TO_MASK(capabilities[i].code);
+	}
+
+	return 0;
+}
+
+int lxc_capabilities_change(__u32 *mask, const char *cap_name,
+							lxc_cap_state state)
+{
+	struct capability *cap_spec;
+
+	cap_spec = find_name(cap_name);
+
+	if (!cap_spec) {
+		ERROR("capability '%s' not found", cap_name);
+		return -1;
+	}
+
+	if (state == LXC_CAP_DROP)
+		*mask &= ~CAP_TO_MASK(cap_spec->code);
+	else if (state == LXC_CAP_KEEP)
+		*mask |= CAP_TO_MASK(cap_spec->code);
+
+	return 0;
+}
+
+
+int lxc_capabilities_apply(__u32 mask)
+{
+	int i;
+	struct capability *cur_cap;
+
+	for (i = 0; i < cap_count; i++) {
+		cur_cap = &capabilities[i];
+
+		if (lxc_capabilities_check(mask, cur_cap->code))
+			continue;
+
+		DEBUG("dropping capability '%s'", cur_cap->name);
+
+	 	if (!prctl(PR_CAPBSET_DROP, cur_cap->code, 0, 0, 0))
+			continue;
+
+		SYSERROR("failed to remove '%s' capability", cur_cap->name);
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/src/lxc/capability.h b/src/lxc/capability.h
new file mode 100644
index 0000000..401ad2a
--- /dev/null
+++ b/src/lxc/capability.h
@@ -0,0 +1,44 @@
+/*
+ * lxc: linux Container library
+ *
+ * (C) Copyright IBM Corp. 2007, 2008
+ *
+ * Authors:
+ * Daniel Lezcano <dlezcano at fr.ibm.com>
+ * File by:
+ * Andrian Nord <nightnord at gmail.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef _capability_h
+#define _capability_h
+
+#include <linux/types.h>
+
+#define lxc_capabilities_check(mask, code) (mask & CAP_TO_MASK(code))
+
+typedef enum {
+	LXC_CAP_DROP	= 0,
+	LXC_CAP_KEEP	= 1
+} lxc_cap_state;
+
+int lxc_capabilities_init(__u32 *mask);
+
+int lxc_capabilities_change(__u32 *mask, const char *cap_name,
+							lxc_cap_state state);
+
+int lxc_capabilities_apply(__u32 mask);
+
+#endif
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index 0f7b723..5c7abf2 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -827,6 +827,10 @@ int lxc_conf_init(const char *name, struct lxc_conf *conf)
 	conf->tty = 0;
 	conf->pts = 0;
 	conf->console[0] = '\0';
+
+	conf->capabilities = malloc(sizeof(__u32));
+	lxc_capabilities_init(&conf->capabilities);
+
 	lxc_list_init(&conf->cgroup);
 	lxc_list_init(&conf->network);
 	lxc_list_init(&conf->mount_list);
@@ -1108,6 +1112,11 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf)
 		return -1;
 	}
 
+	if (lxc_capabilities_apply(lxc_conf->capabilities)) {
+		ERROR("failed to drop capabilities");
+		return -1;
+	}
+
 	NOTICE("'%s' is setup.", name);
 
 	return 0;
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index 2808820..8e907a2 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -134,6 +134,7 @@ struct lxc_conf {
 	char *name;
 	int tty;
 	int pts;
+	__u32 capabilities;
 	struct utsname *utsname;
 	struct lxc_list cgroup;
 	struct lxc_list network;
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index dddae37..376fe3b 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -47,6 +47,7 @@ static int config_include(const char *, char *, struct lxc_conf *);
 static int config_pts(const char *, char *, struct lxc_conf *);
 static int config_tty(const char *, char *, struct lxc_conf *);
 static int config_cgroup(const char *, char *, struct lxc_conf *);
+static int config_capability(const char *, char *, struct lxc_conf *);
 static int config_mount(const char *, char *, struct lxc_conf *);
 static int config_rootfs(const char *, char *, struct lxc_conf *);
 static int config_utsname(const char *, char *, struct lxc_conf *);
@@ -76,6 +77,7 @@ static struct config config[] = {
 	{ "lxc.pts",            config_pts            },
 	{ "lxc.tty",            config_tty            },
 	{ "lxc.cgroup",         config_cgroup         },
+	{ "lxc.capability",     config_capability     },
 	{ "lxc.mount",          config_mount          },
 	{ "lxc.rootfs",         config_rootfs         },
 	{ "lxc.utsname",        config_utsname        },
@@ -486,6 +488,38 @@ static int config_fstab(const char *key, char *value, struct lxc_conf *lxc_conf)
 	return 0;
 }
 
+static int config_capability(const char *key, char *value, struct lxc_conf *lxc_conf)
+{
+	char *prekey = "lxc.capability.";
+	char *subkey;
+
+	subkey = strstr(key, prekey);
+
+	if (!subkey)
+		return -1;
+
+	if (!strlen(subkey))
+		return -1;
+
+	if (strlen(subkey) == strlen(prekey))
+		return -1;
+
+	subkey += strlen(prekey);
+
+	lxc_cap_state state;
+
+	if (strcmp(value, "keep") == 0) {
+		state = LXC_CAP_KEEP;
+	} else if (strcmp(value, "drop") == 0) {
+		state = LXC_CAP_DROP;
+	} else {
+		ERROR("Bad value for '%s', should be 'keep' or 'drop'", key);
+		return -1;
+	}
+
+	return lxc_capabilities_change(&lxc_conf->capabilities, subkey, state);
+}
+
 static int config_mount(const char *key, char *value, struct lxc_conf *lxc_conf)
 {
 	char *fstab_token = "lxc.mount";
diff --git a/src/lxc/lxc.h b/src/lxc/lxc.h
index 1d04b9f..a13f3f9 100644
--- a/src/lxc/lxc.h
+++ b/src/lxc/lxc.h
@@ -36,6 +36,7 @@ extern "C" {
 #include <lxc/state.h>
 #include <lxc/list.h>
 #include <lxc/log.h>
+#include <lxc/capability.h>
 #include <lxc/conf.h>
 #include <lxc/namespace.h>
 #include <lxc/utils.h>
diff --git a/src/lxc/start.c b/src/lxc/start.c
index 435b41f..17b3142 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -39,7 +39,6 @@
 #include <sys/types.h>
 #include <sys/prctl.h>
 #include <sys/types.h>
-#include <sys/capability.h>
 #include <sys/wait.h>
 #include <sys/un.h>
 #include <sys/poll.h>
@@ -87,10 +86,6 @@ int signalfd(int fd, const sigset_t *mask, int flags)
 }
 #endif
 
-#if !HAVE_DECL_PR_CAPBSET_DROP
-#define PR_CAPBSET_DROP 24
-#endif
-
 #include "error.h"
 #include "af_unix.h"
 #include "mainloop.h"
@@ -368,11 +363,6 @@ static int do_start(void *arg)
 		goto out_warn_father;
 	}
 
-	if (prctl(PR_CAPBSET_DROP, CAP_SYS_BOOT, 0, 0, 0)) {
-		SYSERROR("failed to remove CAP_SYS_BOOT capability");
-		goto out_child;
-	}
-
 	if (prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0)) {
 		SYSERROR("failed to set pdeath signal");
 		goto out_child;




More information about the lxc-devel mailing list