]> git.ipfire.org Git - thirdparty/lxc.git/commitdiff
refactor AppArmor into LSM backend, add SELinux support
authorDwight Engen <dwight.engen@oracle.com>
Tue, 24 Sep 2013 15:13:02 +0000 (11:13 -0400)
committerSerge Hallyn <serge.hallyn@ubuntu.com>
Wed, 25 Sep 2013 22:12:36 +0000 (17:12 -0500)
Currently, a maximum of one LSM within LXC will be initialized and
used. If in the future stacked LSMs become a reality, we can support it
without changing the configuration syntax and add support for more than
a single LSM at a time to the lsm code.

Generic LXC code should note that lsm_process_label_set() will take
effect "now" for AppArmor, and upon exec() for SELinux.

- fix Oracle template mounting of proc and sysfs, needed when using SELinux

Signed-off-by: Dwight Engen <dwight.engen@oracle.com>
Acked-by: Serge Hallyn <serge.hallyn@ubuntu.com>
Signed-off-by: Serge Hallyn <serge.hallyn@ubuntu.com>
17 files changed:
configure.ac
doc/lxc.conf.sgml.in
src/lxc/Makefile.am
src/lxc/apparmor.h [deleted file]
src/lxc/attach.c
src/lxc/attach.h
src/lxc/conf.c
src/lxc/conf.h
src/lxc/confile.c
src/lxc/lsm/apparmor.c [moved from src/lxc/apparmor.c with 51% similarity]
src/lxc/lsm/lsm.c [new file with mode: 0644]
src/lxc/lsm/lsm.h [new file with mode: 0644]
src/lxc/lsm/nop.c [new file with mode: 0644]
src/lxc/lsm/selinux.c [new file with mode: 0644]
src/lxc/start.c
src/lxc/start.h
templates/lxc-oracle.in

index 092a67d7cebaae2c6721e7e91394da33326a3b05..92a4690dcad402a9ededeffc95de0b59f55b98d3 100644 (file)
@@ -117,6 +117,20 @@ AM_COND_IF([ENABLE_APPARMOR],
        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])])
 
