From: Serge Hallyn Date: Tue, 31 Jul 2012 14:04:33 +0000 (+0200) Subject: Introduce apparmor support X-Git-Tag: lxc-0.8.0~35 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e075f5d9b64175dad6e591e3f1d05a8434c4699f;p=thirdparty%2Flxc.git 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: Serge Hallyn Signed-off-by: Daniel Lezcano --- diff --git a/configure.ac b/configure.ac index 8470e8a49..70c74ec49 100644 --- a/configure.ac +++ b/configure.ac @@ -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], diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am index 26137fc16..50e67bbf2 100644 --- a/src/lxc/Makefile.am +++ b/src/lxc/Makefile.am @@ -53,20 +53,25 @@ liblxc_so_SOURCES = \ mainloop.c mainloop.h \ af_unix.c af_unix.h \ \ - utmp.c utmp.h + utmp.c utmp.h \ + apparmor.c apparmor.h AM_CFLAGS=-I$(top_srcdir)/src \ -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \ -DLXCPATH=\"$(LXCPATH)\" \ -DLXCINITDIR=\"$(LXCINITDIR)\" +if ENABLE_APPARMOR +AM_CFLAGS += -DHAVE_APPARMOR +endif + liblxc_so_CFLAGS = -fPIC -DPIC $(AM_CFLAGS) liblxc_so_LDFLAGS = \ -shared \ -Wl,-soname,liblxc.so.$(firstword $(subst ., ,$(VERSION))) -liblxc_so_LDADD = -lutil $(CAP_LIBS) +liblxc_so_LDADD = -lutil $(CAP_LIBS) $(APPARMOR_LIBS) bin_SCRIPTS = \ lxc-ps \ @@ -105,7 +110,7 @@ AM_LDFLAGS = -Wl,-E if ENABLE_RPATH AM_LDFLAGS += -Wl,-rpath -Wl,$(libdir) endif -LDADD=liblxc.so @CAP_LIBS@ +LDADD=liblxc.so @CAP_LIBS@ @APPARMOR_LIBS@ lxc_attach_SOURCES = lxc_attach.c lxc_cgroup_SOURCES = lxc_cgroup.c diff --git a/src/lxc/apparmor.c b/src/lxc/apparmor.c new file mode 100644 index 000000000..71e847f75 --- /dev/null +++ b/src/lxc/apparmor.c @@ -0,0 +1,134 @@ +#include +#include +#include +#include +#include +#include + +#include "log.h" + +lxc_log_define(lxc_apparmor, lxc); + +#if HAVE_APPARMOR +#include "apparmor.h" +#include + +#define AA_MOUNT_RESTR "/sys/kernel/security/apparmor/features/mount/mask" +#define AA_ENABLED_FILE "/sys/module/apparmor/parameters/enabled" + +static int aa_am_unconfined(void) +{ + int ret; + char path[100], p[100]; + sprintf(path, "/proc/%d/attr/current", getpid()); + FILE *f = fopen(path, "r"); + if (!f) + return 0; + ret = fscanf(f, "%99s", p); + fclose(f); + if (ret < 1) + return 0; + if (strcmp(p, "unconfined") == 0) + return 1; + return 0; +} + +/* aa_getcon is not working right now. Use our hand-rolled version below */ +static int check_apparmor_enabled(void) +{ + struct stat statbuf; + FILE *fin; + char e; + int ret; + + ret = stat(AA_MOUNT_RESTR, &statbuf); + if (ret != 0) + return 0; + fin = fopen(AA_ENABLED_FILE, "r"); + if (!fin) + return 0; + ret = fscanf(fin, "%c", &e); + fclose(fin); + if (ret == 1 && e == 'Y') + return 1; + return 0; +} + +extern void apparmor_handler_init(struct lxc_handler *handler) +{ + handler->aa_enabled = check_apparmor_enabled(); + INFO("aa_enabled set to %d\n", handler->aa_enabled); +} + +#define AA_DEF_PROFILE "lxc-container-default" +extern int apparmor_load(struct lxc_handler *handler) +{ + if (!handler->aa_enabled) { + INFO("apparmor not enabled"); + return 0; + } + INFO("setting up apparmor"); + + if (!handler->conf->aa_profile) + handler->conf->aa_profile = AA_DEF_PROFILE; + + if (strcmp(handler->conf->aa_profile, "unconfined") == 0 && + aa_am_unconfined()) { + INFO("apparmor profile unchanged"); + return 0; + } + + //if (aa_change_onexec(handler->conf->aa_profile) < 0) { + if (aa_change_profile(handler->conf->aa_profile) < 0) { + SYSERROR("failed to change apparmor profile to %s", handler->conf->aa_profile); + return -1; + } + if (handler->conf->lsm_umount_proc == 1) + umount("/proc"); + + INFO("changed apparmor profile to %s", handler->conf->aa_profile); + + return 0; +} + +/* + * this will likely move to a generic lsm.c, as selinux and smack will both + * also want proc mounted in the container so as to transition + */ +extern int lsm_mount_proc_if_needed(char *root_src, char *rootfs_tgt) +{ + char path[MAXPATHLEN]; + char link[20]; + int linklen, ret; + + ret = snprintf(path, MAXPATHLEN, "%s/proc/self", root_src ? rootfs_tgt : ""); + if (ret < 0 || ret >= MAXPATHLEN) { + SYSERROR("proc path name too long"); + return -1; + } + memset(link, 0, 20); + linklen = readlink(path, link, 20); + INFO("I am %d, /proc/self points to %s\n", getpid(), link); + ret = snprintf(path, MAXPATHLEN, "%s/proc", root_src ? rootfs_tgt : ""); + if (linklen < 0) /* /proc not mounted */ + goto domount; + /* can't be longer than rootfs/proc/1 */ + if (strncmp(link, "1", linklen) != 0) { + /* wrong /procs mounted */ + umount2(path, MNT_DETACH); /* ignore failure */ + goto domount; + } + /* the right proc is already mounted */ + return 0; + +domount: + if (mount("proc", path, "proc", 0, NULL)) + return -1; + INFO("Mounted /proc for the container\n"); + return 1; +} +#else +extern void apparmor_handler_init(struct lxc_handler *handler) { + INFO("apparmor_load - apparmor is disabled"); +} +#endif diff --git a/src/lxc/apparmor.h b/src/lxc/apparmor.h new file mode 100644 index 000000000..87a6e545d --- /dev/null +++ b/src/lxc/apparmor.h @@ -0,0 +1,16 @@ +#include /* for lxc_handler */ +#include + +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 diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 601f4bd80..724a26c74 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -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; diff --git a/src/lxc/conf.h b/src/lxc/conf.h index 0d8f28ed8..0c1f5c317 100644 --- a/src/lxc/conf.h +++ b/src/lxc/conf.h @@ -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 }; /* diff --git a/src/lxc/confile.c b/src/lxc/confile.c index b305aef1c..09902ba4b 100644 --- a/src/lxc/confile.c +++ b/src/lxc/confile.c @@ -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."; diff --git a/src/lxc/start.c b/src/lxc/start.c index 293853757..fffa4cd2c 100644 --- a/src/lxc/start.c +++ b/src/lxc/start.c @@ -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 diff --git a/src/lxc/start.h b/src/lxc/start.h index 016d3ee55..0e12abaed 100644 --- a/src/lxc/start.h +++ b/src/lxc/start.h @@ -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 *);