[lxc-devel] [PATCH RFC] introduce apparmor support

Serge Hallyn serge.hallyn at canonical.com
Mon Jun 18 21:24:18 UTC 2012


This could be done as generic 'lsm_init()' and 'lsm_load()' functions,
however that would make it impossible to compile one package supporting
more than one lsm.  If we explicitly add the selinux, smack, and aa
hooks in the source, then one package can be built to support multiple
kernels.

The smack support should be pretty trivial, and probably very close
to the apparmor support.

The selinux support may require more, including labeling the passed-in
fds (consoles etc) and filesystems.

If someone on the list has the inclination and experience to add selinux
support, please let me know.  Otherwise, I'll do Smack and SELinux.

Signed-off-by: Serge Hallyn <serge.hallyn at ubuntu.com>
---
 configure.ac        |   10 ++++
 src/lxc/Makefile.am |   11 +++--
 src/lxc/apparmor.c  |  134 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/lxc/apparmor.h  |   16 ++++++
 src/lxc/conf.c      |   20 ++++++++
 src/lxc/conf.h      |    9 ++++
 src/lxc/confile.c   |   25 ++++++++++
 src/lxc/start.c     |    5 ++
 src/lxc/start.h     |    3 ++
 9 files changed, 230 insertions(+), 3 deletions(-)
 create mode 100644 src/lxc/apparmor.c
 create mode 100644 src/lxc/apparmor.h

