Commit e075f5d9 by Serge Hallyn Committed by Daniel Lezcano

Introduce apparmor support

This could be done as generic 'lsm_init()' and 'lsm_load()' functions, however that would make it impossible to compile one package supporting more than one lsm. If we explicitly add the selinux, smack, and aa hooks in the source, then one package can be built to support multiple kernels. The smack support should be pretty trivial, and probably very close to the apparmor support. The selinux support may require more, including labeling the passed-in fds (consoles etc) and filesystems. If someone on the list has the inclination and experience to add selinux support, please let me know. Otherwise, I'll do Smack and SELinux. Signed-off-by: 's avatarSerge Hallyn <serge.hallyn@ubuntu.com> Signed-off-by: 's avatarDaniel Lezcano <dlezcano@fr.ibm.com>
parent 72be4f89
......@@ -18,6 +18,11 @@ AC_ARG_ENABLE([rpath],
AM_CONDITIONAL([ENABLE_RPATH], [test "x$enable_rpath" = "xyes"])
AC_ARG_ENABLE([apparmor],
[AC_HELP_STRING([--enable-apparmor], [enable apparmor])],
[], [enable_apparmor=yes])
AM_CONDITIONAL([ENABLE_APPARMOR], [test "x$enable_apparmor" = "xyes"])
AC_ARG_ENABLE([doc],
[AC_HELP_STRING([--enable-doc], [make mans (require docbook2man installed) [default=auto]])],
[], [enable_doc=auto])
......@@ -29,6 +34,11 @@ if test "x$enable_doc" = "xyes" -o "x$enable_doc" = "xauto"; then
AC_MSG_ERROR([docbook2man required by man request, but not found])
fi
AM_COND_IF([ENABLE_APPARMOR],
[AC_CHECK_HEADER([sys/apparmor.h],[],[AC_MSG_ERROR([You must install the AppArmor development package in order to compile lxc])])
AC_CHECK_LIB([apparmor], [aa_change_profile],[],[AC_MSG_ERROR([You must install the AppArmor development package in order to compile lxc])])
AC_SUBST([APPARMOR_LIBS], [-lapparmor])])
AM_CONDITIONAL([ENABLE_DOCBOOK], [test "x$have_docbook" = "xyes"])
AC_ARG_ENABLE([examples],
......
......@@ -53,20 +53,25 @@ liblxc_so_SOURCES = \
mainloop.c mainloop.h \
af_unix.c af_unix.h \
\
utmp.c utmp.h
utmp.c utmp.h \
apparmor.c apparmor.h
AM_CFLAGS=-I$(top_srcdir)/src \
-DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
-DLXCPATH=\"$(LXCPATH)\" \
-DLXCINITDIR=\"$(LXCINITDIR)\"
if ENABLE_APPARMOR
AM_CFLAGS += -DHAVE_APPARMOR
endif
liblxc_so_CFLAGS = -fPIC -DPIC $(AM_CFLAGS)
liblxc_so_LDFLAGS = \
-shared \
-Wl,-soname,liblxc.so.$(firstword $(subst ., ,$(VERSION)))
liblxc_so_LDADD = -lutil $(CAP_LIBS)
liblxc_so_LDADD = -lutil $(CAP_LIBS) $(APPARMOR_LIBS)
bin_SCRIPTS = \
lxc-ps \
......@@ -105,7 +110,7 @@ AM_LDFLAGS = -Wl,-E
if ENABLE_RPATH
AM_LDFLAGS += -Wl,-rpath -Wl,$(libdir)
endif
LDADD=liblxc.so @CAP_LIBS@
LDADD=liblxc.so @CAP_LIBS@ @APPARMOR_LIBS@
lxc_attach_SOURCES = lxc_attach.c
lxc_cgroup_SOURCES = lxc_cgroup.c
......
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include "log.h"
lxc_log_define(lxc_apparmor, lxc);
#if HAVE_APPARMOR
#include "apparmor.h"
#include <sys/apparmor.h>
#define AA_MOUNT_RESTR "/sys/kernel/security/apparmor/features/mount/mask"
#define AA_ENABLED_FILE "/sys/module/apparmor/parameters/enabled"
static int aa_am_unconfined(void)
{
int ret;
char path[100], p[100];
sprintf(path, "/proc/%d/attr/current", getpid());
FILE *f = fopen(path, "r");
if (!f)
return 0;
ret = fscanf(f, "%99s", p);
fclose(f);
if (ret < 1)
return 0;
if (strcmp(p, "unconfined") == 0)
return 1;
return 0;
}
/* aa_getcon is not working right now. Use our hand-rolled version below */
static int check_apparmor_enabled(void)
{
struct stat statbuf;
FILE *fin;
char e;
int ret;
ret = stat(AA_MOUNT_RESTR, &statbuf);
if (ret != 0)
return 0;
fin = fopen(AA_ENABLED_FILE, "r");
if (!fin)
return 0;
ret = fscanf(fin, "%c", &e);
fclose(fin);
if (ret == 1 && e == 'Y')
return 1;
return 0;
}
extern void apparmor_handler_init(struct lxc_handler *handler)
{
handler->aa_enabled = check_apparmor_enabled();
INFO("aa_enabled set to %d\n", handler->aa_enabled);
}
#define AA_DEF_PROFILE "lxc-container-default"
extern int apparmor_load(struct lxc_handler *handler)
{
if (!handler->aa_enabled) {
INFO("apparmor not enabled");
return 0;
}
INFO("setting up apparmor");
if (!handler->conf->aa_profile)
handler->conf->aa_profile = AA_DEF_PROFILE;
if (strcmp(handler->conf->aa_profile, "unconfined") == 0 &&
aa_am_unconfined()) {
INFO("apparmor profile unchanged");
return 0;
}
//if (aa_change_onexec(handler->conf->aa_profile) < 0) {
if (aa_change_profile(handler->conf->aa_profile) < 0) {
SYSERROR("failed to change apparmor profile to %s", handler->conf->aa_profile);
return -1;
}
if (handler->conf->lsm_umount_proc == 1)
umount("/proc");
INFO("changed apparmor profile to %s", handler->conf->aa_profile);
return 0;
}
/*
* this will likely move to a generic lsm.c, as selinux and smack will both
* also want proc mounted in the container so as to transition
*/
extern int lsm_mount_proc_if_needed(char *root_src, char *rootfs_tgt)
{
char path[MAXPATHLEN];
char link[20];
int linklen, ret;
ret = snprintf(path, MAXPATHLEN, "%s/proc/self", root_src ? rootfs_tgt : "");
if (ret < 0 || ret >= MAXPATHLEN) {
SYSERROR("proc path name too long");
return -1;
}
memset(link, 0, 20);
linklen = readlink(path, link, 20);
INFO("I am %d, /proc/self points to %s\n", getpid(), link);
ret = snprintf(path, MAXPATHLEN, "%s/proc", root_src ? rootfs_tgt : "");
if (linklen < 0) /* /proc not mounted */
goto domount;
/* can't be longer than rootfs/proc/1 */
if (strncmp(link, "1", linklen) != 0) {
/* wrong /procs mounted */
umount2(path, MNT_DETACH); /* ignore failure */
goto domount;
}
/* the right proc is already mounted */
return 0;
domount:
if (mount("proc", path, "proc", 0, NULL))
return -1;
INFO("Mounted /proc for the container\n");
return 1;
}
#else
extern void apparmor_handler_init(struct lxc_handler *handler) {
INFO("apparmor_load - apparmor is disabled");
}
#endif
#include <lxc/start.h> /* for lxc_handler */
#include <lxc/conf.h>
struct lxc_handler;
#if HAVE_APPARMOR
extern int apparmor_load(struct lxc_handler *handler);
extern int lsm_mount_proc_if_needed(char *root_src, char *rootfs_tgt);
extern void apparmor_handler_init(struct lxc_handler *handler);
#else
extern int apparmor_load(struct lxc_handler *handler);
static inline int lsm_mount_proc_if_needed(char *root_src, char *rootfs_tgt) {
return 0;
}
extern void apparmor_handler_init(struct lxc_handler *handler) { }
#endif
......@@ -1636,6 +1636,12 @@ struct lxc_conf *lxc_conf_init(void)
lxc_list_init(&new->network);
lxc_list_init(&new->mount_list);
lxc_list_init(&new->caps);
#if HAVE_APPARMOR
new->aa_profile = NULL;
#endif
#if HAVE_APPARMOR /* || HAVE_SMACK || HAVE_SELINUX */
new->lsm_umount_proc = 0;
#endif
return new;
}
......@@ -2032,6 +2038,10 @@ void lxc_delete_tty(struct lxc_tty_info *tty_info)
int lxc_setup(const char *name, struct lxc_conf *lxc_conf)
{
#if HAVE_APPARMOR /* || HAVE_SMACK || HAVE_SELINUX */
int mounted;
#endif
if (setup_utsname(lxc_conf->utsname)) {
ERROR("failed to setup the utsname for '%s'", name);
return -1;
......@@ -2072,6 +2082,16 @@ int lxc_setup(const char *name, struct lxc_conf *lxc_conf)
return -1;
}
#if HAVE_APPARMOR /* || HAVE_SMACK || HAVE_SELINUX */
mounted = lsm_mount_proc_if_needed(lxc_conf->rootfs.path, lxc_conf->rootfs.mount);
if (mounted == -1) {
SYSERROR("failed to mount /proc in the container.");
return -1;
} else if (mounted == 1) {
lxc_conf->lsm_umount_proc = 1;
}
#endif
if (setup_pivot_root(&lxc_conf->rootfs)) {
ERROR("failed to set rootfs for '%s'", name);
return -1;
......
......@@ -198,6 +198,9 @@ struct lxc_rootfs {
* @tty_info : tty data
* @console : console data
* @ttydir : directory (under /dev) in which to create console and ttys
#if HAVE_APPARMOR
* @aa_profile : apparmor profile to switch to
#endif
*/
struct lxc_conf {
char *fstab;
......@@ -216,6 +219,12 @@ struct lxc_conf {
struct lxc_rootfs rootfs;
char *ttydir;
int close_all_fds;
#if HAVE_APPARMOR
char *aa_profile;
#endif
#if HAVE_APPARMOR /* || HAVE_SELINUX || HAVE_SMACK */
int lsm_umount_proc;
#endif
};
/*
......
......@@ -49,6 +49,9 @@ static int config_personality(const char *, char *, struct lxc_conf *);
static int config_pts(const char *, char *, struct lxc_conf *);
static int config_tty(const char *, char *, struct lxc_conf *);
static int config_ttydir(const char *, char *, struct lxc_conf *);
#if HAVE_APPARMOR
static int config_aa_profile(const char *, char *, struct lxc_conf *);
#endif
static int config_cgroup(const char *, char *, struct lxc_conf *);
static int config_mount(const char *, char *, struct lxc_conf *);
static int config_rootfs(const char *, char *, struct lxc_conf *);
......@@ -85,6 +88,9 @@ static struct config config[] = {
{ "lxc.pts", config_pts },
{ "lxc.tty", config_tty },
{ "lxc.devttydir", config_ttydir },
#if HAVE_APPARMOR
{ "lxc.aa_profile", config_aa_profile },
#endif
{ "lxc.cgroup", config_cgroup },
{ "lxc.mount", config_mount },
{ "lxc.rootfs.mount", config_rootfs_mount },
......@@ -633,6 +639,25 @@ static int config_ttydir(const char *key, char *value,
return 0;
}
#if HAVE_APPARMOR
static int config_aa_profile(const char *key, char *value, struct lxc_conf *lxc_conf)
{
char *path;
if (!value || strlen(value) == 0)
return 0;
path = strdup(value);
if (!path) {
SYSERROR("failed to strdup '%s': %m", value);
return -1;
}
lxc_conf->aa_profile = path;
return 0;
}
#endif
static int config_cgroup(const char *key, char *value, struct lxc_conf *lxc_conf)
{
char *token = "lxc.cgroup.";
......
......@@ -126,6 +126,7 @@ int signalfd(int fd, const sigset_t *mask, int flags)
#include "console.h"
#include "sync.h"
#include "namespace.h"
#include "apparmor.h"
lxc_log_define(lxc_start, lxc);
......@@ -345,6 +346,7 @@ struct lxc_handler *lxc_init(const char *name, struct lxc_conf *conf)
handler->conf = conf;
apparmor_handler_init(handler);
handler->name = strdup(name);
if (!handler->name) {
ERROR("failed to allocate memory");
......@@ -517,6 +519,9 @@ static int do_start(void *data)
goto out_warn_father;
}
if (apparmor_load(handler) < 0)
goto out_warn_father;
close(handler->sigfd);
/* after this call, we are in error because this
......
......@@ -45,6 +45,9 @@ struct lxc_handler {
struct lxc_operations *ops;
void *data;
int sv[2];
#if HAVE_APPARMOR
int aa_enabled;
#endif
};
extern struct lxc_handler *lxc_init(const char *name, struct lxc_conf *);
......
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