]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
Introduce apparmor support
authorSerge Hallyn <serge.hallyn@ubuntu.com>
Tue, 31 Jul 2012 14:04:33 +0000 (16:04 +0200)
committerDaniel Lezcano <daniel.lezcano@free.fr>
Tue, 31 Jul 2012 14:04:33 +0000 (16:04 +0200)
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@ubuntu.com>
Signed-off-by: Daniel Lezcano <dlezcano@fr.ibm.com>
configure.ac
src/lxc/Makefile.am
src/lxc/apparmor.c [new file with mode: 0644]
src/lxc/apparmor.h [new file with mode: 0644]
src/lxc/conf.c
src/lxc/conf.h
src/lxc/confile.c
src/lxc/start.c
src/lxc/start.h

index 8470e8a497bc366f365562e9d29ca6e3109cd3ad..70c74ec493a4ce1b64cff1fb4aced86047af9df2 100644 (file)
@@ -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 lxc])])
+     AC_CHECK_LIB([apparmor], [aa_change_profile],[],[AC_MSG_ERROR([You must install the AppArmor development package in order to compile lxc])])
+     AC_SUBST([APPARMOR_LIBS], [-lapparmor])])
+
 AM_CONDITIONAL([ENABLE_DOCBOOK], [test "x$have_docbook" = "xyes"])
 
 AC_ARG_ENABLE([examples],
index 26137fc161558c403ec0f521b0822e2938d7ee80..50e67bbf2984654424d5dcaa44e96ec9890ca859 100644 (file)
@@ -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 \
@@ -105,7 +110,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 (file)
index 0000000..71e847f
--- /dev/null
@@ -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 (file)
index 0000000..87a6e54
--- /dev/null
@@ -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
index 601f4bd801e883d85316727b79dedc0d957b47e2..724a26c742aa74ac93029a2d85ad52dcceff69f1 100644 (file)
@@ -1636,6 +1636,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;
 }
@@ -2032,6 +2038,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;
@@ -2072,6 +2082,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;
index 0d8f28ed85a5bdd2d6bd395552cbc60c3f9a193e..0c1f5c31733517def90ddc7c56e768077c3a82a7 100644 (file)
@@ -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
 };
 
 /*
index b305aef1c905188688c50709586e384632739d0b..09902ba4b8e01ebd6ff5fc21f82eebc5b52f6d60 100644 (file)
@@ -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.";
index 2938537571a28c1f674a80e398f9072e7c3a6018..fffa4cd2cbdb285cdc00ac14fb5e3fe26b5ff878 100644 (file)
@@ -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
index 016d3ee55a2a970eab45576ebe78a5b7e1f16011..0e12abaedb995170f0352c3cf9e2f61085f1a210 100644 (file)
@@ -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 *);