diff --git a/configure.ac b/configure.ac
index 0c8aa69..ada8cad 100644
--- a/configure.ac
+++ b/configure.ac
@@ -18,6 +18,11 @@ AC_ARG_ENABLE([rpath],
 
 AM_CONDITIONAL([ENABLE_RPATH], [test "x$enable_rpath" = "xyes"])
 
+AC_ARG_ENABLE([apparmor],
+	[AC_HELP_STRING([--enable-apparmor], [enable apparmor])],
+	[], [enable_apparmor=yes])
+AM_CONDITIONAL([ENABLE_APPARMOR], [test "x$enable_apparmor" = "xyes"])
+
 AC_ARG_ENABLE([doc],
 	[AC_HELP_STRING([--enable-doc], [make mans (require docbook2man installed) [default=auto]])],
 	[], [enable_doc=auto])
@@ -29,6 +34,11 @@ if test "x$enable_doc" = "xyes" -o "x$enable_doc" = "xauto"; then
 		AC_MSG_ERROR([docbook2man required by man request, but not found])
 fi
 
+AM_COND_IF([ENABLE_APPARMOR],
+    [AC_CHECK_HEADER([sys/apparmor.h],[],[AC_MSG_ERROR([You must install the AppArmor development package in order to compile libvirt])])
+     AC_CHECK_LIB([apparmor], [aa_change_profile],[],[AC_MSG_ERROR([You must install the AppArmor development package in order to compile libvirt])])
+     AC_SUBST([APPARMOR_LIBS], [-lapparmor])])
+
 AM_CONDITIONAL([ENABLE_DOCBOOK], [test "x$have_docbook" = "xyes"])
 
 AC_ARG_ENABLE([examples],
diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
index 1c26952..8b07166 100644
--- a/src/lxc/Makefile.am
+++ b/src/lxc/Makefile.am
@@ -53,20 +53,25 @@ liblxc_so_SOURCES = \
 	mainloop.c mainloop.h \
 	af_unix.c af_unix.h \
 	\
-	utmp.c utmp.h
+	utmp.c utmp.h \
+	apparmor.c apparmor.h
 
 AM_CFLAGS=-I$(top_srcdir)/src \
 	-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
 	-DLXCPATH=\"$(LXCPATH)\" \
 	-DLXCINITDIR=\"$(LXCINITDIR)\"
 
+if ENABLE_APPARMOR
+AM_CFLAGS += -DHAVE_APPARMOR
+endif
+
 liblxc_so_CFLAGS = -fPIC -DPIC $(AM_CFLAGS)
 
 liblxc_so_LDFLAGS = \
 	-shared \
 	-Wl,-soname,liblxc.so.$(firstword $(subst ., ,$(VERSION)))
 
-liblxc_so_LDADD = -lutil $(CAP_LIBS)
+liblxc_so_LDADD = -lutil $(CAP_LIBS) $(APPARMOR_LIBS)
 
 bin_SCRIPTS = \
 	lxc-ps \
@@ -104,7 +109,7 @@ AM_LDFLAGS = -Wl,-E
 if ENABLE_RPATH
 AM_LDFLAGS += -Wl,-rpath -Wl,$(libdir)
 endif
-LDADD=liblxc.so @CAP_LIBS@
+LDADD=liblxc.so @CAP_LIBS@ @APPARMOR_LIBS@
 
 lxc_attach_SOURCES = lxc_attach.c
 lxc_cgroup_SOURCES = lxc_cgroup.c
diff --git a/src/lxc/apparmor.c b/src/lxc/apparmor.c
new file mode 100644
index 0000000..71e847f
--- /dev/null
+++ b/src/lxc/apparmor.c
@@ -0,0 +1,134 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+
+#include "log.h"
+
+lxc_log_define(lxc_apparmor, lxc);
+
+#if HAVE_APPARMOR
+#include "apparmor.h"
+#include <sys/apparmor.h>
+
+#define AA_MOUNT_RESTR "/sys/kernel/security/apparmor/features/mount/mask"
+#define AA_ENABLED_FILE "/sys/module/apparmor/parameters/enabled"
+
+static int aa_am_unconfined(void)
+{
+	int ret;
+	char path[100], p[100];
+	sprintf(path, "/proc/%d/attr/current", getpid());
+	FILE *f = fopen(path, "r");
+	if (!f)
+		return 0;
+	ret = fscanf(f, "%99s", p);
+	fclose(f);
+	if (ret < 1)
+		return 0;
+	if (strcmp(p, "unconfined") == 0)
+		return 1;
+	return 0;
+}
+
+/* aa_getcon is not working right now.  Use our hand-rolled version below */
+static int check_apparmor_enabled(void)
+{
+	struct stat statbuf;
+	FILE *fin;
+	char e;
+	int ret;
+
+	ret = stat(AA_MOUNT_RESTR, &statbuf);
+	if (ret != 0)
+		return 0;
+	fin = fopen(AA_ENABLED_FILE, "r");
+	if (!fin)
+		return 0;
+	ret = fscanf(fin, "%c", &e);
+	fclose(fin);
+	if (ret == 1 && e == 'Y')
+		return 1;
+	return 0;
+}
+
+extern void apparmor_handler_init(struct lxc_handler *handler)
+{
+	handler->aa_enabled = check_apparmor_enabled();
+	INFO("aa_enabled set to %d\n", handler->aa_enabled);
+}
+
+#define AA_DEF_PROFILE "lxc-container-default"
+extern int apparmor_load(struct lxc_handler *handler)
+{
+	if (!handler->aa_enabled) {
+		INFO("apparmor not enabled");
+		return 0;
+	}
+	INFO("setting up apparmor");
+
+	if (!handler->conf->aa_profile)
+		handler->conf->aa_profile = AA_DEF_PROFILE;
+
+	if (strcmp(handler->conf->aa_profile, "unconfined") == 0 &&
+	    aa_am_unconfined()) {
+		INFO("apparmor profile unchanged");
+		return 0;
+	}
+
+	//if (aa_change_onexec(handler->conf->aa_profile) < 0) {
+	if (aa_change_profile(handler->conf->aa_profile) < 0) {
+		SYSERROR("failed to change apparmor profile to %s", handler->conf->aa_profile);
+		return -1;
+	}
+	if (handler->conf->lsm_umount_proc == 1)
+		umount("/proc");
+
+	INFO("changed apparmor profile to %s", handler->conf->aa_profile);
+
+	return 0;
+}
+
+/*
+ * this will likely move to a generic lsm.c, as selinux and smack will both
+ * also want proc mounted in the container so as to transition
+ */
+extern int lsm_mount_proc_if_needed(char *root_src, char *rootfs_tgt)
+{
+	char path[MAXPATHLEN];
+	char link[20];
+	int linklen, ret;
+
+	ret = snprintf(path, MAXPATHLEN, "%s/proc/self", root_src ? rootfs_tgt : "");
+	if (ret < 0 || ret >= MAXPATHLEN) {
+		SYSERROR("proc path name too long");
+		return -1;
+	}
+	memset(link, 0, 20);
+	linklen = readlink(path, link, 20);
+	INFO("I am %d, /proc/self points to %s\n", getpid(), link);
+	ret = snprintf(path, MAXPATHLEN, "%s/proc", root_src ? rootfs_tgt : "");
+	if (linklen < 0) /* /proc not mounted */
+		goto domount;
+	/* can't be longer than rootfs/proc/1 */
+	if (strncmp(link, "1", linklen) != 0) {
+		/* wrong /procs mounted */
+		umount2(path, MNT_DETACH); /* ignore failure */
+		goto domount;
+	}
+	/* the right proc is already mounted */
+	return 0;
+
+domount:
+	if (mount("proc", path, "proc", 0, NULL))
+		return -1;
+	INFO("Mounted /proc for the container\n");
+	return 1;
+}
+#else
+extern void apparmor_handler_init(struct lxc_handler *handler) {
+	INFO("apparmor_load - apparmor is disabled");
+}
+#endif
diff --git a/src/lxc/apparmor.h b/src/lxc/apparmor.h
new file mode 100644
index 0000000..87a6e54
--- /dev/null
+++ b/src/lxc/apparmor.h
@@ -0,0 +1,16 @@
+#include <lxc/start.h>  /* for lxc_handler */
+#include <lxc/conf.h>
+
+struct lxc_handler;
+
+#if HAVE_APPARMOR
+extern int apparmor_load(struct lxc_handler *handler);
+extern int lsm_mount_proc_if_needed(char *root_src, char *rootfs_tgt);
+extern void apparmor_handler_init(struct lxc_handler *handler);
+#else
+extern int apparmor_load(struct lxc_handler *handler);
+static inline int lsm_mount_proc_if_needed(char *root_src, char *rootfs_tgt) {
+	return 0;
+}
+extern void apparmor_handler_init(struct lxc_handler *handler) { }
+#endif
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
index e8088bb..8b2d785 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
@@ -1591,6 +1591,12 @@ struct lxc_conf *lxc_conf_init(void)
 	lxc_list_init(&new->network);
 	lxc_list_init(&new->mount_list);
 	lxc_list_init(&new->caps);
+#if HAVE_APPARMOR
+	new->aa_profile = NULL;
+#endif
+#if HAVE_APPARMOR /* || HAVE_SMACK || HAVE_SELINUX */
+	new->lsm_umount_proc = 0;
+#endif
 
 	return new;
 }
@@ -1987,6 +1993,10 @@ void lxc_delete_tty(struct lxc_tty_info *tty_info)
 
 int lxc_setup(const char *name, struct lxc_conf *lxc_conf)
 {
+#if HAVE_APPARMOR /* || HAVE_SMACK || HAVE_SELINUX */
+	int mounted;
+#endif
+
 	if (setup_utsname(lxc_conf->utsname)) {
 		ERROR("failed to setup the utsname for '%s'", name);
 		return -1;
@@ -2027,6 +2037,16 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf)
 		return -1;
 	}
 
+#if HAVE_APPARMOR /* || HAVE_SMACK || HAVE_SELINUX */
+	mounted = lsm_mount_proc_if_needed(lxc_conf->rootfs.path, lxc_conf->rootfs.mount);
+	if (mounted == -1) {
+		SYSERROR("failed to mount /proc in the container.");
+		return -1;
+	} else if (mounted == 1) {
+		lxc_conf->lsm_umount_proc = 1;
+	}
+#endif
+
 	if (setup_pivot_root(&lxc_conf->rootfs)) {
 		ERROR("failed to set rootfs for '%s'", name);
 		return -1;
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
index 09f55cb..cb2a1cf 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
@@ -198,6 +198,9 @@ struct lxc_rootfs {
  * @tty_info   : tty data
  * @console    : console data
  * @ttydir     : directory (under /dev) in which to create console and ttys
+#if HAVE_APPARMOR
+ * @aa_profile : apparmor profile to switch to
+#endif
  */
 struct lxc_conf {
 	char *fstab;
@@ -216,6 +219,12 @@ struct lxc_conf {
 	struct lxc_rootfs rootfs;
 	char *ttydir;
 	int close_all_fds;
+#if HAVE_APPARMOR
+	char *aa_profile;
+#endif
+#if HAVE_APPARMOR /* || HAVE_SELINUX || HAVE_SMACK */
+	int lsm_umount_proc;
+#endif
 };
 
 /*
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
index b305aef..09902ba 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -49,6 +49,9 @@ static int config_personality(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_ttydir(const char *, char *, struct lxc_conf *);
+#if HAVE_APPARMOR
+static int config_aa_profile(const char *, char *, struct lxc_conf *);
+#endif
 static int config_cgroup(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 *);
@@ -85,6 +88,9 @@ static struct config config[] = {
 	{ "lxc.pts",                  config_pts                  },
 	{ "lxc.tty",                  config_tty                  },
 	{ "lxc.devttydir",            config_ttydir               },
+#if HAVE_APPARMOR
+	{ "lxc.aa_profile",            config_aa_profile          },
+#endif
 	{ "lxc.cgroup",               config_cgroup               },
 	{ "lxc.mount",                config_mount                },
 	{ "lxc.rootfs.mount",         config_rootfs_mount         },
@@ -633,6 +639,25 @@ static int config_ttydir(const char *key, char *value,
 	return 0;
 }
 
+#if HAVE_APPARMOR
+static int config_aa_profile(const char *key, char *value, struct lxc_conf *lxc_conf)
+{
+	char *path;
+
+	if (!value || strlen(value) == 0)
+		return 0;
+	path = strdup(value);
+	if (!path) {
+		SYSERROR("failed to strdup '%s': %m", value);
+		return -1;
+	}
+
+	lxc_conf->aa_profile = path;
+
+	return 0;
+}
+#endif
+
 static int config_cgroup(const char *key, char *value, struct lxc_conf *lxc_conf)
 {
 	char *token = "lxc.cgroup.";
diff --git a/src/lxc/start.c b/src/lxc/start.c
index 920ff77..61084b6 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
@@ -126,6 +126,7 @@ int signalfd(int fd, const sigset_t *mask, int flags)
 #include "console.h"
 #include "sync.h"
 #include "namespace.h"
+#include "apparmor.h"
 
 lxc_log_define(lxc_start, lxc);
 
@@ -345,6 +346,7 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf)
 
 	handler->conf = conf;
 
+	apparmor_handler_init(handler);
 	handler->name = strdup(name);
 	if (!handler->name) {
 		ERROR("failed to allocate memory");
@@ -517,6 +519,9 @@ static int do_start(void *data)
 		goto out_warn_father;
 	}
 
+	if (apparmor_load(handler) < 0)
+		goto out_warn_father;
+
 	close(handler->sigfd);
 
 	/* after this call, we are in error because this
diff --git a/src/lxc/start.h b/src/lxc/start.h
index 016d3ee..0e12aba 100644
--- a/src/lxc/start.h
+++ b/src/lxc/start.h
@@ -45,6 +45,9 @@ struct lxc_handler {
 	struct lxc_operations *ops;
 	void *data;
 	int sv[2];
+#if HAVE_APPARMOR
+	int aa_enabled;
+#endif
 };
 
 extern struct lxc_handler *lxc_init(const char *name, struct lxc_conf *);
-- 
1.7.10.4





More information about the lxc-devel mailing list