+# SELinux
+AC_ARG_ENABLE([selinux],
+       [AC_HELP_STRING([--enable-selinux], [enable SELinux support])],
+       [], [enable_selinux=check])
+
+if test "x$enable_selinux" = xcheck; then
+       AC_CHECK_LIB([selinux],[setexeccon_raw],[enable_selinux=yes],[enable_selinux=no])
+fi
+AM_CONDITIONAL([ENABLE_SELINUX], [test "x$enable_selinux" = "xyes"])
+AM_COND_IF([ENABLE_SELINUX],
+       [AC_CHECK_HEADER([selinux/selinux.h],[],[AC_MSG_ERROR([You must install the SELinux development package in order to compile lxc])])
+       AC_CHECK_LIB([selinux], [setexeccon_raw],[],[AC_MSG_ERROR([You must install the SELinux development package in order to compile lxc])])
+       AC_SUBST([SELINUX_LIBS])])
+
 # Seccomp syscall filter
 AC_ARG_ENABLE([seccomp],
        [AC_HELP_STRING([--enable-seccomp], [enable seccomp])],
index dc416e85631ed30fc826d94b6930a445f5cdf1fa..bad553c1758727fb9bb3f131999a4ba0f669895e 100644 (file)
@@ -810,6 +810,31 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
       </variablelist>
     </refsect2>
 
+    <refsect2>
+      <title>SELinux context</title>
+      <para>
+       If lxc was compiled and installed with SELinux support, and the host
+       system has SELinux enabled, then the SELinux context under which the
+       container should be run can be specified in the container
+       configuration.  The default is <command>unconfined_t</command>,
+       which means that lxc will not attempt to change contexts.
+      </para>
+      <variablelist>
+       <varlistentry>
+         <term>
+           <option>lxc.se_context</option>
+         </term>
+         <listitem>
+           <para>
+             Specify the SELinux context under which the container should
+             be run or <command>unconfined_t</command>. For example
+           </para>
+           <programlisting>lxc.se_context = unconfined_u:unconfined_r:lxc_t:s0-s0:c0.c1023</programlisting>
+         </listitem>
+       </varlistentry>
+      </variablelist>
+    </refsect2>
+
     <refsect2>
       <title>Seccomp configuration</title>
       <para>
index f19a9944f02b51981a07baf231aed463c043a01c..873b97d0443ae7e3d71dc605976ed0c8f6865c72 100644 (file)
@@ -37,6 +37,18 @@ sodir=$(libdir)
 # use PROGRAMS to avoid complains from automake
 so_PROGRAMS = liblxc.so
 
+LSM_SOURCES = \
+       lsm/nop.c \
+       lsm/lsm.h lsm/lsm.c
+
+if ENABLE_APPARMOR
+LSM_SOURCES += lsm/apparmor.c
+endif
+
+if ENABLE_SELINUX
+LSM_SOURCES += lsm/selinux.c
+endif
+
 liblxc_so_SOURCES = \
        arguments.c arguments.h \
        bdev.c bdev.h \
@@ -73,10 +85,11 @@ liblxc_so_SOURCES = \
        af_unix.c af_unix.h \
        \
        lxcutmp.c lxcutmp.h \
-       apparmor.c apparmor.h \
        lxclock.h lxclock.c \
        lxccontainer.c lxccontainer.h \
-       version.c version.h
+       version.c version.h \
+       \
+       $(LSM_SOURCES)
 
 if IS_BIONIC
 liblxc_so_SOURCES += \
@@ -107,6 +120,10 @@ if ENABLE_APPARMOR
 AM_CFLAGS += -DHAVE_APPARMOR
 endif
 
+if ENABLE_SELINUX
+AM_CFLAGS += -DHAVE_SELINUX
+endif
+
 if HAVE_NEWUIDMAP
 AM_CFLAGS += -DHAVE_NEWUIDMAP
 endif
diff --git a/src/lxc/apparmor.h b/src/lxc/apparmor.h
deleted file mode 100644 (file)
index e27a728..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/* apparmor
- *
- * Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>.
- * Copyright © 2012 Canonical Ltd.
- *
- *  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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#include <lxc/start.h>  /* for lxc_handler */
-#include <lxc/conf.h>
-
-struct lxc_handler;
-
-/*
- * apparmor_handler_init is really just a wrapper around check_apparmor_enabled
- * to allow us to keep from having #ifdef APPARMOR in start.c
- */
-extern void apparmor_handler_init(struct lxc_handler *handler);
-
-#if HAVE_APPARMOR
-extern char *aa_get_profile(pid_t pid);
-extern int do_apparmor_load(int aa_enabled, char *aa_profile,
-                                  int umount_proc, int dropprivs);
-extern int apparmor_load(struct lxc_handler *handler);
-extern int attach_apparmor(char *profile);
-extern int lsm_mount_proc_if_needed(char *root_src, char *rootfs_tgt);
-#else
-static inline char *aa_get_profile(pid_t pid) {
-       return NULL;
-}
-static inline int do_apparmor_load(int aa_enabled, char *aa_profile,
-                                  int umount_proc, int dropprivs) {
-       return 0;
-}
-static inline int attach_apparmor(char *profile) {
-       return 0;
-}
-static inline int apparmor_load(struct lxc_handler *handler) {
-       return 0;
-}
-static inline int lsm_mount_proc_if_needed(char *root_src, char *rootfs_tgt) {
-       return 0;
-}
-#endif
index cc95079a5674c61fa044d6e11b6cbc5a39bb1661..37cefb0204eec4bdd9e450b37e014af51391b863 100644 (file)
 #include "attach.h"
 #include "caps.h"
 #include "config.h"
-#include "apparmor.h"
 #include "utils.h"
 #include "commands.h"
 #include "cgroup.h"
 #include "lxclock.h"
+#include "lsm/lsm.h"
 
 #if HAVE_SYS_PERSONALITY_H
 #include <sys/personality.h>
@@ -129,7 +129,7 @@ struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid)
                errno = ENOENT;
                goto out_error;
        }
-       info->aa_profile = aa_get_profile(pid);
+       info->lsm_label = lsm_process_label_get(pid);
 
        return info;
 
@@ -138,6 +138,13 @@ out_error:
        return NULL;
 }
 
+static void lxc_proc_put_context_info(struct lxc_proc_context_info *ctx)
+{
+       if (ctx->lsm_label)
+               free(ctx->lsm_label);
+       free(ctx);
+}
+
 int lxc_attach_to_ns(pid_t pid, int which)
 {
        char path[MAXPATHLEN];
@@ -644,8 +651,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
                        ERROR("failed to automatically determine the "
                              "namespaces which the container unshared");
                        free(cwd);
-                       free(init_ctx->aa_profile);
-                       free(init_ctx);
+                       lxc_proc_put_context_info(init_ctx);
                        return -1;
                }
        }
@@ -683,8 +689,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
        if (ret < 0) {
                SYSERROR("could not set up required IPC mechanism for attaching");
                free(cwd);
-               free(init_ctx->aa_profile);
-               free(init_ctx);
+               lxc_proc_put_context_info(init_ctx);
                return -1;
        }
 
@@ -705,8 +710,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
        if (pid < 0) {
                SYSERROR("failed to create first subprocess");
                free(cwd);
-               free(init_ctx->aa_profile);
-               free(init_ctx);
+               lxc_proc_put_context_info(init_ctx);
                return -1;
        }
 
