Commit fe4de9a6 by Dwight Engen Committed by Serge Hallyn

refactor AppArmor into LSM backend, add SELinux support

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: 's avatarDwight Engen <dwight.engen@oracle.com> Acked-by: 's avatarSerge Hallyn <serge.hallyn@ubuntu.com> Signed-off-by: 's avatarSerge Hallyn <serge.hallyn@ubuntu.com>
parent 3a0abb3a
...@@ -117,6 +117,20 @@ AM_COND_IF([ENABLE_APPARMOR], ...@@ -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_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])]) 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 # Seccomp syscall filter
AC_ARG_ENABLE([seccomp], AC_ARG_ENABLE([seccomp],
[AC_HELP_STRING([--enable-seccomp], [enable seccomp])], [AC_HELP_STRING([--enable-seccomp], [enable seccomp])],
......
...@@ -811,6 +811,31 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ...@@ -811,6 +811,31 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
</refsect2> </refsect2>
<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> <title>Seccomp configuration</title>
<para> <para>
A container can be started with a reduced set of available A container can be started with a reduced set of available
......
...@@ -37,6 +37,18 @@ sodir=$(libdir) ...@@ -37,6 +37,18 @@ sodir=$(libdir)
# use PROGRAMS to avoid complains from automake # use PROGRAMS to avoid complains from automake
so_PROGRAMS = liblxc.so 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 = \ liblxc_so_SOURCES = \
arguments.c arguments.h \ arguments.c arguments.h \
bdev.c bdev.h \ bdev.c bdev.h \
...@@ -73,10 +85,11 @@ liblxc_so_SOURCES = \ ...@@ -73,10 +85,11 @@ liblxc_so_SOURCES = \
af_unix.c af_unix.h \ af_unix.c af_unix.h \
\ \
lxcutmp.c lxcutmp.h \ lxcutmp.c lxcutmp.h \
apparmor.c apparmor.h \
lxclock.h lxclock.c \ lxclock.h lxclock.c \
lxccontainer.c lxccontainer.h \ lxccontainer.c lxccontainer.h \
version.c version.h version.c version.h \
\
$(LSM_SOURCES)
if IS_BIONIC if IS_BIONIC
liblxc_so_SOURCES += \ liblxc_so_SOURCES += \
...@@ -107,6 +120,10 @@ if ENABLE_APPARMOR ...@@ -107,6 +120,10 @@ if ENABLE_APPARMOR
AM_CFLAGS += -DHAVE_APPARMOR AM_CFLAGS += -DHAVE_APPARMOR
endif endif
if ENABLE_SELINUX
AM_CFLAGS += -DHAVE_SELINUX
endif
if HAVE_NEWUIDMAP if HAVE_NEWUIDMAP
AM_CFLAGS += -DHAVE_NEWUIDMAP AM_CFLAGS += -DHAVE_NEWUIDMAP
endif endif
......
/* 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
...@@ -46,11 +46,11 @@ ...@@ -46,11 +46,11 @@
#include "attach.h" #include "attach.h"
#include "caps.h" #include "caps.h"
#include "config.h" #include "config.h"
#include "apparmor.h"
#include "utils.h" #include "utils.h"
#include "commands.h" #include "commands.h"
#include "cgroup.h" #include "cgroup.h"
#include "lxclock.h" #include "lxclock.h"
#include "lsm/lsm.h"
#if HAVE_SYS_PERSONALITY_H #if HAVE_SYS_PERSONALITY_H
#include <sys/personality.h> #include <sys/personality.h>
...@@ -129,7 +129,7 @@ struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid) ...@@ -129,7 +129,7 @@ struct lxc_proc_context_info *lxc_proc_get_context_info(pid_t pid)
errno = ENOENT; errno = ENOENT;
goto out_error; goto out_error;
} }
info->aa_profile = aa_get_profile(pid); info->lsm_label = lsm_process_label_get(pid);
return info; return info;
...@@ -138,6 +138,13 @@ out_error: ...@@ -138,6 +138,13 @@ out_error:
return NULL; 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) int lxc_attach_to_ns(pid_t pid, int which)
{ {
char path[MAXPATHLEN]; char path[MAXPATHLEN];
...@@ -644,8 +651,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun ...@@ -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 " ERROR("failed to automatically determine the "
"namespaces which the container unshared"); "namespaces which the container unshared");
free(cwd); free(cwd);
free(init_ctx->aa_profile); lxc_proc_put_context_info(init_ctx);
free(init_ctx);
return -1; return -1;
} }
} }
...@@ -683,8 +689,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun ...@@ -683,8 +689,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
if (ret < 0) { if (ret < 0) {
SYSERROR("could not set up required IPC mechanism for attaching"); SYSERROR("could not set up required IPC mechanism for attaching");
free(cwd); free(cwd);
free(init_ctx->aa_profile); lxc_proc_put_context_info(init_ctx);
free(init_ctx);
return -1; return -1;
} }
...@@ -705,8 +710,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun ...@@ -705,8 +710,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
if (pid < 0) { if (pid < 0) {
SYSERROR("failed to create first subprocess"); SYSERROR("failed to create first subprocess");
free(cwd); free(cwd);
free(init_ctx->aa_profile); lxc_proc_put_context_info(init_ctx);
free(init_ctx);
return -1; return -1;
} }
...@@ -794,8 +798,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun ...@@ -794,8 +798,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
process_lock(); process_lock();
close(ipc_sockets[0]); close(ipc_sockets[0]);
process_unlock(); process_unlock();
free(init_ctx->aa_profile); lxc_proc_put_context_info(init_ctx);
free(init_ctx);
/* we're done, the child process should now execute whatever /* we're done, the child process should now execute whatever
* it is that the user requested. The parent can now track it * 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 ...@@ -815,8 +818,7 @@ int lxc_attach(const char* name, const char* lxcpath, lxc_attach_exec_t exec_fun
process_unlock(); process_unlock();
if (to_cleanup_pid) if (to_cleanup_pid)
(void) wait_for_pid(to_cleanup_pid); (void) wait_for_pid(to_cleanup_pid);
free(init_ctx->aa_profile); lxc_proc_put_context_info(init_ctx);
free(init_ctx);
return -1; return -1;
} }
...@@ -918,7 +920,7 @@ int attach_child_main(void* data) ...@@ -918,7 +920,7 @@ int attach_child_main(void* data)
/* load apparmor profile */ /* load apparmor profile */
if ((options->namespaces & CLONE_NEWNS) && (options->attach_flags & LXC_ATTACH_APPARMOR)) { 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) { if (ret < 0) {
shutdown(ipc_socket, SHUT_RDWR); shutdown(ipc_socket, SHUT_RDWR);
rexit(-1); rexit(-1);
...@@ -1021,8 +1023,7 @@ int attach_child_main(void* data) ...@@ -1021,8 +1023,7 @@ int attach_child_main(void* data)
shutdown(ipc_socket, SHUT_RDWR); shutdown(ipc_socket, SHUT_RDWR);
close(ipc_socket); close(ipc_socket);
free(init_ctx->aa_profile); lxc_proc_put_context_info(init_ctx);
free(init_ctx);
/* The following is done after the communication socket is /* The following is done after the communication socket is
* shut down. That way, all errors that might (though * shut down. That way, all errors that might (though
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
#include "attach_options.h" #include "attach_options.h"
struct lxc_proc_context_info { struct lxc_proc_context_info {
char *aa_profile; char *lsm_label;
unsigned long personality; unsigned long personality;
unsigned long long capability_mask; unsigned long long capability_mask;
}; };
......
...@@ -74,10 +74,7 @@ ...@@ -74,10 +74,7 @@
#include "bdev.h" #include "bdev.h"
#include "cgroup.h" #include "cgroup.h"
#include "lxclock.h" #include "lxclock.h"
#include "lsm/lsm.h"
#if HAVE_APPARMOR
#include <apparmor.h>
#endif
#if HAVE_SYS_CAPABILITY_H #if HAVE_SYS_CAPABILITY_H
#include <sys/capability.h> #include <sys/capability.h>
...@@ -2398,12 +2395,9 @@ struct lxc_conf *lxc_conf_init(void) ...@@ -2398,12 +2395,9 @@ struct lxc_conf *lxc_conf_init(void)
lxc_list_init(&new->id_map); lxc_list_init(&new->id_map);
for (i=0; i<NUM_LXC_HOOKS; i++) for (i=0; i<NUM_LXC_HOOKS; i++)
lxc_list_init(&new->hooks[i]); lxc_list_init(&new->hooks[i]);
#if HAVE_APPARMOR new->lsm_aa_profile = NULL;
new->aa_profile = NULL; new->lsm_se_context = NULL;
#endif
#if HAVE_APPARMOR /* || HAVE_SMACK || HAVE_SELINUX */
new->lsm_umount_proc = 0; new->lsm_umount_proc = 0;
#endif
return new; return new;
} }
...@@ -3043,10 +3037,6 @@ int uid_shift_ttys(int pid, struct lxc_conf *conf) ...@@ -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) 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)) { if (setup_utsname(lxc_conf->utsname)) {
ERROR("failed to setup the utsname for '%s'", name); ERROR("failed to setup the utsname for '%s'", name);
return -1; return -1;
...@@ -3140,24 +3130,11 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf, const char *lxcpath, ...@@ -3140,24 +3130,11 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf, const char *lxcpath,
return -1; return -1;
} }
#if HAVE_APPARMOR /* || HAVE_SMACK || HAVE_SELINUX */ /* mount /proc if needed for LSM transition */
INFO("rootfs path is .%s., mount is .%s.", lxc_conf->rootfs.path, if (lsm_proc_mount(lxc_conf) < 0) {
lxc_conf->rootfs.mount); ERROR("failed to LSM mount proc for '%s'", name);
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.");
return -1; return -1;
} else if (mounted == 1) {
lxc_conf->lsm_umount_proc = 1;
} }
#endif
if (setup_pivot_root(&lxc_conf->rootfs)) { if (setup_pivot_root(&lxc_conf->rootfs)) {
ERROR("failed to set rootfs for '%s'", name); ERROR("failed to set rootfs for '%s'", name);
...@@ -3488,10 +3465,10 @@ void lxc_conf_free(struct lxc_conf *conf) ...@@ -3488,10 +3465,10 @@ void lxc_conf_free(struct lxc_conf *conf)
if (conf->rcfile) if (conf->rcfile)
free(conf->rcfile); free(conf->rcfile);
lxc_clear_config_network(conf); lxc_clear_config_network(conf);
#if HAVE_APPARMOR if (conf->lsm_aa_profile)
if (conf->aa_profile) free(conf->lsm_aa_profile);
free(conf->aa_profile); if (conf->lsm_se_context)
#endif free(conf->lsm_se_context);
lxc_seccomp_free(conf); lxc_seccomp_free(conf);
lxc_clear_config_caps(conf); lxc_clear_config_caps(conf);
lxc_clear_config_keepcaps(conf); lxc_clear_config_keepcaps(conf);
......
...@@ -247,9 +247,8 @@ enum { ...@@ -247,9 +247,8 @@ enum {
* @tty_info : tty data * @tty_info : tty data
* @console : console data * @console : console data
* @ttydir : directory (under /dev) in which to create console and ttys * @ttydir : directory (under /dev) in which to create console and ttys
#if HAVE_APPARMOR * @lsm_aa_profile : apparmor profile to switch to or NULL
* @aa_profile : apparmor profile to switch to * @lsm_se_context : selinux type to switch to or NULL
#endif
*/ */
enum lxchooks { enum lxchooks {
LXCHOOK_PRESTART, LXCHOOK_PREMOUNT, LXCHOOK_MOUNT, LXCHOOK_AUTODEV, LXCHOOK_PRESTART, LXCHOOK_PREMOUNT, LXCHOOK_MOUNT, LXCHOOK_AUTODEV,
...@@ -285,13 +284,10 @@ struct lxc_conf { ...@@ -285,13 +284,10 @@ struct lxc_conf {
char *ttydir; char *ttydir;
int close_all_fds; int close_all_fds;
struct lxc_list hooks[NUM_LXC_HOOKS]; 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; int lsm_umount_proc;
#endif
char *seccomp; // filename with the seccomp rules char *seccomp; // filename with the seccomp rules
#if HAVE_SCMP_FILTER_CTX #if HAVE_SCMP_FILTER_CTX
scmp_filter_ctx *seccomp_ctx; scmp_filter_ctx *seccomp_ctx;
......
...@@ -57,9 +57,8 @@ static int config_pts(const char *, const char *, struct lxc_conf *); ...@@ -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_tty(const char *, const char *, struct lxc_conf *);
static int config_ttydir(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 *); static int config_kmsg(const char *, const char *, struct lxc_conf *);
#if HAVE_APPARMOR static int config_lsm_aa_profile(const char *, const char *, struct lxc_conf *);
static int config_aa_profile(const char *, const char *, struct lxc_conf *); static int config_lsm_se_context(const char *, const char *, struct lxc_conf *);
#endif
static int config_cgroup(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_idmap(const char *, const char *, struct lxc_conf *);
static int config_loglevel(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[] = { ...@@ -100,9 +99,8 @@ static struct lxc_config_t config[] = {
{ "lxc.tty", config_tty }, { "lxc.tty", config_tty },
{ "lxc.devttydir", config_ttydir }, { "lxc.devttydir", config_ttydir },
{ "lxc.kmsg", config_kmsg }, { "lxc.kmsg", config_kmsg },
#if HAVE_APPARMOR { "lxc.aa_profile", config_lsm_aa_profile },
{ "lxc.aa_profile", config_aa_profile }, { "lxc.se_context", config_lsm_se_context },
#endif
{ "lxc.cgroup", config_cgroup }, { "lxc.cgroup", config_cgroup },
{ "lxc.id_map", config_idmap }, { "lxc.id_map", config_idmap },
{ "lxc.loglevel", config_loglevel }, { "lxc.loglevel", config_loglevel },
...@@ -967,9 +965,8 @@ static int config_kmsg(const char *key, const char *value, ...@@ -967,9 +965,8 @@ static int config_kmsg(const char *key, const char *value,
return 0; return 0;
} }
#if HAVE_APPARMOR static int config_lsm_aa_profile(const char *key, const char *value,
static int config_aa_profile(const char *key, const char *value, struct lxc_conf *lxc_conf)
struct lxc_conf *lxc_conf)
{ {
char *path; char *path;
...@@ -981,13 +978,32 @@ static int config_aa_profile(const char *key, const char *value, ...@@ -981,13 +978,32 @@ static int config_aa_profile(const char *key, const char *value,
return -1; return -1;
} }
if (lxc_conf->aa_profile) if (lxc_conf->lsm_aa_profile)
free(lxc_conf->aa_profile); free(lxc_conf->lsm_aa_profile);
lxc_conf->aa_profile = path; 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; return 0;
} }
#endif
static int config_logfile(const char *key, const char *value, static int config_logfile(const char *key, const char *value,
struct lxc_conf *lxc_conf) struct lxc_conf *lxc_conf)
...@@ -1913,10 +1929,10 @@ int lxc_get_config_item(struct lxc_conf *c, const char *key, char *retv, ...@@ -1913,10 +1929,10 @@ int lxc_get_config_item(struct lxc_conf *c, const char *key, char *retv,
v = c->ttydir; v = c->ttydir;
else if (strcmp(key, "lxc.arch") == 0) else if (strcmp(key, "lxc.arch") == 0)
return lxc_get_arch_entry(c, retv, inlen); return lxc_get_arch_entry(c, retv, inlen);
#if HAVE_APPARMOR
else if (strcmp(key, "lxc.aa_profile") == 0) else if (strcmp(key, "lxc.aa_profile") == 0)
v = c->aa_profile; v = c->lsm_aa_profile;
#endif else if (strcmp(key, "lxc.se_context") == 0)
v = c->lsm_se_context;
else if (strcmp(key, "lxc.logfile") == 0) else if (strcmp(key, "lxc.logfile") == 0)
v = lxc_log_get_file(); v = lxc_log_get_file();
else if (strcmp(key, "lxc.loglevel") == 0) else if (strcmp(key, "lxc.loglevel") == 0)
...@@ -2000,10 +2016,10 @@ void write_config(FILE *fout, struct lxc_conf *c) ...@@ -2000,10 +2016,10 @@ void write_config(FILE *fout, struct lxc_conf *c)
default: break; default: break;
} }
#endif #endif
#if HAVE_APPARMOR if (c->lsm_aa_profile)
if (c->aa_profile) fprintf(fout, "lxc.aa_profile = %s\n", c->lsm_aa_profile);
fprintf(fout, "lxc.aa_profile = %s\n", c->aa_profile); if (c->lsm_se_context)
#endif fprintf(fout, "lxc.se_context = %s\n", c->lsm_se_context);
if (c->loglevel != LXC_LOG_PRIORITY_NOTSET) if (c->loglevel != LXC_LOG_PRIORITY_NOTSET)
fprintf(fout, "lxc.loglevel = %s\n", lxc_log_priority_to_string(c->loglevel)); fprintf(fout, "lxc.loglevel = %s\n", lxc_log_priority_to_string(c->loglevel));
if (c->logfile) if (c->logfile)
......
...@@ -25,22 +25,43 @@ ...@@ -25,22 +25,43 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/mount.h> #include <sys/mount.h>
#include <sys/apparmor.h>
#include "log.h" #include "log.h"
#include "apparmor.h"
#include "lxclock.h" #include "lxclock.h"
#include "lsm/lsm.h"
lxc_log_define(lxc_apparmor, lxc); lxc_log_define(lxc_apparmor, lxc);
#if HAVE_APPARMOR #define AA_DEF_PROFILE "lxc-container-default"
#include <sys/apparmor.h>
#define AA_MOUNT_RESTR "/sys/kernel/security/apparmor/features/mount/mask" #define AA_MOUNT_RESTR "/sys/kernel/security/apparmor/features/mount/mask"
#define AA_ENABLED_FILE "/sys/module/apparmor/parameters/enabled" #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 */ static char *apparmor_process_label_get(pid_t pid)
extern char *aa_get_profile(pid_t pid)
{ {
char path[100], *space; char path[100], *space;
int ret; int ret;
...@@ -65,7 +86,6 @@ again: ...@@ -65,7 +86,6 @@ again:
} }
sz += 1024; sz += 1024;
buf = realloc(buf, sz); buf = realloc(buf, sz);
memset(buf, 0, sz);
if (!buf) { if (!buf) {
ERROR("out of memory"); ERROR("out of memory");
process_lock(); process_lock();
...@@ -73,17 +93,18 @@ again: ...@@ -73,17 +93,18 @@ again:
process_unlock(); process_unlock();
return NULL; return NULL;
} }
memset(buf, 0, sz);
ret = fread(buf, 1, sz - 1, f); ret = fread(buf, 1, sz - 1, f);
process_lock(); process_lock();
fclose(f); fclose(f);
process_unlock(); process_unlock();
if (ret >= sz)
goto again;
if (ret < 0) { if (ret < 0) {
ERROR("reading %s\n", path); ERROR("reading %s\n", path);
free(buf); free(buf);
return NULL; return NULL;
} }
if (ret >= sz)
goto again;
space = index(buf, '\n'); space = index(buf, '\n');
if (space) if (space)
*space = '\0'; *space = '\0';
...@@ -93,9 +114,9 @@ again: ...@@ -93,9 +114,9 @@ again:
return buf; 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; int ret = 0;
if (!p || strcmp(p, "unconfined") == 0) if (!p || strcmp(p, "unconfined") == 0)
ret = 1; ret = 1;
...@@ -104,127 +125,55 @@ static int aa_am_unconfined(void) ...@@ -104,127 +125,55 @@ static int aa_am_unconfined(void)
return ret; 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; if (!apparmor_enabled())
FILE *fin;
char e;
int ret;
ret = stat(AA_MOUNT_RESTR, &statbuf);
if (ret != 0)
return 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;
}
extern void apparmor_handler_init(struct lxc_handler *handler) if (!label) {
{ if (use_default)
handler->aa_enabled = check_apparmor_enabled(); label = AA_DEF_PROFILE;
INFO("aa_enabled set to %d\n", handler->aa_enabled); else
} return 0;
#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()) { if (strcmp(label, "unconfined") == 0 && apparmor_am_unconfined()) {
INFO("apparmor profile unchanged"); INFO("apparmor profile unchanged");
return 0; return 0;
} }
//if (aa_change_onexec(aa_profile) < 0) { /* XXX: instant instead of aa_change_onexec(), may be used by attach
if (aa_change_profile(aa_profile) < 0) { * when using a function that doesn't exec
SYSERROR("failed to change apparmor profile to %s", aa_profile); */
if (aa_change_profile(label) < 0) {
SYSERROR("failed to change apparmor profile to %s", label);
return -1; 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; return 0;
} }
extern int apparmor_load(struct lxc_handler *handler) static struct lsm_drv apparmor_drv = {
{ .name = "AppArmor",
if (!handler->conf->aa_profile) .process_label_get = apparmor_process_label_get,
handler->conf->aa_profile = AA_DEF_PROFILE; .process_label_set = apparmor_process_label_set,
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);
}
/* struct lsm_drv *lsm_apparmor_drv_init(void)
* 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]; if (!apparmor_enabled())
char link[20]; return NULL;
int linklen, ret; return &apparmor_drv;
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
/*
* 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
/*
* 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
/*
* 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;
}
/*
* 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;
}
...@@ -68,10 +68,10 @@ ...@@ -68,10 +68,10 @@
#include "console.h" #include "console.h"
#include "sync.h" #include "sync.h"
#include "namespace.h" #include "namespace.h"
#include "apparmor.h"
#include "lxcseccomp.h" #include "lxcseccomp.h"
#include "caps.h" #include "caps.h"
#include "lxclock.h" #include "lxclock.h"
#include "lsm/lsm.h"
lxc_log_define(lxc_start, lxc); lxc_log_define(lxc_start, lxc);
...@@ -285,7 +285,8 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char ...@@ -285,7 +285,8 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf, const char
handler->lxcpath = lxcpath; handler->lxcpath = lxcpath;
handler->pinfd = -1; handler->pinfd = -1;
apparmor_handler_init(handler); lsm_init();
handler->name = strdup(name); handler->name = strdup(name);
if (!handler->name) { if (!handler->name) {
ERROR("failed to allocate memory"); ERROR("failed to allocate memory");
...@@ -555,8 +556,16 @@ static int do_start(void *data) ...@@ -555,8 +556,16 @@ static int do_start(void *data)
if (lxc_sync_barrier_parent(handler, LXC_SYNC_CGROUP)) if (lxc_sync_barrier_parent(handler, LXC_SYNC_CGROUP))
return -1; 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; goto out_warn_father;
lsm_proc_unmount(handler->conf);
if (lxc_seccomp_load(handler->conf) != 0) if (lxc_seccomp_load(handler->conf) != 0)
goto out_warn_father; goto out_warn_father;
......
...@@ -50,9 +50,6 @@ struct lxc_handler { ...@@ -50,9 +50,6 @@ struct lxc_handler {
struct lxc_operations *ops; struct lxc_operations *ops;
void *data; void *data;
int sv[2]; int sv[2];
#if HAVE_APPARMOR
int aa_enabled;
#endif
int pinfd; int pinfd;
const char *lxcpath; const char *lxcpath;
struct cgroup_process_info *cgroup; struct cgroup_process_info *cgroup;
......
...@@ -110,10 +110,8 @@ EOF ...@@ -110,10 +110,8 @@ EOF
# this file has to exist for libvirt/Virtual machine monitor to boot the container # this file has to exist for libvirt/Virtual machine monitor to boot the container
touch $container_rootfs/etc/mtab 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 cat <<EOF > $container_rootfs/etc/fstab
proc /proc proc nodev,noexec,nosuid 0 0
sysfs /sys sysfs defaults 0 0
EOF EOF
# remove module stuff for iptables it just shows errors that are not # 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 ...@@ -404,9 +402,8 @@ lxc.cgroup.devices.allow = c 5:2 rwm # /dev/ptmx pty master
EOF EOF
cat <<EOF > $cfg_dir/fstab || die "unable to create $cfg_dir/fstab" cat <<EOF > $cfg_dir/fstab || die "unable to create $cfg_dir/fstab"
proc $container_rootfs/proc proc nodev,noexec,nosuid 0 0 proc proc proc nodev,noexec,nosuid 0 0
devpts $container_rootfs/dev/pts devpts defaults 0 0 sysfs sys sysfs defaults 0 0
sysfs $container_rootfs/sys sysfs defaults 0 0
EOF EOF
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment