- fix Oracle template mounting of proc and sysfs, needed when using SELinux
Signed-off-by: Dwight Engen <dwight.en...@oracle.com> --- configure.ac | 14 ++++ doc/lxc.conf.sgml.in | 29 ++++++- src/lxc/Makefile.am | 21 ++++- src/lxc/apparmor.c | 219 ------------------------------------------------ src/lxc/apparmor.h | 56 ------------- src/lxc/attach.c | 31 +++---- src/lxc/attach.h | 2 +- src/lxc/conf.c | 40 ++------- src/lxc/conf.h | 11 +-- src/lxc/confile.c | 32 +++---- src/lxc/lsm/apparmor.c | 168 +++++++++++++++++++++++++++++++++++++ src/lxc/lsm/lsm.c | 154 ++++++++++++++++++++++++++++++++++ src/lxc/lsm/lsm.h | 51 +++++++++++ src/lxc/lsm/nop.c | 45 ++++++++++ src/lxc/lsm/selinux.c | 101 ++++++++++++++++++++++ src/lxc/start.c | 13 ++- src/lxc/start.h | 3 - templates/lxc-oracle.in | 9 +- 18 files changed, 630 insertions(+), 369 deletions(-) delete mode 100644 src/lxc/apparmor.c delete mode 100644 src/lxc/apparmor.h create mode 100644 src/lxc/lsm/apparmor.c create mode 100644 src/lxc/lsm/lsm.c create mode 100644 src/lxc/lsm/lsm.h create mode 100644 src/lxc/lsm/nop.c create mode 100644 src/lxc/lsm/selinux.c diff --git a/configure.ac b/configure.ac index cffbdac..9d77bb5 100644 --- a/configure.ac +++ b/configure.ac @@ -115,6 +115,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])], diff --git a/doc/lxc.conf.sgml.in b/doc/lxc.conf.sgml.in index dc416e8..8991220 100644 --- a/doc/lxc.conf.sgml.in +++ b/doc/lxc.conf.sgml.in @@ -796,7 +796,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA <variablelist> <varlistentry> <term> - <option>lxc.aa_profile</option> + <option>lxc.lsm_label</option> </term> <listitem> <para> @@ -804,7 +804,32 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA be run. To specify that the container should be unconfined, use </para> - <programlisting>lxc.aa_profile = unconfined</programlisting> + <programlisting>lxc.lsm_label = unconfined</programlisting> + </listitem> + </varlistentry> + </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.lsm_label</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.lsm_label = unconfined_u:unconfined_r:lxc_t:s0-s0:c0.c1023</programlisting> </listitem> </varlistentry> </variablelist> diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index f19a994..873b97d 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -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.c b/src/lxc/apparmor.c deleted file mode 100644 index 4dad801..0000000 --- a/src/lxc/apparmor.c +++ /dev/null @@ -1,219 +0,0 @@ -/* apparmor - * - * Copyright © 2012 Serge Hallyn <serge.hal...@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 <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <errno.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/mount.h> - -#include "log.h" -#include "apparmor.h" - -lxc_log_define(lxc_apparmor, lxc); - -#if HAVE_APPARMOR -#include <sys/apparmor.h> - -#define AA_MOUNT_RESTR "/sys/kernel/security/apparmor/features/mount/mask" -#define AA_ENABLED_FILE "/sys/module/apparmor/parameters/enabled" - - -/* caller must free the returned profile */ -extern char *aa_get_profile(pid_t pid) -{ - char path[100], *space; - int ret; - char *buf = NULL; - int sz = 0; - FILE *f; - - ret = snprintf(path, 100, "/proc/%d/attr/current", pid); - if (ret < 0 || ret >= 100) { - ERROR("path name too long"); - return NULL; - } -again: - f = fopen(path, "r"); - if (!f) { - SYSERROR("opening %s\n", path); - if (buf) - free(buf); - return NULL; - } - sz += 1024; - buf = realloc(buf, sz); - memset(buf, 0, sz); - if (!buf) { - ERROR("out of memory"); - fclose(f); - return NULL; - } - ret = fread(buf, 1, sz - 1, f); - fclose(f); - if (ret >= sz) - goto again; - if (ret < 0) { - ERROR("reading %s\n", path); - free(buf); - return NULL; - } - space = index(buf, '\n'); - if (space) - *space = '\0'; - space = index(buf, ' '); - if (space) - *space = '\0'; - return buf; -} - -static int aa_am_unconfined(void) -{ - char *p = aa_get_profile(getpid()); - int ret = 0; - if (!p || strcmp(p, "unconfined") == 0) - ret = 1; - if (p) - free(p); - return ret; -} - -/* 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 do_apparmor_load(int aa_enabled, char *aa_profile, - int umount_proc, int dropprivs) -{ - if (!aa_enabled) { - INFO("apparmor not enabled"); - return 0; - } - INFO("setting up apparmor"); - - if (!aa_profile) - aa_profile = AA_DEF_PROFILE; - - if (strcmp(aa_profile, "unconfined") == 0 && !dropprivs && aa_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); - return -1; - } - if (umount_proc == 1) - umount("/proc"); - - INFO("changed apparmor profile to %s", aa_profile); - - 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); -} - -/* - * 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 deleted file mode 100644 index e27a728..0000000 --- a/src/lxc/apparmor.h +++ /dev/null @@ -1,56 +0,0 @@ -/* apparmor - * - * Copyright © 2012 Serge Hallyn <serge.hal...@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 diff --git a/src/lxc/attach.c b/src/lxc/attach.c index 6f33252..e2f1308 100644 --- a/src/lxc/attach.c +++ b/src/lxc/attach.c @@ -46,10 +46,10 @@ #include "attach.h" #include "caps.h" #include "config.h" -#include "apparmor.h" #include "utils.h" #include "commands.h" #include "cgroup.h" +#include "lsm/lsm.h" #if HAVE_SYS_PERSONALITY_H #include <sys/personality.h> @@ -120,7 +120,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; @@ -129,6 +129,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]; @@ -618,8 +625,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; } } @@ -655,8 +661,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; } @@ -677,8 +682,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; } @@ -762,8 +766,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun /* now shut down communication with child, we're done */ shutdown(ipc_sockets[0], SHUT_RDWR); close(ipc_sockets[0]); - 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 @@ -781,8 +784,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun close(ipc_sockets[0]); 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; } @@ -883,7 +885,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); @@ -986,8 +988,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 diff --git a/src/lxc/attach.h b/src/lxc/attach.h index 518d086..b5f3ea1 100644 --- a/src/lxc/attach.h +++ b/src/lxc/attach.h @@ -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; }; diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 79220d1..fb30fd4 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -73,10 +73,7 @@ #include "caps.h" /* for lxc_caps_last_cap() */ #include "bdev.h" #include "cgroup.h" - -#if HAVE_APPARMOR -#include <apparmor.h> -#endif +#include "lsm/lsm.h" #if HAVE_SYS_CAPABILITY_H #include <sys/capability.h> @@ -2332,12 +2329,8 @@ 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_label = NULL; new->lsm_umount_proc = 0; -#endif return new; } @@ -2968,10 +2961,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; @@ -3065,24 +3054,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); @@ -3413,10 +3389,8 @@ 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_label) + free(conf->lsm_label); lxc_seccomp_free(conf); lxc_clear_config_caps(conf); lxc_clear_config_keepcaps(conf); diff --git a/src/lxc/conf.h b/src/lxc/conf.h index d99bdfe..c05f83a 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -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_label : apparmor profile or selinux type to switch to or NULL if + * no security framework is being used */ enum lxchooks { LXCHOOK_PRESTART, LXCHOOK_PREMOUNT, LXCHOOK_MOUNT, LXCHOOK_AUTODEV, @@ -285,13 +284,9 @@ 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_label; int lsm_umount_proc; -#endif char *seccomp; // filename with the seccomp rules #if HAVE_SCMP_FILTER_CTX scmp_filter_ctx *seccomp_ctx; diff --git a/src/lxc/confile.c b/src/lxc/confile.c index b378c3a..1e1daa7 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -57,9 +57,7 @@ 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_label(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 +98,7 @@ 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.lsm_label", config_lsm_label }, { "lxc.cgroup", config_cgroup }, { "lxc.id_map", config_idmap }, { "lxc.loglevel", config_loglevel }, @@ -967,9 +963,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_label(const char *key, const char *value, + struct lxc_conf *lxc_conf) { char *path; @@ -981,13 +976,12 @@ 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_label) + free(lxc_conf->lsm_label); + lxc_conf->lsm_label = path; return 0; } -#endif static int config_logfile(const char *key, const char *value, struct lxc_conf *lxc_conf) @@ -1913,10 +1907,8 @@ 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 + else if (strcmp(key, "lxc.lsm_label") == 0) + v = c->lsm_label; else if (strcmp(key, "lxc.logfile") == 0) v = lxc_log_get_file(); else if (strcmp(key, "lxc.loglevel") == 0) @@ -2000,10 +1992,8 @@ 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_label) + fprintf(fout, "lxc.lsm_label = %s\n", c->lsm_label); if (c->loglevel != LXC_LOG_PRIORITY_NOTSET) fprintf(fout, "lxc.loglevel = %s\n", lxc_log_priority_to_string(c->loglevel)); if (c->logfile) diff --git a/src/lxc/lsm/apparmor.c b/src/lxc/lsm/apparmor.c new file mode 100644 index 0000000..82839eb --- /dev/null +++ b/src/lxc/lsm/apparmor.c @@ -0,0 +1,168 @@ +/* apparmor + * + * Copyright © 2012 Serge Hallyn <serge.hal...@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 <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mount.h> +#include <sys/apparmor.h> +#include "log.h" +#include "lsm/lsm.h" + +lxc_log_define(lxc_apparmor, lxc); + +#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; + 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; +} + +static char *apparmor_process_label_get(pid_t pid) +{ + char path[100], *space; + int ret; + char *buf = NULL; + int sz = 0; + FILE *f; + + ret = snprintf(path, 100, "/proc/%d/attr/current", pid); + if (ret < 0 || ret >= 100) { + ERROR("path name too long"); + return NULL; + } +again: + f = fopen(path, "r"); + if (!f) { + SYSERROR("opening %s\n", path); + if (buf) + free(buf); + return NULL; + } + sz += 1024; + buf = realloc(buf, sz); + if (!buf) { + ERROR("out of memory"); + fclose(f); + return NULL; + } + memset(buf, 0, sz); + ret = fread(buf, 1, sz - 1, f); + fclose(f); + 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'; + space = index(buf, ' '); + if (space) + *space = '\0'; + return buf; +} + +static int apparmor_am_unconfined(void) +{ + char *p = apparmor_process_label_get(getpid()); + int ret = 0; + if (!p || strcmp(p, "unconfined") == 0) + ret = 1; + if (p) + free(p); + return ret; +} + +/* + * 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) +{ + if (!apparmor_enabled()) + return 0; + + if (!label) { + if (use_default) + label = AA_DEF_PROFILE; + else + return 0; + } + + if (strcmp(label, "unconfined") == 0 && apparmor_am_unconfined()) { + INFO("apparmor profile unchanged"); + return 0; + } + + /* 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; + } + + INFO("changed apparmor profile to %s", label); + return 0; +} + +static struct lsm_drv apparmor_drv = { + .name = "AppArmor", + .process_label_get = apparmor_process_label_get, + .process_label_set = apparmor_process_label_set, +}; + +struct lsm_drv *lsm_apparmor_drv_init(void) +{ + if (!apparmor_enabled()) + return NULL; + return &apparmor_drv; +} diff --git a/src/lxc/lsm/lsm.c b/src/lxc/lsm/lsm.c new file mode 100644 index 0000000..21ad1ad --- /dev/null +++ b/src/lxc/lsm/lsm.c @@ -0,0 +1,154 @@ +/* + * lxc: linux Container library + * + * Authors: + * Copyright © 2012 Serge Hallyn <serge.hal...@ubuntu.com> + * Copyright © 2012 Canonical Ltd. + * Dwight Engen <dwight.en...@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. + */ + +#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 + 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_mount_proc(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_mount_proc(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 index 0000000..e9b9c7f --- /dev/null +++ b/src/lxc/lsm/lsm.h @@ -0,0 +1,51 @@ +/* + * lxc: linux Container library + * + * Copyright © 2013 Oracle. + * + * Authors: + * Dwight Engen <dwight.en...@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. + */ + +#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 index 0000000..61dfbe9 --- /dev/null +++ b/src/lxc/lsm/nop.c @@ -0,0 +1,45 @@ +/* + * lxc: linux Container library + * + * Copyright © 2013 Oracle. + * + * Authors: + * Dwight Engen <dwight.en...@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 <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 index 0000000..6e44e8b --- /dev/null +++ b/src/lxc/lsm/selinux.c @@ -0,0 +1,101 @@ +/* + * lxc: linux Container library + * + * Copyright © 2013 Oracle. + * + * Authors: + * Dwight Engen <dwight.en...@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; +} diff --git a/src/lxc/start.c b/src/lxc/start.c index 48a06cf..2871fbd 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -68,9 +68,9 @@ #include "console.h" #include "sync.h" #include "namespace.h" -#include "apparmor.h" #include "lxcseccomp.h" #include "caps.h" +#include "lsm/lsm.h" lxc_log_define(lxc_start, lxc); @@ -276,7 +276,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"); @@ -534,8 +535,14 @@ 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_label, 1) < 0) goto out_warn_father; + lsm_proc_unmount(handler->conf); if (lxc_seccomp_load(handler->conf) != 0) goto out_warn_father; diff --git a/src/lxc/start.h b/src/lxc/start.h index 9bf6024..c35c5c4 100644 --- a/src/lxc/start.h +++ b/src/lxc/start.h @@ -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; diff --git a/templates/lxc-oracle.in b/templates/lxc-oracle.in index 98ea609..c0fcd30 100644 --- a/templates/lxc-oracle.in +++ b/templates/lxc-oracle.in @@ -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 } -- 1.8.1.4 ------------------------------------------------------------------------------ LIMITED TIME SALE - Full Year of Microsoft Training For Just $49.99! 1,500+ hours of tutorials including VisualStudio 2012, Windows 8, SharePoint 2013, SQL 2012, MVC 4, more. BEST VALUE: New Multi-Library Power Pack includes Mobile, Cloud, Java, and UX Design. Lowest price ever! Ends 9/20/13. http://pubads.g.doubleclick.net/gampad/clk?id=58041151&iu=/4140/ostg.clktrk _______________________________________________ Lxc-devel mailing list Lxc-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/lxc-devel