@@ -794,8 +798,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
                process_lock();
                close(ipc_sockets[0]);
                process_unlock();
-               free(init_ctx->aa_profile);
-               free(init_ctx);
+               lxc_proc_put_context_info(init_ctx);
 
                /* we're done, the child process should now execute whatever
                 * it is that the user requested. The parent can now track it
@@ -815,8 +818,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
                process_unlock();
                if (to_cleanup_pid)
                        (void) wait_for_pid(to_cleanup_pid);
-               free(init_ctx->aa_profile);
-               free(init_ctx);
+               lxc_proc_put_context_info(init_ctx);
                return -1;
        }
 
@@ -918,7 +920,7 @@ int attach_child_main(void* data)
 
        /* load apparmor profile */
        if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_APPARMOR)) {
-               ret = attach_apparmor(init_ctx->aa_profile);
+               ret = lsm_process_label_set(init_ctx->lsm_label, 0);
                if (ret < 0) {
                        shutdown(ipc_socket, SHUT_RDWR);
                        rexit(-1);
@@ -1021,8 +1023,7 @@ int attach_child_main(void* data)
 
        shutdown(ipc_socket, SHUT_RDWR);
        close(ipc_socket);
-       free(init_ctx->aa_profile);
-       free(init_ctx);
+       lxc_proc_put_context_info(init_ctx);
 
        /* The following is done after the communication socket is
         * shut down. That way, all errors that might (though
index 518d08662c59d04aaeb7cb59f99119eb563619ff..b5f3ea1c3f9cc4e37683f1dd10afa830e1d92d52 100644 (file)
@@ -28,7 +28,7 @@
 #include "attach_options.h"
 
 struct lxc_proc_context_info {
-       char *aa_profile;
+       char *lsm_label;
        unsigned long personality;
        unsigned long long capability_mask;
 };
index e933c9a3a561172d44588d3b1e713c13ba257b78..18a92c9d2e2f8845416cf066aba6e38615861da1 100644 (file)
 #include "bdev.h"
 #include "cgroup.h"
 #include "lxclock.h"
-
-#if HAVE_APPARMOR
-#include <apparmor.h>
-#endif
+#include "lsm/lsm.h"
 
 #if HAVE_SYS_CAPABILITY_H
 #include <sys/capability.h>
@@ -2398,12 +2395,9 @@ struct lxc_conf *lxc_conf_init(void)
        lxc_list_init(&new->id_map);
        for (i=0; i<NUM_LXC_HOOKS; i++)
                lxc_list_init(&new->hooks[i]);
-#if HAVE_APPARMOR
-       new->aa_profile = NULL;
-#endif
-#if HAVE_APPARMOR /* || HAVE_SMACK || HAVE_SELINUX */
+       new->lsm_aa_profile = NULL;
+       new->lsm_se_context = NULL;
        new->lsm_umount_proc = 0;
-#endif
 
        return new;
 }
@@ -3043,10 +3037,6 @@ int uid_shift_ttys(int pid, struct lxc_conf *conf)
 
 int lxc_setup(const char *name, struct lxc_conf *lxc_conf, const char *lxcpath, struct cgroup_process_info *cgroup_info)
 {
-#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;
@@ -3140,24 +3130,11 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf, const char *lxcpath,
                return -1;
        }
 
-#if HAVE_APPARMOR /* || HAVE_SMACK || HAVE_SELINUX */
-       INFO("rootfs path is .%s., mount is .%s.", lxc_conf->rootfs.path,
-               lxc_conf->rootfs.mount);
-       if (lxc_conf->rootfs.path == NULL || strlen(lxc_conf->rootfs.path) == 0) {
-               if (mount("proc", "/proc", "proc", 0, NULL)) {
-                       SYSERROR("Failed mounting /proc, proceeding");
-                       mounted = 0;
-               } else
-                       mounted = 1;
-       } else
-               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.");
+       /* mount /proc if needed for LSM transition */
+       if (lsm_proc_mount(lxc_conf) < 0) {
+               ERROR("failed to LSM mount proc for '%s'", name);
                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);
@@ -3488,10 +3465,10 @@ void lxc_conf_free(struct lxc_conf *conf)
        if (conf->rcfile)
                free(conf->rcfile);
        lxc_clear_config_network(conf);
-#if HAVE_APPARMOR
-       if (conf->aa_profile)
-               free(conf->aa_profile);
-#endif
+       if (conf->lsm_aa_profile)
+               free(conf->lsm_aa_profile);
+       if (conf->lsm_se_context)
+               free(conf->lsm_se_context);
        lxc_seccomp_free(conf);
        lxc_clear_config_caps(conf);
        lxc_clear_config_keepcaps(conf);
index d99bdfe4256e535a5be98bbe0b36df5ccb386615..8c2dc4e8fc9ca81cc38a552fe07a333db6bac81d 100644 (file)
@@ -247,9 +247,8 @@ enum {
  * @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
+ * @lsm_aa_profile : apparmor profile to switch to or NULL
+ * @lsm_se_context : selinux type to switch to or NULL
  */
 enum lxchooks {
        LXCHOOK_PRESTART, LXCHOOK_PREMOUNT, LXCHOOK_MOUNT, LXCHOOK_AUTODEV,
@@ -285,13 +284,10 @@ struct lxc_conf {
        char *ttydir;
        int close_all_fds;
        struct lxc_list hooks[NUM_LXC_HOOKS];
-#if HAVE_APPARMOR
-       char *aa_profile;
-#endif
 
-#if HAVE_APPARMOR /* || HAVE_SELINUX || HAVE_SMACK */
+       char *lsm_aa_profile;
+       char *lsm_se_context;
        int lsm_umount_proc;
-#endif
        char *seccomp;  // filename with the seccomp rules
 #if HAVE_SCMP_FILTER_CTX
        scmp_filter_ctx *seccomp_ctx;
index b378c3ae8390e14392a7a7f61bfbf086fc92396a..a623d887ed7b3a1f67c3d95585d88367beecdbe7 100644 (file)
@@ -57,9 +57,8 @@ static int config_pts(const char *, const char *, struct lxc_conf *);
 static int config_tty(const char *, const char *, struct lxc_conf *);
 static int config_ttydir(const char *, const char *, struct lxc_conf *);
 static int config_kmsg(const char *, const char *, struct lxc_conf *);
-#if HAVE_APPARMOR
-static int config_aa_profile(const char *, const char *, struct lxc_conf *);
-#endif
+static int config_lsm_aa_profile(const char *, const char *, struct lxc_conf *);
+static int config_lsm_se_context(const char *, const char *, struct lxc_conf *);
 static int config_cgroup(const char *, const char *, struct lxc_conf *);
 static int config_idmap(const char *, const char *, struct lxc_conf *);
 static int config_loglevel(const char *, const char *, struct lxc_conf *);
@@ -100,9 +99,8 @@ static struct lxc_config_t config[] = {
        { "lxc.tty",                  config_tty                  },
        { "lxc.devttydir",            config_ttydir               },
        { "lxc.kmsg",                 config_kmsg                 },
-#if HAVE_APPARMOR
-       { "lxc.aa_profile",            config_aa_profile          },
-#endif
+       { "lxc.aa_profile",           config_lsm_aa_profile       },
+       { "lxc.se_context",           config_lsm_se_context       },
        { "lxc.cgroup",               config_cgroup               },
        { "lxc.id_map",               config_idmap                },
        { "lxc.loglevel",             config_loglevel             },
@@ -967,9 +965,8 @@ static int config_kmsg(const char *key, const char *value,
        return 0;
 }
 
-#if HAVE_APPARMOR
-static int config_aa_profile(const char *key, const char *value,
-                            struct lxc_conf *lxc_conf)
+static int config_lsm_aa_profile(const char *key, const char *value,
+                                struct lxc_conf *lxc_conf)
 {
        char *path;
 
@@ -981,13 +978,32 @@ static int config_aa_profile(const char *key, const char *value,
                return -1;
        }
 
-       if (lxc_conf->aa_profile)
-               free(lxc_conf->aa_profile);
-       lxc_conf->aa_profile = path;
+       if (lxc_conf->lsm_aa_profile)
+               free(lxc_conf->lsm_aa_profile);
+       lxc_conf->lsm_aa_profile = path;
+
+       return 0;
+}
+
+static int config_lsm_se_context(const char *key, const 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;
+       }
+
+       if (lxc_conf->lsm_se_context)
+               free(lxc_conf->lsm_se_context);
+       lxc_conf->lsm_se_context = path;
 
        return 0;
 }
-#endif
 
 static int config_logfile(const char *key, const char *value,
                             struct lxc_conf *lxc_conf)
@@ -1913,10 +1929,10 @@ int lxc_get_config_item(struct lxc_conf *c, const char *key, char *retv,
                v = c->ttydir;
        else if (strcmp(key, "lxc.arch") == 0)
                return lxc_get_arch_entry(c, retv, inlen);
-#if HAVE_APPARMOR
        else if (strcmp(key, "lxc.aa_profile") == 0)
-               v = c->aa_profile;
-#endif
+               v = c->lsm_aa_profile;
+       else if (strcmp(key, "lxc.se_context") == 0)
+               v = c->lsm_se_context;
        else if (strcmp(key, "lxc.logfile") == 0)
                v = lxc_log_get_file();
        else if (strcmp(key, "lxc.loglevel") == 0)
@@ -2000,10 +2016,10 @@ void write_config(FILE *fout, struct lxc_conf *c)
        default: break;
        }
        #endif
-#if HAVE_APPARMOR
-       if (c->aa_profile)
-               fprintf(fout, "lxc.aa_profile = %s\n", c->aa_profile);
-#endif
+       if (c->lsm_aa_profile)
+               fprintf(fout, "lxc.aa_profile = %s\n", c->lsm_aa_profile);
+       if (c->lsm_se_context)
+               fprintf(fout, "lxc.se_context = %s\n", c->lsm_se_context);
        if (c->loglevel != LXC_LOG_PRIORITY_NOTSET)
                fprintf(fout, "lxc.loglevel = %s\n", lxc_log_priority_to_string(c->loglevel));
        if (c->logfile)
similarity index 51%
rename from src/lxc/apparmor.c
rename to src/lxc/lsm/apparmor.c
index c31cce760cf975477757eef49a86e1b949fa54d4..c13613a61c7ca9c141c2df66026099d1b3585955 100644 (file)
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/mount.h>
-
+#include <sys/apparmor.h>
 #include "log.h"
-#include "apparmor.h"
 #include "lxclock.h"
+#include "lsm/lsm.h"
 
 lxc_log_define(lxc_apparmor, lxc);
 
-#if HAVE_APPARMOR
-#include <sys/apparmor.h>
-
+#define AA_DEF_PROFILE "lxc-container-default"
 #define AA_MOUNT_RESTR "/sys/kernel/security/apparmor/features/mount/mask"
 #define AA_ENABLED_FILE "/sys/module/apparmor/parameters/enabled"
 
+/* aa_getcon is not working right now.  Use our hand-rolled version below */
+static int apparmor_enabled(void)
+{
+       struct stat statbuf;
+       FILE *fin;
+       char e;
+       int ret;
+
+       ret = stat(AA_MOUNT_RESTR, &statbuf);
+       if (ret != 0)
+               return 0;
+       process_lock();
+       fin = fopen(AA_ENABLED_FILE, "r");
+       process_unlock();
+       if (!fin)
+               return 0;
+       ret = fscanf(fin, "%c", &e);
+       process_lock();
+       fclose(fin);
+       process_unlock();
+       if (ret == 1 && e == 'Y')
+               return 1;
+       return 0;
+}
 
-/* caller must free the returned profile */
-extern char *aa_get_profile(pid_t pid)
+static char *apparmor_process_label_get(pid_t pid)
 {
        char path[100], *space;
        int ret;
@@ -65,7 +86,6 @@ again:
        }
        sz += 1024;
        buf = realloc(buf, sz);
-       memset(buf, 0, sz);
        if (!buf) {
                ERROR("out of memory");
                process_lock();
@@ -73,17 +93,18 @@ again:
                process_unlock();
                return NULL;
        }
+       memset(buf, 0, sz);
        ret = fread(buf, 1, sz - 1, f);
        process_lock();
        fclose(f);
        process_unlock();
-       if (ret >= sz)
-               goto again;
        if (ret < 0) {
                ERROR("reading %s\n", path);
                free(buf);
                return NULL;
        }
+       if (ret >= sz)
+               goto again;
        space = index(buf, '\n');
        if (space)
                *space = '\0';
@@ -93,9 +114,9 @@ again:
        return buf;
 }
 
-static int aa_am_unconfined(void)
+static int apparmor_am_unconfined(void)
 {
-       char *p = aa_get_profile(getpid());
+       char *p = apparmor_process_label_get(getpid());
        int ret = 0;
        if (!p || strcmp(p, "unconfined") == 0)
                ret = 1;
@@ -104,127 +125,55 @@ static int aa_am_unconfined(void)
        return ret;
 }
 
-/* aa_getcon is not working right now.  Use our hand-rolled version below */
-static int check_apparmor_enabled(void)
+/*
+ * apparmor_process_label_set: Set AppArmor process profile
+ *
+ * @label   : the profile to set
+ * @default : use the default profile if label is NULL
+ *
+ * Returns 0 on success, < 0 on failure
+ *
+ * Notes: This relies on /proc being available. The new context
+ * will take effect immediately.
+ */
+static int apparmor_process_label_set(const char *label, int use_default)
 {
-       struct stat statbuf;
-       FILE *fin;
-       char e;
-       int ret;
-
-       ret = stat(AA_MOUNT_RESTR, &statbuf);
-       if (ret != 0)
+       if (!apparmor_enabled())
                return 0;
-       process_lock();
-       fin = fopen(AA_ENABLED_FILE, "r");
-       process_unlock();
-       if (!fin)
-               return 0;
-       ret = fscanf(fin, "%c", &e);
-       process_lock();
-       fclose(fin);
-       process_unlock();
-       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 do_apparmor_load(int aa_enabled, char *aa_profile,
-                                  int umount_proc, int dropprivs)
-{
-       if (!aa_enabled) {
-               INFO("apparmor not enabled");
-               return 0;
+       if (!label) {
+               if (use_default)
+                       label = AA_DEF_PROFILE;
+               else
+                       return 0;
        }
-       INFO("setting up apparmor");
-
-       if (!aa_profile)
-               aa_profile = AA_DEF_PROFILE;
 
-       if (strcmp(aa_profile, "unconfined") == 0 && !dropprivs && aa_am_unconfined()) {
+       if (strcmp(label, "unconfined") == 0 && apparmor_am_unconfined()) {
                INFO("apparmor profile unchanged");
                return 0;
        }
 
-       //if (aa_change_onexec(aa_profile) < 0) {
-       if (aa_change_profile(aa_profile) < 0) {
-               SYSERROR("failed to change apparmor profile to %s", aa_profile);
+       /* XXX: instant instead of aa_change_onexec(), may be used by attach
+        * when using a function that doesn't exec
+        */
+       if (aa_change_profile(label) < 0) {
+               SYSERROR("failed to change apparmor profile to %s", label);
                return -1;
        }
-       if (umount_proc == 1)
-               umount("/proc");
-
-       INFO("changed apparmor profile to %s", aa_profile);
 
+       INFO("changed apparmor profile to %s", label);
        return 0;
 }
 
-extern int apparmor_load(struct lxc_handler *handler)
-{
-       if (!handler->conf->aa_profile)
-               handler->conf->aa_profile = AA_DEF_PROFILE;
-       return do_apparmor_load(handler->aa_enabled,
-                               handler->conf->aa_profile,
-                               handler->conf->lsm_umount_proc, 0);
-}
-
-extern int attach_apparmor(char *profile)
-{
-       if (!profile)
-               return 0;
-       if (!check_apparmor_enabled())
-               return 0;
-       if (strcmp(profile, "unconfined") == 0)
-               return 0;
-       return do_apparmor_load(1, profile, 0, 1);
-}
+static struct lsm_drv apparmor_drv = {
+       .name = "AppArmor",
+       .process_label_get = apparmor_process_label_get,
+       .process_label_set = apparmor_process_label_set,
+};
 
-/*
- * 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)
+struct lsm_drv *lsm_apparmor_drv_init(void)
 {
-       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");
+       if (!apparmor_enabled())
+               return NULL;
+       return &apparmor_drv;
 }
-#endif
diff --git a/src/lxc/lsm/lsm.c b/src/lxc/lsm/lsm.c
new file mode 100644 (file)
index 0000000..3974f11
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * lxc: linux Container library
+ *
+ * Authors:
+ * Copyright © 2012 Serge Hallyn <serge.hallyn@ubuntu.com>
+ * Copyright © 2012 Canonical Ltd.
+ * Dwight Engen <dwight.engen@oracle.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if HAVE_APPARMOR || HAVE_SELINUX
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include <sys/param.h>
+
+#include "conf.h"
+#include "log.h"
+#include "lsm/lsm.h"
+
+lxc_log_define(lxc_lsm, lxc);
+
+static struct lsm_drv *drv = NULL;
+
+extern struct lsm_drv *lsm_apparmor_drv_init(void);
+extern struct lsm_drv *lsm_selinux_drv_init(void);
+extern struct lsm_drv *lsm_nop_drv_init(void);
+
+__attribute__((constructor))
+void lsm_init(void)
+{
+       if (drv) {
+               INFO("LSM security driver %s", drv->name);
+               return;
+       }
+
+       #if HAVE_APPARMOR
+       drv = lsm_apparmor_drv_init();
+       #endif
+       #if HAVE_SELINUX
+       if (!drv)
+               drv = lsm_selinux_drv_init();
+       #endif
+
+       if (!drv)
+               drv = lsm_nop_drv_init();
+       INFO("Initialized LSM security driver %s", drv->name);
+}
+
+char *lsm_process_label_get(pid_t pid)
+{
+       if (!drv) {
+               ERROR("LSM driver not inited");
+               return NULL;
+       }
+       return drv->process_label_get(pid);
+}
+
+int lsm_process_label_set(const char *label, int use_default)
+{
+       if (!drv) {
+               ERROR("LSM driver not inited");
+               return -1;
+       }
+       return drv->process_label_set(label, use_default);
+}
+
+/*
+ * _lsm_mount_proc: Mount /proc inside container to enable
+ * security domain transition
+ *
+ * @rootfs : the rootfs where proc should be mounted
+ *
+ * Returns < 0 on failure, 0 if the correct proc was already mounted
+ * and 1 if a new proc was mounted.
+ */
+static int _lsm_proc_mount(const char *rootfs)
+{
+       char path[MAXPATHLEN];
+       char link[20];
+       int linklen, ret;
+
+       ret = snprintf(path, MAXPATHLEN, "%s/proc/self", rootfs);
+       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'", getpid(), link);
+       ret = snprintf(path, MAXPATHLEN, "%s/proc", rootfs);
+       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 in container for security transition");
+       return 1;
+}
+
+int lsm_proc_mount(struct lxc_conf *lxc_conf)
+{
+       int mounted;
+
+       if (!drv || strcmp(drv->name, "nop") == 0)
+               return 0;
+
+       if (lxc_conf->rootfs.path == NULL || strlen(lxc_conf->rootfs.path) == 0) {
+               if (mount("proc", "/proc", "proc", 0, NULL)) {
+                       SYSERROR("Failed mounting /proc, proceeding");
+                       mounted = 0;
+               } else
+                       mounted = 1;
+       } else
+               mounted = _lsm_proc_mount(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;
+       }
+       return 0;
+}
+
+void lsm_proc_unmount(struct lxc_conf *lxc_conf)
+{
+       if (lxc_conf->lsm_umount_proc == 1) {
+               umount("/proc");
+               lxc_conf->lsm_umount_proc = 0;
+       }
+}
+#endif
diff --git a/src/lxc/lsm/lsm.h b/src/lxc/lsm/lsm.h
new file mode 100644 (file)
index 0000000..2a82c66
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * lxc: linux Container library
+ *
+ * Copyright © 2013 Oracle.
+ *
+ * Authors:
+ * Dwight Engen <dwight.engen@oracle.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __lxc_lsm_h
+#define __lxc_lsm_h
+
+struct lxc_conf;
+
+#include <sys/types.h>
+
+struct lsm_drv {
+       const char *name;
+
+       char *(*process_label_get)(pid_t pid);
+       int   (*process_label_set)(const char *label, int use_default);
+};
+
+#if HAVE_APPARMOR || HAVE_SELINUX
+void  lsm_init(void);
+char *lsm_process_label_get(pid_t pid);
+int   lsm_process_label_set(const char *label, int use_default);
+int   lsm_proc_mount(struct lxc_conf *lxc_conf);
+void  lsm_proc_unmount(struct lxc_conf *lxc_conf);
+#else
+static inline void  lsm_init(void) { }
+static inline char *lsm_process_label_get(pid_t pid) { return NULL; }
+static inline int   lsm_process_label_set(char *label, int use_default) { return 0; }
+static inline int   lsm_proc_mount(struct lxc_conf *lxc_conf) { return 0; }
+static inline void  lsm_proc_unmount(struct lxc_conf *lxc_conf) { }
+#endif
+
+#endif
diff --git a/src/lxc/lsm/nop.c b/src/lxc/lsm/nop.c
new file mode 100644 (file)
index 0000000..9184e6b
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * lxc: linux Container library
+ *
+ * Copyright © 2013 Oracle.
+ *
+ * Authors:
+ * Dwight Engen <dwight.engen@oracle.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include "lsm/lsm.h"
+
+static char *nop_process_label_get(pid_t pid)
+{
+       return NULL;
+}
+
+static int nop_process_label_set(const char *label, int use_default)
+{
+       return 0;
+}
+
+static struct lsm_drv nop_drv = {
+       .name = "nop",
+       .process_label_get = nop_process_label_get,
+       .process_label_set = nop_process_label_set,
+};
+
+struct lsm_drv *lsm_nop_drv_init(void)
+{
+       return &nop_drv;
+}
diff --git a/src/lxc/lsm/selinux.c b/src/lxc/lsm/selinux.c
new file mode 100644 (file)
index 0000000..6e44e8b
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * lxc: linux Container library
+ *
+ * Copyright © 2013 Oracle.
+ *
+ * Authors:
+ * Dwight Engen <dwight.engen@oracle.com>
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <selinux/selinux.h>
+#include "log.h"
+#include "lsm/lsm.h"
+
+#define DEFAULT_LABEL "unconfined_t"
+
+lxc_log_define(lxc_lsm_selinux, lxc);
+
+/*
+ * selinux_process_label_get: Get SELinux context of a process
+ *
+ * @pid     : the pid to get, or 0 for self
+ *
+ * Returns the context of the given pid. The caller must free()
+ * the returned string.
+ *
+ * Note that this relies on /proc being available.
+ */
+static char *selinux_process_label_get(pid_t pid)
+{
+       security_context_t ctx;
+       char *label;
+
+       if (getpidcon_raw(pid, &ctx) < 0) {
+               SYSERROR("failed to get SELinux context for pid %d", pid);
+               return NULL;
+       }
+       label = strdup((char *)ctx);
+       freecon(ctx);
+       return label;
+}
+
+/*
+ * selinux_process_label_set: Set SELinux context of a process
+ *
+ * @label   : the context to set
+ * @default : use the default context if label is NULL
+ *
+ * Returns 0 on success, < 0 on failure
+ *
+ * Notes: This relies on /proc being available. The new context
+ * will take effect on the next exec(2).
+ */
+static int selinux_process_label_set(const char *label, int use_default)
+{
+       if (!label) {
+               if (use_default)
+                       label = DEFAULT_LABEL;
+               else
+                       return -1;
+       }
+       if (!strcmp(label, "unconfined_t"))
+               return 0;
+
+       if (setexeccon_raw((char *)label) < 0) {
+               SYSERROR("failed to set new SELinux context %s", label);
+               return -1;
+       }
+
+       INFO("changed SELinux context to %s", label);
+       return 0;
+}
+
+static struct lsm_drv selinux_drv = {
+       .name = "SELinux",
+       .process_label_get = selinux_process_label_get,
+       .process_label_set = selinux_process_label_set,
+};
+
+struct lsm_drv *lsm_selinux_drv_init(void)
+{
+       if (!is_selinux_enabled())
+               return NULL;
+       return &selinux_drv;
+}
index c968bb13445e600fa3ec7ae2fd9e82411b51fe08..753840358be65e9c7b4591debc604cd3360920cc 100644 (file)
 #include "console.h"
 #include "sync.h"
 #include "namespace.h"
-#include "apparmor.h"
 #include "lxcseccomp.h"
 #include "caps.h"
 #include "lxclock.h"
+#include "lsm/lsm.h"
 
 lxc_log_define(lxc_start, lxc);
 
@@ -285,7 +285,8 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char
        handler->lxcpath = lxcpath;
        handler->pinfd = -1;
 
-       apparmor_handler_init(handler);
+       lsm_init();
+
        handler->name = strdup(name);
        if (!handler->name) {
                ERROR("failed to allocate memory");
@@ -555,8 +556,16 @@ static int do_start(void *data)
        if (lxc_sync_barrier_parent(handler, LXC_SYNC_CGROUP))
                return -1;
 
-       if (apparmor_load(handler) < 0)
+       /* XXX: hmm apparmor switches right away since it uses
+        * aa_change_profile() and not aa_change_onexec(). SELinux on the other
+        * hand is going to transition on exec(). Is it bad to run the stuff
+        * between here and exec() in the more privileged context?
+        */
+       if (lsm_process_label_set(handler->conf->lsm_aa_profile ?
+                                 handler->conf->lsm_aa_profile :
+                                 handler->conf->lsm_se_context, 1) < 0)
                goto out_warn_father;
+       lsm_proc_unmount(handler->conf);
 
        if (lxc_seccomp_load(handler->conf) != 0)
                goto out_warn_father;
index 9bf6024426957fa7f4a5c098494eb3007753678b..c35c5c481d791fa617dd524bcf2139dc1042bc6e 100644 (file)
@@ -50,9 +50,6 @@ struct lxc_handler {
        struct lxc_operations *ops;
        void *data;
        int sv[2];
-#if HAVE_APPARMOR
-       int aa_enabled;
-#endif
        int pinfd;
        const char *lxcpath;
        struct cgroup_process_info *cgroup;
index 98ea609f0a302760664e701e3379ccb041a022b5..c0fcd307832f4be8e3a9b9d9be8e8b5bf6c892be 100644 (file)
@@ -110,10 +110,8 @@ EOF
     # this file has to exist for libvirt/Virtual machine monitor to boot the container
     touch $container_rootfs/etc/mtab
 
-    # don't put devpts in here, it will already be mounted for us by lxc/libvirt
+    # don't put devpts,proc, nor sysfs in here, it will already be mounted for us by lxc/libvirt
     cat <<EOF > $container_rootfs/etc/fstab
-proc    /proc     proc   nodev,noexec,nosuid 0 0
-sysfs   /sys      sysfs  defaults 0 0
 EOF
 
     # remove module stuff for iptables it just shows errors that are not
@@ -404,9 +402,8 @@ lxc.cgroup.devices.allow = c 5:2 rwm        # /dev/ptmx pty master
 EOF
 
     cat <<EOF > $cfg_dir/fstab || die "unable to create $cfg_dir/fstab"
-proc    $container_rootfs/proc     proc   nodev,noexec,nosuid 0 0
-devpts  $container_rootfs/dev/pts  devpts defaults 0 0
-sysfs   $container_rootfs/sys      sysfs  defaults  0 0
+proc    proc     proc   nodev,noexec,nosuid 0 0
+sysfs   sys      sysfs  defaults  0 0
 EOF
 }