PACKAGE=lxc-pve
-LXCVER=3.0.1+pve2
+LXCVER=3.0.2+pve1
DEBREL=1
SRCDIR=lxc
+lxc (3.0.2+pve1-1) unstable; urgency=medium
+
+ * update to lxc-3.0.2
+
+ -- Proxmox Support Team <support@proxmox.com> Mon, 20 Aug 2018 11:51:27 +0200
+
lxc (3.0.1+pve2-1) unstable; urgency=medium
* fix CVE-2018-6556
5 files changed, 64 insertions(+)
diff --git a/doc/lxc.container.conf.sgml.in b/doc/lxc.container.conf.sgml.in
-index 4ac26058..3e9e4e03 100644
+index 8caee9ee..2d85ab7f 100644
--- a/doc/lxc.container.conf.sgml.in
+++ b/doc/lxc.container.conf.sgml.in
-@@ -2380,6 +2380,21 @@ dev/null proc/kcore none bind,relative 0 0
+@@ -2382,6 +2382,21 @@ dev/null proc/kcore none bind,relative 0 0
</varlistentry>
<varlistentry>
<term>
</term>
<listitem>
diff --git a/src/lxc/conf.c b/src/lxc/conf.c
-index c5d6f5b1..d36987c8 100644
+index 85ae6cd2..4dd063e6 100644
--- a/src/lxc/conf.c
+++ b/src/lxc/conf.c
-@@ -2683,6 +2683,7 @@ struct lxc_conf *lxc_conf_init(void)
+@@ -2707,6 +2707,7 @@ struct lxc_conf *lxc_conf_init(void)
new->console.name[0] = '\0';
memset(&new->console.ringbuf, 0, sizeof(struct lxc_ringbuf));
new->maincmd_fd = -1;
new->rootfs.mount = strdup(default_rootfs_mount);
if (!new->rootfs.mount) {
diff --git a/src/lxc/conf.h b/src/lxc/conf.h
-index ea3a71df..f7a879c3 100644
+index d87b3347..1c029fea 100644
--- a/src/lxc/conf.h
+++ b/src/lxc/conf.h
-@@ -303,6 +303,7 @@ struct lxc_conf {
+@@ -307,6 +307,7 @@ struct lxc_conf {
/* unshare the mount namespace in the monitor */
unsigned int monitor_unshare;
/* list of included files */
struct lxc_list includes;
diff --git a/src/lxc/confile.c b/src/lxc/confile.c
-index 4f46d7bf..8a7505da 100644
+index f4a01b04..3bc86790 100644
--- a/src/lxc/confile.c
+++ b/src/lxc/confile.c
@@ -111,6 +111,7 @@ lxc_config_define(log_file);
static int set_config_group(const char *key, const char *value,
struct lxc_conf *lxc_conf, void *data)
{
-@@ -3406,6 +3430,13 @@ static int get_config_monitor(const char *key, char *retv, int inlen,
+@@ -3413,6 +3437,13 @@ static int get_config_monitor(const char *key, char *retv, int inlen,
return lxc_get_conf_int(c, retv, inlen, c->monitor_unshare);
}
static int get_config_group(const char *key, char *retv, int inlen,
struct lxc_conf *c, void *data)
{
-@@ -3957,6 +3988,13 @@ static inline int clr_config_monitor(const char *key, struct lxc_conf *c,
+@@ -3968,6 +3999,13 @@ static inline int clr_config_monitor(const char *key, struct lxc_conf *c,
return 0;
}
void *data)
{
diff --git a/src/lxc/start.c b/src/lxc/start.c
-index bccd5807..3343f9bf 100644
+index 827a9ee9..f67e3f66 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
-@@ -1383,6 +1383,15 @@ static int do_start(void *data)
+@@ -1385,6 +1385,15 @@ static int do_start(void *data)
goto out_warn_father;
}
1 file changed, 19 insertions(+)
diff --git a/doc/ja/lxc.container.conf.sgml.in b/doc/ja/lxc.container.conf.sgml.in
-index ee93f3bf..7aee768b 100644
+index 8834cc64..e35005c6 100644
--- a/doc/ja/lxc.container.conf.sgml.in
+++ b/doc/ja/lxc.container.conf.sgml.in
-@@ -3158,6 +3158,25 @@ by KATOH Yasufumi <karma at jazz.email.ne.jp>
+@@ -3161,6 +3161,25 @@ by KATOH Yasufumi <karma at jazz.email.ne.jp>
</varlistentry>
<varlistentry>
<term>
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Wed, 25 Jul 2018 12:11:31 +0200
+Subject: [PATCH] apparmor: profile generation
+
+This copies lxd's apparmor profile generation. This tries to
+detect features such as cgroup namespaces, apparmor
+namespaces and stacking support, and has profile parts
+conditionally for unprivileged containers.
+
+This introduces the following changes to the configuration:
+ lxc.apparmor.profile = generated
+ The fixed value 'generated' will cause this
+ functionality to be used, otherwise there should be no
+ functional changes happening unless specifically
+ requested with the next key:
+ lxc.apparmor.allow_nesting
+ This is a boolean which, if enabled, causes the
+ following changes: When generated apparmor profiles are
+ used, they will contain the necessary changes to allow
+ creating a nested container. In addition to the usual
+ mount points, /dev/.lxc/proc and /dev/.lxc/sys will
+ contain procfs and sysfs mount points without the lxcfs
+ overlays, which, if generated apparmor profiles are
+ being used, will not be read/writable directly.
+ lxc.apparmor.raw
+ A list of raw apparmor profile lines to append to the
+ profile. Only valid when using generated profiles.
+
+The following apparmor profile lines have not been copied
+from lxd:
+
+ mount /var/lib/lxd/shmounts/ -> /var/lib/lxd/shmounts/,
+ mount none -> /var/lib/lxd/shmounts/,
+ mount options=bind /var/lib/lxd/shmounts/** -> /var/lib/lxd/**,
+
+They should be added via lxc.apparmor.raw entries by lxd.
+
+In order for apparmor_parser's cache to be of use, this adds
+a --with-apparmor-cache-dir ./configure option.
+
+Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
+(cherry picked from commit 1800f9247357948fd11c9da73b1943a8a7b6882b)
+---
+ configure.ac | 8 +
+ src/lxc/Makefile.am | 1 +
+ src/lxc/conf.c | 43 ++-
+ src/lxc/conf.h | 8 +-
+ src/lxc/confile.c | 95 +++++
+ src/lxc/criu.c | 3 +-
+ src/lxc/lsm/apparmor.c | 974 ++++++++++++++++++++++++++++++++++++++++++++++---
+ src/lxc/lsm/lsm.c | 30 +-
+ src/lxc/lsm/lsm.h | 8 +-
+ src/lxc/lsm/nop.c | 2 +-
+ src/lxc/lsm/selinux.c | 4 +-
+ src/lxc/start.c | 14 +-
+ 12 files changed, 1134 insertions(+), 56 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index bcf7ab64..71fe63b0 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -469,6 +469,13 @@ AC_ARG_WITH([cgroup-pattern],
+ [pattern for container cgroups]
+ )], [], [with_cgroup_pattern=['lxc/%n']])
+
++# The path for the apparmor_parser's cache for generated apparmor profiles
++AC_ARG_WITH([apparmor-cache-dir],
++ [AC_HELP_STRING(
++ [--with-apparmor-cache-dir=dir],
++ [path for apparmor_parser cache]
++ )], [], [with_apparmor_cache_dir=['${localstatedir}/cache/lxc/apparmor']])
++
+ # Container log path. By default, use $lxcpath.
+ AC_MSG_CHECKING([Whether to place logfiles in container config path])
+ AC_ARG_ENABLE([configpath-log],
+@@ -515,6 +522,7 @@ AS_AC_EXPAND(LXCBINHOOKDIR, "$libexecdir/lxc/hooks")
+ AS_AC_EXPAND(LXCINITDIR, "$libexecdir")
+ AS_AC_EXPAND(LOGPATH, "$with_log_path")
+ AS_AC_EXPAND(RUNTIME_PATH, "$with_runtime_path")
++AS_AC_EXPAND(APPARMOR_CACHE_DIR, "$with_apparmor_cache_dir")
+ AC_SUBST(DEFAULT_CGROUP_PATTERN, ["$with_cgroup_pattern"])
+
+ # We need the install path so criu knows where to reference the hook scripts.
+diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
+index c3f11176..14db7cb4 100644
+--- a/src/lxc/Makefile.am
++++ b/src/lxc/Makefile.am
+@@ -176,6 +176,7 @@ AM_CFLAGS = -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
+ -DDEFAULT_CGROUP_PATTERN=\"$(DEFAULT_CGROUP_PATTERN)\" \
+ -DRUNTIME_PATH=\"$(RUNTIME_PATH)\" \
+ -DSBINDIR=\"$(SBINDIR)\" \
++ -DAPPARMOR_CACHE_DIR=\"$(APPARMOR_CACHE_DIR)\" \
+ -I $(top_srcdir)/src \
+ -I $(top_srcdir)/src/lxc \
+ -I $(top_srcdir)/src/lxc/storage \
+diff --git a/src/lxc/conf.c b/src/lxc/conf.c
+index 4dd063e6..8c9dce36 100644
+--- a/src/lxc/conf.c
++++ b/src/lxc/conf.c
+@@ -2356,7 +2356,23 @@ static int setup_mount(const struct lxc_conf *conf,
+ return ret;
+ }
+
+-FILE *make_anonymous_mount_file(struct lxc_list *mount)
++/*
++ * In order for nested containers to be able to mount /proc and /sys they need
++ * to see a "pure" proc and sysfs mount points with nothing mounted on top
++ * (like lxcfs).
++ * For this we provide proc and sysfs in /dev/.lxc/{proc,sys} while using an
++ * apparmor rule to deny access to them. This is mostly for convenience: The
++ * container's root user can mount them anyway and thus has access to the two
++ * file systems. But a non-root user in the container should not be allowed to
++ * access them as a side effect without explicitly allowing it.
++ */
++static const char nesting_helpers[] =
++"proc dev/.lxc/proc proc create=dir,optional\n"
++"sys dev/.lxc/sys sysfs create=dir,optional\n"
++;
++
++FILE *make_anonymous_mount_file(struct lxc_list *mount,
++ bool include_nesting_helpers)
+ {
+ int ret;
+ char *mount_entry;
+@@ -2398,6 +2414,13 @@ FILE *make_anonymous_mount_file(struct lxc_list *mount)
+ goto on_error;
+ }
+
++ if (include_nesting_helpers) {
++ ret = lxc_write_nointr(fd, nesting_helpers,
++ sizeof(nesting_helpers) - 1);
++ if (ret != sizeof(nesting_helpers) - 1)
++ goto on_error;
++ }
++
+ ret = lseek(fd, 0, SEEK_SET);
+ if (ret < 0)
+ goto on_error;
+@@ -2418,7 +2441,7 @@ static int setup_mount_entries(const struct lxc_conf *conf,
+ int ret;
+ FILE *f;
+
+- f = make_anonymous_mount_file(mount);
++ f = make_anonymous_mount_file(mount, conf->lsm_aa_allow_nesting);
+ if (!f)
+ return -1;
+
+@@ -2736,6 +2759,7 @@ struct lxc_conf *lxc_conf_init(void)
+ lxc_list_init(&new->groups);
+ lxc_list_init(&new->state_clients);
+ new->lsm_aa_profile = NULL;
++ lxc_list_init(&new->lsm_aa_raw);
+ new->lsm_se_context = NULL;
+ new->tmp_umount_proc = false;
+
+@@ -4021,6 +4045,19 @@ void lxc_clear_includes(struct lxc_conf *conf)
+ }
+ }
+
++int lxc_clear_apparmor_raw(struct lxc_conf *c)
++{
++ struct lxc_list *it, *next;
++
++ lxc_list_for_each_safe (it, &c->lsm_aa_raw, next) {
++ lxc_list_del(it);
++ free(it->elem);
++ free(it);
++ }
++
++ return 0;
++}
++
+ void lxc_conf_free(struct lxc_conf *conf)
+ {
+ if (!conf)
+@@ -4048,6 +4085,7 @@ void lxc_conf_free(struct lxc_conf *conf)
+ free(conf->syslog);
+ lxc_free_networks(&conf->network);
+ free(conf->lsm_aa_profile);
++ free(conf->lsm_aa_profile_computed);
+ free(conf->lsm_se_context);
+ lxc_seccomp_free(conf);
+ lxc_clear_config_caps(conf);
+@@ -4064,6 +4102,7 @@ void lxc_conf_free(struct lxc_conf *conf)
+ lxc_clear_limits(conf, "lxc.prlimit");
+ lxc_clear_sysctls(conf, "lxc.sysctl");
+ lxc_clear_procs(conf, "lxc.proc");
++ lxc_clear_apparmor_raw(conf);
+ free(conf->cgroup_meta.dir);
+ free(conf->cgroup_meta.controllers);
+ free(conf);
+diff --git a/src/lxc/conf.h b/src/lxc/conf.h
+index 1c029fea..f9864b35 100644
+--- a/src/lxc/conf.h
++++ b/src/lxc/conf.h
+@@ -276,7 +276,11 @@ struct lxc_conf {
+ };
+
+ char *lsm_aa_profile;
++ char *lsm_aa_profile_computed;
++ bool lsm_aa_profile_created;
++ unsigned int lsm_aa_allow_nesting;
+ unsigned int lsm_aa_allow_incomplete;
++ struct lxc_list lsm_aa_raw;
+ char *lsm_se_context;
+ bool tmp_umount_proc;
+ char *seccomp; /* filename with the seccomp rules */
+@@ -422,7 +426,8 @@ extern int parse_propagationopts(const char *mntopts, unsigned long *pflags);
+ extern void tmp_proc_unmount(struct lxc_conf *lxc_conf);
+ extern void remount_all_slave(void);
+ extern void suggest_default_idmap(void);
+-extern FILE *make_anonymous_mount_file(struct lxc_list *mount);
++extern FILE *make_anonymous_mount_file(struct lxc_list *mount,
++ bool include_nesting_helpers);
+ extern struct lxc_list *sort_cgroup_settings(struct lxc_list *cgroup_settings);
+ extern unsigned long add_required_remount_flags(const char *s, const char *d,
+ unsigned long flags);
+@@ -436,5 +441,6 @@ extern int setup_sysctl_parameters(struct lxc_list *sysctls);
+ extern int lxc_clear_sysctls(struct lxc_conf *c, const char *key);
+ extern int setup_proc_filesystem(struct lxc_list *procs, pid_t pid);
+ extern int lxc_clear_procs(struct lxc_conf *c, const char *key);
++extern int lxc_clear_apparmor_raw(struct lxc_conf *c);
+
+ #endif /* __LXC_CONF_H */
+diff --git a/src/lxc/confile.c b/src/lxc/confile.c
+index 3bc86790..ec795aa4 100644
+--- a/src/lxc/confile.c
++++ b/src/lxc/confile.c
+@@ -84,7 +84,9 @@ lxc_log_define(confile, lxc);
+
+ lxc_config_define(autodev);
+ lxc_config_define(apparmor_allow_incomplete);
++lxc_config_define(apparmor_allow_nesting);
+ lxc_config_define(apparmor_profile);
++lxc_config_define(apparmor_raw);
+ lxc_config_define(cap_drop);
+ lxc_config_define(cap_keep);
+ lxc_config_define(cgroup_controller);
+@@ -158,6 +160,8 @@ static struct lxc_config_t config[] = {
+ { "lxc.arch", set_config_personality, get_config_personality, clr_config_personality, },
+ { "lxc.apparmor.profile", set_config_apparmor_profile, get_config_apparmor_profile, clr_config_apparmor_profile, },
+ { "lxc.apparmor.allow_incomplete", set_config_apparmor_allow_incomplete, get_config_apparmor_allow_incomplete, clr_config_apparmor_allow_incomplete, },
++ { "lxc.apparmor.allow_nesting", set_config_apparmor_allow_nesting, get_config_apparmor_allow_nesting, clr_config_apparmor_allow_nesting, },
++ { "lxc.apparmor.raw", set_config_apparmor_raw, get_config_apparmor_raw, clr_config_apparmor_raw, },
+ { "lxc.autodev", set_config_autodev, get_config_autodev, clr_config_autodev, },
+ { "lxc.cap.drop", set_config_cap_drop, get_config_cap_drop, clr_config_cap_drop, },
+ { "lxc.cap.keep", set_config_cap_keep, get_config_cap_keep, clr_config_cap_keep, },
+@@ -1115,6 +1119,52 @@ static int set_config_apparmor_allow_incomplete(const char *key,
+ return 0;
+ }
+
++static int set_config_apparmor_allow_nesting(const char *key,
++ const char *value,
++ struct lxc_conf *lxc_conf,
++ void *data)
++{
++ if (lxc_config_value_empty(value))
++ return clr_config_apparmor_allow_nesting(key, lxc_conf, NULL);
++
++ if (lxc_safe_uint(value, &lxc_conf->lsm_aa_allow_nesting) < 0)
++ return -1;
++
++ if (lxc_conf->lsm_aa_allow_nesting > 1)
++ return -1;
++
++ return 0;
++}
++
++static int set_config_apparmor_raw(const char *key,
++ const char *value,
++ struct lxc_conf *lxc_conf,
++ void *data)
++{
++ char *elem;
++ struct lxc_list *list;
++
++ if (lxc_config_value_empty(value))
++ return lxc_clear_apparmor_raw(lxc_conf);
++
++ list = malloc(sizeof(*list));
++ if (!list) {
++ errno = ENOMEM;
++ return -1;
++ }
++
++ elem = strdup(value);
++ if (!elem) {
++ free(list);
++ return -1;
++ }
++ list->elem = elem;
++
++ lxc_list_add_tail(&lxc_conf->lsm_aa_raw, list);
++
++ return 0;
++}
++
+ static int set_config_selinux_context(const char *key, const char *value,
+ struct lxc_conf *lxc_conf, void *data)
+ {
+@@ -2966,6 +3016,34 @@ static int get_config_apparmor_allow_incomplete(const char *key, char *retv,
+ c->lsm_aa_allow_incomplete);
+ }
+
++static int get_config_apparmor_allow_nesting(const char *key, char *retv,
++ int inlen, struct lxc_conf *c,
++ void *data)
++{
++ return lxc_get_conf_int(c, retv, inlen,
++ c->lsm_aa_allow_nesting);
++}
++
++static int get_config_apparmor_raw(const char *key, char *retv,
++ int inlen, struct lxc_conf *c,
++ void *data)
++{
++ int len;
++ struct lxc_list *it;
++ int fulllen = 0;
++
++ if (!retv)
++ inlen = 0;
++ else
++ memset(retv, 0, inlen);
++
++ lxc_list_for_each(it, &c->lsm_aa_raw) {
++ strprint(retv, inlen, "%s\n", (char *)it->elem);
++ }
++
++ return fulllen;
++}
++
+ static int get_config_selinux_context(const char *key, char *retv, int inlen,
+ struct lxc_conf *c, void *data)
+ {
+@@ -3756,6 +3834,21 @@ static inline int clr_config_apparmor_allow_incomplete(const char *key,
+ return 0;
+ }
+
++static inline int clr_config_apparmor_allow_nesting(const char *key,
++ struct lxc_conf *c,
++ void *data)
++{
++ c->lsm_aa_allow_nesting = 0;
++ return 0;
++}
++
++static inline int clr_config_apparmor_raw(const char *key,
++ struct lxc_conf *c,
++ void *data)
++{
++ return lxc_clear_apparmor_raw(c);
++}
++
+ static inline int clr_config_selinux_context(const char *key,
+ struct lxc_conf *c, void *data)
+ {
+@@ -4952,7 +5045,9 @@ int lxc_list_subkeys(struct lxc_conf *conf, const char *key, char *retv,
+
+ if (!strcmp(key, "lxc.apparmor")) {
+ strprint(retv, inlen, "allow_incomplete\n");
++ strprint(retv, inlen, "allow_nesting\n");
+ strprint(retv, inlen, "profile\n");
++ strprint(retv, inlen, "raw\n");
+ } else if (!strcmp(key, "lxc.cgroup")) {
+ strprint(retv, inlen, "dir\n");
+ } else if (!strcmp(key, "lxc.selinux")) {
+diff --git a/src/lxc/criu.c b/src/lxc/criu.c
+index 398e8e94..72ba0a95 100644
+--- a/src/lxc/criu.c
++++ b/src/lxc/criu.c
+@@ -378,7 +378,8 @@ static void exec_criu(struct cgroup_ops *cgroup_ops, struct criu_opts *opts)
+ DECLARE_ARG(opts->user->action_script);
+ }
+
+- mnts = make_anonymous_mount_file(&opts->c->lxc_conf->mount_list);
++ mnts = make_anonymous_mount_file(&opts->c->lxc_conf->mount_list,
++ opts->c->lxc_conf->lsm_aa_allow_nesting);
+ if (!mnts)
+ goto err;
+
+diff --git a/src/lxc/lsm/apparmor.c b/src/lxc/lsm/apparmor.c
+index 5fe6d525..ec3f805d 100644
+--- a/src/lxc/lsm/apparmor.c
++++ b/src/lxc/lsm/apparmor.c
+@@ -33,11 +33,18 @@
+ #include "conf.h"
+ #include "utils.h"
+ #include "initutils.h"
++#include "caps.h"
++#include "parse.h"
+
+ lxc_log_define(apparmor, lsm);
+
+ /* set by lsm_apparmor_drv_init if true */
+ static int aa_enabled = 0;
++static bool aa_parser_available = false;
++static bool aa_supports_unix = false;
++static bool aa_can_stack = false;
++static bool aa_is_stacked = false;
++static bool aa_admin = false;
+
+ static int mount_features_enabled = 0;
+
+@@ -46,6 +53,332 @@ static int mount_features_enabled = 0;
+ #define AA_MOUNT_RESTR "/sys/kernel/security/apparmor/features/mount/mask"
+ #define AA_ENABLED_FILE "/sys/module/apparmor/parameters/enabled"
+ #define AA_UNCHANGED "unchanged"
++#define AA_GENERATED "generated"
++
++#define AA_CMD_LOAD 'r'
++#define AA_CMD_UNLOAD 'R'
++#define AA_CMD_PARSE 'Q'
++
++static const char AA_PROFILE_BASE[] =
++" ### Base profile\n"
++" capability,\n"
++" dbus,\n"
++" file,\n"
++" network,\n"
++" umount,\n"
++"\n"
++" # Allow us to receive signals from anywhere.\n"
++" signal (receive),\n"
++"\n"
++" # Allow us to send signals to ourselves\n"
++" signal peer=@{profile_name},\n"
++"\n"
++" # Allow other processes to read our /proc entries, futexes, perf tracing and\n"
++" # kcmp for now (they will need 'read' in the first place). Administrators can\n"
++" # override with:\n"
++" # deny ptrace (readby) ...\n"
++" ptrace (readby),\n"
++"\n"
++" # Allow other processes to trace us by default (they will need 'trace' in\n"
++" # the first place). Administrators can override with:\n"
++" # deny ptrace (tracedby) ...\n"
++" ptrace (tracedby),\n"
++"\n"
++" # Allow us to ptrace ourselves\n"
++" ptrace peer=@{profile_name},\n"
++"\n"
++" # ignore DENIED message on / remount\n"
++" deny mount options=(ro, remount) -> /,\n"
++" deny mount options=(ro, remount, silent) -> /,\n"
++"\n"
++" # allow tmpfs mounts everywhere\n"
++" mount fstype=tmpfs,\n"
++"\n"
++" # allow hugetlbfs mounts everywhere\n"
++" mount fstype=hugetlbfs,\n"
++"\n"
++" # allow mqueue mounts everywhere\n"
++" mount fstype=mqueue,\n"
++"\n"
++" # allow fuse mounts everywhere\n"
++" mount fstype=fuse,\n"
++" mount fstype=fuse.*,\n"
++"\n"
++" # deny access under /proc/bus to avoid e.g. messing with pci devices directly\n"
++" deny @{PROC}/bus/** wklx,\n"
++"\n"
++" # deny writes in /proc/sys/fs but allow binfmt_misc to be mounted\n"
++" mount fstype=binfmt_misc -> /proc/sys/fs/binfmt_misc/,\n"
++" deny @{PROC}/sys/fs/** wklx,\n"
++"\n"
++" # allow efivars to be mounted, writing to it will be blocked though\n"
++" mount fstype=efivarfs -> /sys/firmware/efi/efivars/,\n"
++"\n"
++" # block some other dangerous paths\n"
++" deny @{PROC}/kcore rwklx,\n"
++" deny @{PROC}/sysrq-trigger rwklx,\n"
++"\n"
++" # deny writes in /sys except for /sys/fs/cgroup, also allow\n"
++" # fusectl, securityfs and debugfs to be mounted there (read-only)\n"
++" mount fstype=fusectl -> /sys/fs/fuse/connections/,\n"
++" mount fstype=securityfs -> /sys/kernel/security/,\n"
++" mount fstype=debugfs -> /sys/kernel/debug/,\n"
++" deny mount fstype=debugfs -> /var/lib/ureadahead/debugfs/,\n"
++" mount fstype=proc -> /proc/,\n"
++" mount fstype=sysfs -> /sys/,\n"
++" mount options=(rw, nosuid, nodev, noexec, remount) -> /sys/,\n"
++" deny /sys/firmware/efi/efivars/** rwklx,\n"
++" # note, /sys/kernel/security/** handled below\n"
++" mount options=(ro, nosuid, nodev, noexec, remount, strictatime) -> /sys/fs/cgroup/,\n"
++"\n"
++" # deny reads from debugfs\n"
++" deny /sys/kernel/debug/{,**} rwklx,\n"
++"\n"
++" # allow paths to be made slave, shared, private or unbindable\n"
++" # FIXME: This currently doesn't work due to the apparmor parser treating those as allowing all mounts.\n"
++"# mount options=(rw,make-slave) -> **,\n"
++"# mount options=(rw,make-rslave) -> **,\n"
++"# mount options=(rw,make-shared) -> **,\n"
++"# mount options=(rw,make-rshared) -> **,\n"
++"# mount options=(rw,make-private) -> **,\n"
++"# mount options=(rw,make-rprivate) -> **,\n"
++"# mount options=(rw,make-unbindable) -> **,\n"
++"# mount options=(rw,make-runbindable) -> **,\n"
++"\n"
++" # allow bind-mounts of anything except /proc, /sys and /dev\n"
++" mount options=(rw,bind) /[^spd]*{,/**},\n"
++" mount options=(rw,bind) /d[^e]*{,/**},\n"
++" mount options=(rw,bind) /de[^v]*{,/**},\n"
++" mount options=(rw,bind) /dev/.[^l]*{,/**},\n"
++" mount options=(rw,bind) /dev/.l[^x]*{,/**},\n"
++" mount options=(rw,bind) /dev/.lx[^c]*{,/**},\n"
++" mount options=(rw,bind) /dev/.lxc?*{,/**},\n"
++" mount options=(rw,bind) /dev/[^.]*{,/**},\n"
++" mount options=(rw,bind) /dev?*{,/**},\n"
++" mount options=(rw,bind) /p[^r]*{,/**},\n"
++" mount options=(rw,bind) /pr[^o]*{,/**},\n"
++" mount options=(rw,bind) /pro[^c]*{,/**},\n"
++" mount options=(rw,bind) /proc?*{,/**},\n"
++" mount options=(rw,bind) /s[^y]*{,/**},\n"
++" mount options=(rw,bind) /sy[^s]*{,/**},\n"
++" mount options=(rw,bind) /sys?*{,/**},\n"
++"\n"
++" # allow read-only bind-mounts of anything except /proc, /sys and /dev\n"
++" mount options=(ro,remount,bind) -> /[^spd]*{,/**},\n"
++" mount options=(ro,remount,bind) -> /d[^e]*{,/**},\n"
++" mount options=(ro,remount,bind) -> /de[^v]*{,/**},\n"
++" mount options=(ro,remount,bind) -> /dev/.[^l]*{,/**},\n"
++" mount options=(ro,remount,bind) -> /dev/.l[^x]*{,/**},\n"
++" mount options=(ro,remount,bind) -> /dev/.lx[^c]*{,/**},\n"
++" mount options=(ro,remount,bind) -> /dev/.lxc?*{,/**},\n"
++" mount options=(ro,remount,bind) -> /dev/[^.]*{,/**},\n"
++" mount options=(ro,remount,bind) -> /dev?*{,/**},\n"
++" mount options=(ro,remount,bind) -> /p[^r]*{,/**},\n"
++" mount options=(ro,remount,bind) -> /pr[^o]*{,/**},\n"
++" mount options=(ro,remount,bind) -> /pro[^c]*{,/**},\n"
++" mount options=(ro,remount,bind) -> /proc?*{,/**},\n"
++" mount options=(ro,remount,bind) -> /s[^y]*{,/**},\n"
++" mount options=(ro,remount,bind) -> /sy[^s]*{,/**},\n"
++" mount options=(ro,remount,bind) -> /sys?*{,/**},\n"
++"\n"
++" # allow moving mounts except for /proc, /sys and /dev\n"
++" mount options=(rw,move) /[^spd]*{,/**},\n"
++" mount options=(rw,move) /d[^e]*{,/**},\n"
++" mount options=(rw,move) /de[^v]*{,/**},\n"
++" mount options=(rw,move) /dev/.[^l]*{,/**},\n"
++" mount options=(rw,move) /dev/.l[^x]*{,/**},\n"
++" mount options=(rw,move) /dev/.lx[^c]*{,/**},\n"
++" mount options=(rw,move) /dev/.lxc?*{,/**},\n"
++" mount options=(rw,move) /dev/[^.]*{,/**},\n"
++" mount options=(rw,move) /dev?*{,/**},\n"
++" mount options=(rw,move) /p[^r]*{,/**},\n"
++" mount options=(rw,move) /pr[^o]*{,/**},\n"
++" mount options=(rw,move) /pro[^c]*{,/**},\n"
++" mount options=(rw,move) /proc?*{,/**},\n"
++" mount options=(rw,move) /s[^y]*{,/**},\n"
++" mount options=(rw,move) /sy[^s]*{,/**},\n"
++" mount options=(rw,move) /sys?*{,/**},\n"
++"\n"
++" # generated by: lxc-generate-aa-rules.py container-rules.base\n"
++" deny /proc/sys/[^kn]*{,/**} wklx,\n"
++" deny /proc/sys/k[^e]*{,/**} wklx,\n"
++" deny /proc/sys/ke[^r]*{,/**} wklx,\n"
++" deny /proc/sys/ker[^n]*{,/**} wklx,\n"
++" deny /proc/sys/kern[^e]*{,/**} wklx,\n"
++" deny /proc/sys/kerne[^l]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/[^smhd]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/d[^o]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/do[^m]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/dom[^a]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/doma[^i]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/domai[^n]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/domain[^n]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/domainn[^a]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/domainna[^m]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/domainnam[^e]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/domainname?*{,/**} wklx,\n"
++" deny /proc/sys/kernel/h[^o]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/ho[^s]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/hos[^t]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/host[^n]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/hostn[^a]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/hostna[^m]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/hostnam[^e]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/hostname?*{,/**} wklx,\n"
++" deny /proc/sys/kernel/m[^s]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/ms[^g]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/msg*/** wklx,\n"
++" deny /proc/sys/kernel/s[^he]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/se[^m]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/sem*/** wklx,\n"
++" deny /proc/sys/kernel/sh[^m]*{,/**} wklx,\n"
++" deny /proc/sys/kernel/shm*/** wklx,\n"
++" deny /proc/sys/kernel?*{,/**} wklx,\n"
++" deny /proc/sys/n[^e]*{,/**} wklx,\n"
++" deny /proc/sys/ne[^t]*{,/**} wklx,\n"
++" deny /proc/sys/net?*{,/**} wklx,\n"
++" deny /sys/[^fdck]*{,/**} wklx,\n"
++" deny /sys/c[^l]*{,/**} wklx,\n"
++" deny /sys/cl[^a]*{,/**} wklx,\n"
++" deny /sys/cla[^s]*{,/**} wklx,\n"
++" deny /sys/clas[^s]*{,/**} wklx,\n"
++" deny /sys/class/[^n]*{,/**} wklx,\n"
++" deny /sys/class/n[^e]*{,/**} wklx,\n"
++" deny /sys/class/ne[^t]*{,/**} wklx,\n"
++" deny /sys/class/net?*{,/**} wklx,\n"
++" deny /sys/class?*{,/**} wklx,\n"
++" deny /sys/d[^e]*{,/**} wklx,\n"
++" deny /sys/de[^v]*{,/**} wklx,\n"
++" deny /sys/dev[^i]*{,/**} wklx,\n"
++" deny /sys/devi[^c]*{,/**} wklx,\n"
++" deny /sys/devic[^e]*{,/**} wklx,\n"
++" deny /sys/device[^s]*{,/**} wklx,\n"
++" deny /sys/devices/[^v]*{,/**} wklx,\n"
++" deny /sys/devices/v[^i]*{,/**} wklx,\n"
++" deny /sys/devices/vi[^r]*{,/**} wklx,\n"
++" deny /sys/devices/vir[^t]*{,/**} wklx,\n"
++" deny /sys/devices/virt[^u]*{,/**} wklx,\n"
++" deny /sys/devices/virtu[^a]*{,/**} wklx,\n"
++" deny /sys/devices/virtua[^l]*{,/**} wklx,\n"
++" deny /sys/devices/virtual/[^n]*{,/**} wklx,\n"
++" deny /sys/devices/virtual/n[^e]*{,/**} wklx,\n"
++" deny /sys/devices/virtual/ne[^t]*{,/**} wklx,\n"
++" deny /sys/devices/virtual/net?*{,/**} wklx,\n"
++" deny /sys/devices/virtual?*{,/**} wklx,\n"
++" deny /sys/devices?*{,/**} wklx,\n"
++" deny /sys/f[^s]*{,/**} wklx,\n"
++" deny /sys/fs/[^c]*{,/**} wklx,\n"
++" deny /sys/fs/c[^g]*{,/**} wklx,\n"
++" deny /sys/fs/cg[^r]*{,/**} wklx,\n"
++" deny /sys/fs/cgr[^o]*{,/**} wklx,\n"
++" deny /sys/fs/cgro[^u]*{,/**} wklx,\n"
++" deny /sys/fs/cgrou[^p]*{,/**} wklx,\n"
++" deny /sys/fs/cgroup?*{,/**} wklx,\n"
++" deny /sys/fs?*{,/**} wklx,\n"
++;
++
++static const char AA_PROFILE_UNIX_SOCKETS[] =
++"\n"
++" ### Feature: unix\n"
++" # Allow receive via unix sockets from anywhere\n"
++" unix (receive),\n"
++"\n"
++" # Allow all unix sockets in the container\n"
++" unix peer=(label=@{profile_name}),\n"
++;
++
++static const char AA_PROFILE_CGROUP_NAMESPACES[] =
++"\n"
++" ### Feature: cgroup namespace\n"
++" mount fstype=cgroup -> /sys/fs/cgroup/**,\n"
++" mount fstype=cgroup2 -> /sys/fs/cgroup/**,\n"
++;
++
++/* '_BASE' because we still need to append generated change_profile rules */
++static const char AA_PROFILE_STACKING_BASE[] =
++"\n"
++" ### Feature: apparmor stacking\n"
++" ### Configuration: apparmor profile loading (in namespace)\n"
++" deny /sys/k[^e]*{,/**} wklx,\n"
++" deny /sys/ke[^r]*{,/**} wklx,\n"
++" deny /sys/ker[^n]*{,/**} wklx,\n"
++" deny /sys/kern[^e]*{,/**} wklx,\n"
++" deny /sys/kerne[^l]*{,/**} wklx,\n"
++" deny /sys/kernel/[^s]*{,/**} wklx,\n"
++" deny /sys/kernel/s[^e]*{,/**} wklx,\n"
++" deny /sys/kernel/se[^c]*{,/**} wklx,\n"
++" deny /sys/kernel/sec[^u]*{,/**} wklx,\n"
++" deny /sys/kernel/secu[^r]*{,/**} wklx,\n"
++" deny /sys/kernel/secur[^i]*{,/**} wklx,\n"
++" deny /sys/kernel/securi[^t]*{,/**} wklx,\n"
++" deny /sys/kernel/securit[^y]*{,/**} wklx,\n"
++" deny /sys/kernel/security/[^a]*{,/**} wklx,\n"
++" deny /sys/kernel/security/a[^p]*{,/**} wklx,\n"
++" deny /sys/kernel/security/ap[^p]*{,/**} wklx,\n"
++" deny /sys/kernel/security/app[^a]*{,/**} wklx,\n"
++" deny /sys/kernel/security/appa[^r]*{,/**} wklx,\n"
++" deny /sys/kernel/security/appar[^m]*{,/**} wklx,\n"
++" deny /sys/kernel/security/apparm[^o]*{,/**} wklx,\n"
++" deny /sys/kernel/security/apparmo[^r]*{,/**} wklx,\n"
++" deny /sys/kernel/security/apparmor?*{,/**} wklx,\n"
++" deny /sys/kernel/security?*{,/**} wklx,\n"
++" deny /sys/kernel?*{,/**} wklx,\n"
++;
++
++static const char AA_PROFILE_NO_STACKING[] =
++"\n"
++" ### Feature: apparmor stacking (not present)\n"
++" deny /sys/k*{,/**} rwklx,\n"
++;
++
++/* '_BASE' because we need to append change_profile for stacking */
++static const char AA_PROFILE_NESTING_BASE[] =
++"\n"
++" ### Configuration: nesting\n"
++" pivot_root,\n"
++" ptrace,\n"
++" signal,\n"
++"\n"
++ /* NOTE: See conf.c's "nesting_helpers" for details. */
++" deny /dev/.lxc/proc/** rw,\n"
++" deny /dev/.lxc/sys/** rw,\n"
++"\n"
++" mount fstype=proc -> /usr/lib/*/lxc/**,\n"
++" mount fstype=sysfs -> /usr/lib/*/lxc/**,\n"
++" mount options=(rw,bind),\n"
++" mount options=(rw,rbind),\n"
++" mount options=(rw,make-rshared),\n"
++"\n"
++ /* FIXME: What's the state here on apparmor's side? */
++" # there doesn't seem to be a way to ask for:\n"
++" # mount options=(ro,nosuid,nodev,noexec,remount,bind),\n"
++" # as we always get mount to $cdir/proc/sys with those flags denied\n"
++" # So allow all mounts until that is straightened out:\n"
++" mount,\n"
++;
++
++static const char AA_PROFILE_UNPRIVILEGED[] =
++"\n"
++" ### Configuration: unprivileged container\n"
++" pivot_root,\n"
++"\n"
++" # Allow modifying mount propagation\n"
++" mount options=(rw,make-slave) -> **,\n"
++" mount options=(rw,make-rslave) -> **,\n"
++" mount options=(rw,make-shared) -> **,\n"
++" mount options=(rw,make-rshared) -> **,\n"
++" mount options=(rw,make-private) -> **,\n"
++" mount options=(rw,make-rprivate) -> **,\n"
++" mount options=(rw,make-unbindable) -> **,\n"
++" mount options=(rw,make-runbindable) -> **,\n"
++"\n"
++" # Allow all bind-mounts\n"
++" mount options=(rw,bind),\n"
++" mount options=(rw,rbind),\n"
++"\n"
++" # Allow remounting things read-only\n"
++" mount options=(ro,remount),\n"
++;
+
+ static bool check_mount_feature_enabled(void)
+ {
+@@ -144,11 +477,6 @@ static bool apparmor_am_unconfined(void)
+ return ret;
+ }
+
+-/* aa stacking is not yet supported */
+-static bool aa_stacking_supported(void) {
+- return false;
+-}
+-
+ static bool aa_needs_transition(char *curlabel)
+ {
+ if (!curlabel)
+@@ -160,61 +488,546 @@ static bool aa_needs_transition(char *curlabel)
+ return true;
+ }
+
++static inline void uint64hex(char *buf, uint64_t num)
++{
++ size_t i;
++
++ buf[16] = 0;
++ for (i = 16; i--;) {
++ char c = (char)(num & 0xf);
++ buf[i] = c + (c < 0xa ? '0' : 'a' - 0xa);
++ num >>= 4;
++ }
++}
++
++static inline char *shorten_apparmor_name(char *name)
++{
++ size_t len = strlen(name);
++ if (len + 7 > 253) {
++ uint64_t hash;
++ hash = fnv_64a_buf(name, len, FNV1A_64_INIT);
++ name = must_realloc(name, 16 + 1);
++ uint64hex(name, hash);
++ }
++
++ return name;
++}
++
++/* Replace slashes with hyphens */
++static inline void sanitize_path(char *path)
++{
++ size_t i;
++
++ for (i = 0; path[i]; i++)
++ if (path[i] == '/')
++ path[i] = '-';
++}
++
++static inline char *apparmor_dir(const char *ctname, const char *lxcpath)
++{
++ return must_make_path(lxcpath, ctname, "apparmor", NULL);
++}
++
++
++static inline char *apparmor_profile_full(const char *ctname, const char *lxcpath)
++{
++ return shorten_apparmor_name(must_concat("lxc-", ctname, "_<", lxcpath, ">", NULL));
++}
++
++/* Like apparmor_profile_full() but with slashes replaced by hyphens */
++static inline char *apparmor_namespace(const char *ctname, const char *lxcpath)
++{
++ char *full;
++
++ full = apparmor_profile_full(ctname, lxcpath);
++ sanitize_path(full);
++
++ return full;
++}
++
++/* FIXME: This is currently run only in the context of a constructor (via the
++ * initial lsm_init() called due to its __attribute__((constructor)), so we
++ * do not have ERROR/... macros available, so there are some fprintf(stderr)s
++ * in there.
++ */
++static bool check_apparmor_parser_version()
++{
++ struct lxc_popen_FILE *parserpipe;
++ int rc;
++ int major = 0, minor = 0, micro = 0;
++
++ parserpipe = lxc_popen("apparmor_parser --version");
++ if (!parserpipe) {
++ fprintf(stderr, "Failed to run check for apparmor_parser\n");
++ return false;
++ }
++
++ rc = fscanf(parserpipe->f, "AppArmor parser version %d.%d.%d", &major, &minor, µ);
++ if (rc < 1) {
++ lxc_pclose(parserpipe);
++ /* We stay silent for now as this most likely means the shell
++ * lxc_popen executed failed to find the apparmor_parser binary.
++ * See the FIXME comment above for details.
++ */
++ return false;
++ }
++
++ rc = lxc_pclose(parserpipe);
++ if (rc < 0) {
++ fprintf(stderr, "Error waiting for child process\n");
++ return false;
++ }
++ if (rc != 0) {
++ fprintf(stderr, "'apparmor_parser --version' executed with an error status\n");
++ return false;
++ }
++
++ aa_supports_unix = (major > 2) ||
++ (major == 2 && minor > 10) ||
++ (major == 2 && minor == 10 && micro >= 95);
++
++ return true;
++}
++
++static bool file_is_yes(const char *path)
++{
++ ssize_t rd;
++ int fd;
++ char buf[8]; /* we actually just expect "yes" or "no" */
++
++ fd = open(path, O_RDONLY | O_CLOEXEC);
++ if (fd < 0)
++ return false;
++
++ rd = read(fd, buf, sizeof(buf));
++ close(fd);
++
++ return rd >= 4 && strncmp(buf, "yes\n", 4) == 0;
++}
++
++static bool apparmor_can_stack()
++{
++ int major, minor, scanned;
++ FILE *f;
++
++ if (!file_is_yes("/sys/kernel/security/apparmor/features/domain/stack"))
++ return false;
++
++ f = fopen_cloexec("/sys/kernel/security/apparmor/features/domain/version", "r");
++ if (!f)
++ return false;
++
++ scanned = fscanf(f, "%d.%d", &major, &minor);
++ fclose(f);
++ if (scanned != 2)
++ return false;
++
++ return major > 1 || (major == 1 && minor >= 2);
++}
++
++static void must_append_sized_full(char **buf, size_t *bufsz, const char *data,
++ size_t size, bool append_newline)
++{
++ size_t newsize = *bufsz + size;
++
++ if (append_newline)
++ ++newsize;
++
++ *buf = must_realloc(*buf, newsize);
++ memcpy(*buf + *bufsz, data, size);
++
++ if (append_newline)
++ (*buf)[newsize - 1] = '\n';
++
++ *bufsz = newsize;
++}
++
++static void must_append_sized(char **buf, size_t *bufsz, const char *data, size_t size)
++{
++ return must_append_sized_full(buf, bufsz, data, size, false);
++}
++
++static bool is_privileged(struct lxc_conf *conf)
++{
++ return lxc_list_empty(&conf->id_map);
++}
++
++static char *get_apparmor_profile_content(struct lxc_conf *conf, const char *lxcpath)
++{
++ char *profile, *profile_name_full;
++ size_t size;
++ struct lxc_list *it;
++
++ profile_name_full = apparmor_profile_full(conf->name, lxcpath);
++
++ profile = must_concat(
++"#include <tunables/global>\n"
++"profile \"", profile_name_full, "\" flags=(attach_disconnected,mediate_deleted) {\n",
++ NULL);
++ size = strlen(profile);
++
++ must_append_sized(&profile, &size, AA_PROFILE_BASE,
++ sizeof(AA_PROFILE_BASE) - 1);
++
++ if (aa_supports_unix)
++ must_append_sized(&profile, &size, AA_PROFILE_UNIX_SOCKETS,
++ sizeof(AA_PROFILE_UNIX_SOCKETS) - 1);
++
++ if (file_exists("/proc/self/ns/cgroup"))
++ must_append_sized(&profile, &size, AA_PROFILE_CGROUP_NAMESPACES,
++ sizeof(AA_PROFILE_CGROUP_NAMESPACES) - 1);
++
++ if (aa_can_stack && !aa_is_stacked) {
++ char *namespace, *temp;
++
++ must_append_sized(&profile, &size, AA_PROFILE_STACKING_BASE,
++ sizeof(AA_PROFILE_STACKING_BASE) - 1);
++
++ namespace = apparmor_namespace(conf->name, lxcpath);
++ temp = must_concat(" change_profile -> \":", namespace, ":*\",\n"
++ " change_profile -> \":", namespace, "://*\",\n",
++ NULL);
++ free(namespace);
++
++ must_append_sized(&profile, &size, temp, strlen(temp));
++ free(temp);
++ } else {
++ must_append_sized(&profile, &size, AA_PROFILE_NO_STACKING,
++ sizeof(AA_PROFILE_NO_STACKING) - 1);
++ }
++
++ if (conf->lsm_aa_allow_nesting) {
++ must_append_sized(&profile, &size, AA_PROFILE_NESTING_BASE,
++ sizeof(AA_PROFILE_NESTING_BASE) - 1);
++
++ if (!aa_can_stack || aa_is_stacked) {
++ char *temp;
++
++ temp = must_concat(" change_profile -> \"",
++ profile_name_full, "\",\n", NULL);
++ must_append_sized(&profile, &size, temp, strlen(temp));
++ free(temp);
++ }
++ }
++
++ if (!is_privileged(conf) || am_host_unpriv())
++ must_append_sized(&profile, &size, AA_PROFILE_UNPRIVILEGED,
++ sizeof(AA_PROFILE_UNPRIVILEGED) - 1);
++
++ lxc_list_for_each(it, &conf->lsm_aa_raw) {
++ const char *line = it->elem;
++
++ must_append_sized_full(&profile, &size, line, strlen(line), true);
++ }
++
++ /* include terminating \0 byte */
++ must_append_sized(&profile, &size, "}\n", 3);
++
++ free(profile_name_full);
++
++ return profile;
++}
++
+ /*
+- * apparmor_process_label_set: Set AppArmor process profile
+- *
+- * @label : the profile to set
+- * @conf : the container configuration to use if @label is NULL
+- * @default : use the default profile if @label is NULL
+- * @on_exec : this is ignored. Apparmor profile will be changed immediately
+- *
+- * Returns 0 on success, < 0 on failure
+- *
+- * Notes: This relies on /proc being available.
++ * apparmor_parser creates a cache file using the parsed file's name as a name.
++ * This means there may be multiple containers with the same name but different
++ * lxcpaths. Therefore we need a sanitized version of the complete profile name
++ * as profile file-name.
++ * We already get this exactly from apparmor_namespace().
+ */
+-static int apparmor_process_label_set(const char *inlabel, struct lxc_conf *conf,
+- bool use_default, bool on_exec)
++static char *make_apparmor_profile_path(const char *ctname, const char *lxcpath)
+ {
+- int label_fd, ret;
+- pid_t tid;
+- const char *label = inlabel ? inlabel : conf->lsm_aa_profile;
+- char *curlabel;
++ char *ret, *filename;
+
+- if (!aa_enabled)
+- return 0;
++ filename = apparmor_namespace(ctname, lxcpath);
++ ret = must_make_path(lxcpath, ctname, "apparmor", filename, NULL);
++ free(filename);
++
++ return ret;
++}
++
++static char *make_apparmor_namespace_path(const char *ctname, const char *lxcpath)
++{
++ char *ret, *namespace;
++
++ namespace = apparmor_namespace(ctname, lxcpath);
++ ret = must_make_path("/sys/kernel/security/apparmor/policy/namespaces", namespace, NULL);
++ free(namespace);
++
++ return ret;
++}
++
++static bool make_apparmor_namespace(struct lxc_conf *conf, const char *lxcpath)
++{
++ char *path;
++
++ if (!aa_can_stack || aa_is_stacked)
++ return true;
++
++ path = make_apparmor_namespace_path(conf->name, lxcpath);
++ errno = 0;
++ if (mkdir(path, 0755) < 0 && errno != EEXIST) {
++ SYSERROR("Error creating AppArmor namespace: %s", path);
++ free(path);
++ return false;
++ }
++ free(path);
++
++ return true;
++}
++
++static void remove_apparmor_namespace(struct lxc_conf *conf, const char *lxcpath)
++{
++ char *path;
++
++ path = make_apparmor_namespace_path(conf->name, lxcpath);
++ if (rmdir(path) != 0)
++ SYSERROR("Error removing AppArmor namespace");
++ free(path);
++}
++
++struct apparmor_parser_args {
++ char cmd;
++ char *file;
++};
++
++static int apparmor_parser_exec(void *data)
++{
++ struct apparmor_parser_args *args = data;
++ char cmdbuf[] = { '-', args->cmd, 'W', 'L', 0 };
++
++ execlp("apparmor_parser", "apparmor_parser", cmdbuf, APPARMOR_CACHE_DIR, args->file, NULL);
++
++ return -1;
++}
++
++static int run_apparmor_parser(char command,
++ struct lxc_conf *conf,
++ const char *lxcpath)
++{
++ char output[MAXPATHLEN];
++ int ret;
++ struct apparmor_parser_args args = {
++ .cmd = command,
++ .file = make_apparmor_profile_path(conf->name, lxcpath),
++ };
++
++ ret = run_command(output, sizeof(output), apparmor_parser_exec, (void*)&args);
++ if (ret < 0) {
++ ERROR("Failed to run apparmor_parser on \"%s\": %s", args.file, output);
++ ret = -1;
++ }
++
++
++ free(args.file);
++ return ret;
++}
++
++static void remove_apparmor_profile(struct lxc_conf *conf, const char *lxcpath)
++{
++ char *path;
++
++ /* It's ok if these deletes fail: if the container was never started,
++ * we'll have never written a profile or cached it.
++ */
++
++ path = make_apparmor_profile_path(conf->name, lxcpath);
++ (void)unlink(path);
++ free(path);
++
++ /* Also remove the apparmor/ subdirectory */
++ path = apparmor_dir(conf->name, lxcpath);
++ (void)rmdir(path);
++ free(path);
++}
++
++static int load_apparmor_profile(struct lxc_conf *conf, const char *lxcpath)
++{
++ struct stat profile_sb;
++ size_t content_len;
++ int ret = -1;
++ size_t old_len = 0;
++ char *profile_path = NULL, *old_content = NULL, *new_content = NULL;
++ int profile_fd = -1;
++
++ if (!make_apparmor_namespace(conf, lxcpath))
++ return -1;
++
++ /* In order to avoid forcing a profile parse (potentially slow) on
++ * every container start, let's use apparmor's binary policy cache,
++ * which checks mtime of the files to figure out if the policy needs to
++ * be regenerated.
++ *
++ * Since it uses mtimes, we shouldn't just always write out our local
++ * apparmor template; instead we should check to see whether the
++ * template is the same as ours. If it isn't we should write our
++ * version out so that the new changes are reflected and we definitely
++ * force a recompile.
++ */
++
++ profile_path = make_apparmor_profile_path(conf->name, lxcpath);
++ profile_fd = open(profile_path, O_RDONLY | O_CLOEXEC);
++ if (profile_fd >= 0) {
++ if (fstat(profile_fd, &profile_sb) < 0) {
++ SYSERROR("Error accessing old profile from %s",
++ profile_path);
++ goto out;
++ }
++ old_len = profile_sb.st_size;
++ old_content = lxc_strmmap(NULL, old_len, PROT_READ,
++ MAP_PRIVATE, profile_fd, 0);
++ if (!old_content) {
++ SYSERROR("Failed to mmap old profile from %s",
++ profile_path);
++ goto out;
++ }
++ } else if (errno != ENOENT) {
++ SYSERROR("Error reading old profile from %s", profile_path);
++ goto out;
++ }
++
++ new_content = get_apparmor_profile_content(conf, lxcpath);
++ if (!new_content)
++ goto out;
++
++ content_len = strlen(new_content);
++
++ if (!old_content || old_len != content_len || memcmp(old_content, new_content, content_len) != 0) {
++ char *path;
++
++ ret = mkdir_p(APPARMOR_CACHE_DIR, 0755);
++ if (ret < 0) {
++ SYSERROR("Error creating AppArmor profile cache directory " APPARMOR_CACHE_DIR);
++ goto out;
++ }
++
++ path = apparmor_dir(conf->name, lxcpath);
++ ret = mkdir_p(path, 0755);
++ if (ret < 0) {
++ SYSERROR("Error creating AppArmor profile directory: %s", path);
++ free(path);
++ goto out;
++ }
++ free(path);
++
++ ret = lxc_write_to_file(profile_path, new_content, content_len, false, 0600);
++ if (ret < 0) {
++ SYSERROR("Error writing profile to %s", profile_path);
++ goto out;
++ }
++ }
++
++ ret = run_apparmor_parser(AA_CMD_LOAD, conf, lxcpath);
++ if (ret != 0)
++ goto out_remove_profile;
++
++ conf->lsm_aa_profile_created = true;
++
++ goto out_ok;
++
++out_remove_profile:
++ remove_apparmor_profile(conf, lxcpath);
++out:
++ remove_apparmor_namespace(conf, lxcpath);
++out_ok:
++ if (profile_fd >= 0) {
++ if (old_content)
++ lxc_strmunmap(old_content, old_len);
++ close(profile_fd);
++ }
++ free(profile_path);
++ free(new_content);
++ return ret;
++}
++
++/*
++ * Ensure that the container's policy namespace is unloaded to free kernel
++ * memory. This does not delete the policy from disk or cache.
++ */
++static void apparmor_cleanup(struct lxc_conf *conf, const char *lxcpath)
++{
++ if (!aa_admin)
++ return;
++
++ if (!conf->lsm_aa_profile_created)
++ return;
++
++ remove_apparmor_namespace(conf, lxcpath);
++ (void)run_apparmor_parser(AA_CMD_UNLOAD, conf, lxcpath);
++
++ remove_apparmor_profile(conf, lxcpath);
++}
++
++static int apparmor_prepare(struct lxc_conf *conf, const char *lxcpath)
++{
++ int ret = -1;
++ const char *label;
++ char *curlabel = NULL, *genlabel = NULL;
++
++ if (!aa_enabled) {
++ ERROR("AppArmor not enabled");
++ return -1;
++ }
++
++ label = conf->lsm_aa_profile;
+
+ /* user may request that we just ignore apparmor */
+ if (label && strcmp(label, AA_UNCHANGED) == 0) {
+- INFO("apparmor profile unchanged per user request");
++ INFO("AppArmor profile unchanged per user request");
++ conf->lsm_aa_profile_computed = must_copy_string(label);
+ return 0;
+ }
+
++ if (label && strcmp(label, AA_GENERATED) == 0) {
++ if (!aa_parser_available) {
++ ERROR("Cannot use generated profile: apparmor_parser not available");
++ goto out;
++ }
++
++ /* auto-generate profile based on available/requested security features */
++ if (load_apparmor_profile(conf, lxcpath) != 0) {
++ ERROR("Failed to load generated AppArmor profile");
++ goto out;
++ }
++
++ genlabel = apparmor_profile_full(conf->name, lxcpath);
++ if (!genlabel) {
++ ERROR("Failed to build AppArmor profile name");
++ goto out;
++ }
++
++ if (aa_can_stack && !aa_is_stacked) {
++ char *namespace = apparmor_namespace(conf->name, lxcpath);
++ size_t llen = strlen(genlabel);
++ must_append_sized(&genlabel, &llen, "//&:", sizeof("//&:") - 1);
++ must_append_sized(&genlabel, &llen, namespace, strlen(namespace));
++ must_append_sized(&genlabel, &llen, ":", sizeof(":")); /* with the nul byte */
++ free(namespace);
++ }
++
++ label = genlabel;
++ }
++
+ curlabel = apparmor_process_label_get(lxc_raw_getpid());
+
+- if (!aa_stacking_supported() && aa_needs_transition(curlabel)) {
++ if (!aa_can_stack && aa_needs_transition(curlabel)) {
+ /* we're already confined, and stacking isn't supported */
+
+ if (!label || strcmp(curlabel, label) == 0) {
+ /* no change requested */
+- free(curlabel);
+- return 0;
++ ret = 0;
++ goto out;
+ }
+
+- ERROR("already apparmor confined, but new label requested.");
+- free(curlabel);
+- return -1;
++ ERROR("Already AppArmor confined, but new label requested.");
++ goto out;
+ }
+- free(curlabel);
+
+ if (!label) {
+- if (use_default) {
+- if (cgns_supported())
+- label = AA_DEF_PROFILE_CGNS;
+- else
+- label = AA_DEF_PROFILE;
+- }
++ if (cgns_supported())
++ label = AA_DEF_PROFILE_CGNS;
+ else
+- label = "unconfined";
++ label = AA_DEF_PROFILE;
+ }
+
+ if (!check_mount_feature_enabled() && strcmp(label, "unconfined") != 0) {
+@@ -223,30 +1036,78 @@ static int apparmor_process_label_set(const char *inlabel, struct lxc_conf *conf
+ ERROR("If you really want to start this container, set");
+ ERROR("lxc.apparmor.allow_incomplete = 1");
+ ERROR("in your container configuration file");
+- return -1;
++ goto out;
+ }
+ }
+
++ conf->lsm_aa_profile_computed = must_copy_string(label);
++ ret = 0;
++
++out:
++ if (genlabel) {
++ free(genlabel);
++ if (ret != 0)
++ apparmor_cleanup(conf, lxcpath);
++ }
++ free(curlabel);
++ return ret;
++}
++
++/*
++ * apparmor_process_label_set: Set AppArmor process profile
++ *
++ * @label : the profile to set
++ * @conf : the container configuration to use if @label is NULL
++ * @default : use the default profile if @label is NULL
++ * @on_exec : this is ignored. Apparmor profile will be changed immediately
++ *
++ * Returns 0 on success, < 0 on failure
++ *
++ * Notes: This relies on /proc being available.
++ */
++static int apparmor_process_label_set(const char *inlabel, struct lxc_conf *conf,
++ bool on_exec)
++{
++ int label_fd, ret;
++ pid_t tid;
++ const char *label;
++
++ if (!aa_enabled) {
++ ERROR("AppArmor not enabled");
++ return -1;
++ }
++
++ label = inlabel ? inlabel : conf->lsm_aa_profile_computed;
++ if (!label) {
++ ERROR("LSM wasn't prepared");
++ return -1;
++ }
++
++ /* user may request that we just ignore apparmor */
++ if (strcmp(label, AA_UNCHANGED) == 0) {
++ INFO("AppArmor profile unchanged per user request");
++ return 0;
++ }
+
+ if (strcmp(label, "unconfined") == 0 && apparmor_am_unconfined()) {
+- INFO("apparmor profile unchanged");
++ INFO("AppArmor profile unchanged");
+ return 0;
+ }
+ tid = lxc_raw_gettid();
+ label_fd = lsm_process_label_fd_get(tid, on_exec);
+ if (label_fd < 0) {
+- SYSERROR("Failed to change apparmor profile to %s", label);
++ SYSERROR("Failed to change AppArmor profile to %s", label);
+ return -1;
+ }
+
+ ret = lsm_process_label_set_at(label_fd, label, on_exec);
+ close(label_fd);
+ if (ret < 0) {
+- ERROR("Failed to change apparmor profile to %s", label);
++ ERROR("Failed to change AppArmor profile to %s", label);
+ return -1;
+ }
+
+- INFO("Changed apparmor profile to %s", label);
++ INFO("Changed AppArmor profile to %s", label);
+ return 0;
+ }
+
+@@ -255,12 +1116,39 @@ static struct lsm_drv apparmor_drv = {
+ .enabled = apparmor_enabled,
+ .process_label_get = apparmor_process_label_get,
+ .process_label_set = apparmor_process_label_set,
++ .prepare = apparmor_prepare,
++ .cleanup = apparmor_cleanup,
+ };
+
+ struct lsm_drv *lsm_apparmor_drv_init(void)
+ {
++ bool have_mac_admin = false;
++
+ if (!apparmor_enabled())
+ return NULL;
++
++ /* We only support generated profiles when apparmor_parser is usable */
++ if (!check_apparmor_parser_version())
++ goto out;
++
++ aa_parser_available = true;
++
++ aa_can_stack = apparmor_can_stack();
++ if (aa_can_stack)
++ aa_is_stacked = file_is_yes("/sys/kernel/security/apparmor/.ns_stacked");
++
++ #if HAVE_LIBCAP
++ have_mac_admin = lxc_proc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE);
++ #endif
++
++ if (!have_mac_admin)
++ WARN("Per-container AppArmor profiles are disabled because the mac_admin capability is missing");
++ else if (am_host_unpriv() && !aa_is_stacked)
++ WARN("Per-container AppArmor profiles are disabled because LXC is running in an unprivileged container without stacking");
++ else
++ aa_admin = true;
++
++out:
+ aa_enabled = 1;
+ return &apparmor_drv;
+ }
+diff --git a/src/lxc/lsm/lsm.c b/src/lxc/lsm/lsm.c
+index 8d7de2db..46e21206 100644
+--- a/src/lxc/lsm/lsm.c
++++ b/src/lxc/lsm/lsm.c
+@@ -177,11 +177,37 @@ on_error:
+ }
+
+ int lsm_process_label_set(const char *label, struct lxc_conf *conf,
+- bool use_default, bool on_exec)
++ bool on_exec)
+ {
+ if (!drv) {
+ ERROR("LSM driver not inited");
+ return -1;
+ }
+- return drv->process_label_set(label, conf, use_default, on_exec);
++ return drv->process_label_set(label, conf, on_exec);
++}
++
++int lsm_process_prepare(struct lxc_conf *conf, const char *lxcpath)
++{
++ if (!drv) {
++ ERROR("LSM driver not inited");
++ return 0;
++ }
++
++ if (!drv->prepare)
++ return 0;
++
++ return drv->prepare(conf, lxcpath);
++}
++
++void lsm_process_cleanup(struct lxc_conf *conf, const char *lxcpath)
++{
++ if (!drv) {
++ ERROR("LSM driver not inited");
++ return;
++ }
++
++ if (!drv->cleanup)
++ return;
++
++ drv->cleanup(conf, lxcpath);
+ }
+diff --git a/src/lxc/lsm/lsm.h b/src/lxc/lsm/lsm.h
+index cafb2ac7..52e656d6 100644
+--- a/src/lxc/lsm/lsm.h
++++ b/src/lxc/lsm/lsm.h
+@@ -38,17 +38,21 @@ struct lsm_drv {
+ int (*enabled)(void);
+ char *(*process_label_get)(pid_t pid);
+ int (*process_label_set)(const char *label, struct lxc_conf *conf,
+- bool use_default, bool on_exec);
++ bool on_exec);
++ int (*prepare)(struct lxc_conf *conf, const char *lxcpath);
++ void (*cleanup)(struct lxc_conf *conf, const char *lxcpath);
+ };
+
+ extern void lsm_init(void);
+ extern int lsm_enabled(void);
+ extern const char *lsm_name(void);
+ extern char *lsm_process_label_get(pid_t pid);
++extern int lsm_process_prepare(struct lxc_conf *conf, const char *lxcpath);
+ extern int lsm_process_label_set(const char *label, struct lxc_conf *conf,
+- bool use_default, bool on_exec);
++ bool on_exec);
+ extern int lsm_process_label_fd_get(pid_t pid, bool on_exec);
+ extern int lsm_process_label_set_at(int label_fd, const char *label,
+ bool on_exec);
++extern void lsm_process_cleanup(struct lxc_conf *conf, const char *lxcpath);
+
+ #endif /* __LXC_LSM_H */
+diff --git a/src/lxc/lsm/nop.c b/src/lxc/lsm/nop.c
+index 7bb8121b..9397f2bf 100644
+--- a/src/lxc/lsm/nop.c
++++ b/src/lxc/lsm/nop.c
+@@ -30,7 +30,7 @@ static char *nop_process_label_get(pid_t pid)
+ }
+
+ static int nop_process_label_set(const char *label, struct lxc_conf *conf,
+- bool use_default, bool on_exec)
++ bool on_exec)
+ {
+ return 0;
+ }
+diff --git a/src/lxc/lsm/selinux.c b/src/lxc/lsm/selinux.c
+index c88c18e3..9f7b7bc3 100644
+--- a/src/lxc/lsm/selinux.c
++++ b/src/lxc/lsm/selinux.c
+@@ -75,15 +75,13 @@ static char *selinux_process_label_get(pid_t pid)
+ * Notes: This relies on /proc being available.
+ */
+ static int selinux_process_label_set(const char *inlabel, struct lxc_conf *conf,
+- bool use_default, bool on_exec)
++ bool on_exec)
+ {
+ int ret;
+ const char *label;
+
+ label = inlabel ? inlabel : conf->lsm_se_context;
+ if (!label) {
+- if (!use_default)
+- return -EINVAL;
+
+ label = DEFAULT_LABEL;
+ }
+diff --git a/src/lxc/start.c b/src/lxc/start.c
+index f67e3f66..71bc841b 100644
+--- a/src/lxc/start.c
++++ b/src/lxc/start.c
+@@ -863,9 +863,19 @@ int lxc_init(const char *name, struct lxc_handler *handler)
+ }
+ TRACE("Initialized cgroup driver");
+
++ ret = lsm_process_prepare(conf, handler->lxcpath);
++ if (ret < 0) {
++ ERROR("Failed to initialize LSM");
++ goto out_destroy_cgroups;
++ }
++ TRACE("Initialized LSM");
++
+ INFO("Container \"%s\" is initialized", name);
+ return 0;
+
++out_destroy_cgroups:
++ handler->cgroup_ops->destroy(handler->cgroup_ops, handler);
++
+ out_delete_terminal:
+ lxc_terminal_delete(&handler->conf->console);
+
+@@ -956,6 +966,8 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
+ while (namespace_count--)
+ free(namespaces[namespace_count]);
+
++ lsm_process_cleanup(handler->conf, handler->lxcpath);
++
+ cgroup_ops->destroy(cgroup_ops, handler);
+ cgroup_exit(cgroup_ops);
+
+@@ -1236,7 +1248,7 @@ static int do_start(void *data)
+ }
+
+ /* Set the label to change to when we exec(2) the container's init. */
+- ret = lsm_process_label_set(NULL, handler->conf, 1, 1);
++ ret = lsm_process_label_set(NULL, handler->conf, true);
+ if (ret < 0)
+ goto out_warn_father;
+
+--
+2.11.0
+
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Mon, 23 Jul 2018 17:23:08 +0200
-Subject: [PATCH] tests: lxc-test-apparmor-mount: show a log on error
-
-Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
-(cherry picked from commit d6523915861f2289505a11140874001099dfdfdc)
----
- src/tests/lxc-test-apparmor-mount | 24 ++++++++++++++++++++----
- 1 file changed, 20 insertions(+), 4 deletions(-)
-
-diff --git a/src/tests/lxc-test-apparmor-mount b/src/tests/lxc-test-apparmor-mount
-index ddcee8a7..144467c8 100755
---- a/src/tests/lxc-test-apparmor-mount
-+++ b/src/tests/lxc-test-apparmor-mount
-@@ -45,6 +45,7 @@ DONE=0
- KNOWN_RELEASES="precise trusty xenial yakkety zesty"
- MOUNTSR=/sys/kernel/security/apparmor/features/mount
- dnam=`mktemp -d`
-+logfile=`mktemp`
- cname=`basename $dnam`
- cleanup() {
- run_cmd lxc-destroy -f -n $cname || true
-@@ -56,12 +57,21 @@ cleanup() {
- rm -Rf $HDIR /run/user/$(id -u $TUSER)
- deluser $TUSER
- if [ $DONE -eq 0 ]; then
-+ echo 'Failed container log:' >&2
-+ cat "$logfile" >&2
-+ echo 'End log' >&2
-+ rm -f "$logfile"
- echo "FAIL"
- exit 1
- fi
-+ rm -f "$logfile"
- echo "PASS"
- }
-
-+clear_log() {
-+ truncate -s0 "$logfile"
-+}
-+
- trap cleanup exit
-
- # Only run on a normally configured ubuntu lxc system
-@@ -74,6 +84,8 @@ if [ "$(id -u)" != "0" ]; then
- exit 1
- fi
-
-+chmod 0666 "$logfile"
-+
- # This would be much simpler if we could run it as
- # root. However, in order to not have the bind mount
- # of an empty directory over the securitfs 'mount' directory
-@@ -160,7 +172,7 @@ fi
- run_cmd lxc-create -t download -n $cname -- -d ubuntu -r $release -a $ARCH
-
- echo "test default confined container"
--run_cmd lxc-start -n $cname -d
-+run_cmd lxc-start -n $cname -d -lDEBUG -o "$logfile"
- run_cmd lxc-wait -n $cname -s RUNNING
- pid=`run_cmd lxc-info -p -H -n $cname`
- profile=`cat /proc/$pid/attr/current`
-@@ -169,10 +181,11 @@ if [ "x$profile" != "x${default_profile}" ]; then
- exit 1
- fi
- run_cmd lxc-stop -n $cname -k
-+clear_log
-
- echo "test regular unconfined container"
- echo "lxc.apparmor.profile = unconfined" >> $HDIR/.local/share/lxc/$cname/config
--run_cmd lxc-start -n $cname -d
-+run_cmd lxc-start -n $cname -d -lDEBUG -o "$logfile"
- run_cmd lxc-wait -n $cname -s RUNNING
- pid=`run_cmd lxc-info -p -H -n $cname`
- profile=`cat /proc/$pid/attr/current`
-@@ -181,6 +194,7 @@ if [ "x$profile" != "xunconfined" ]; then
- exit 1
- fi
- run_cmd lxc-stop -n $cname -k
-+clear_log
-
- echo "masking $MOUNTSR"
- mount --bind $dnam $MOUNTSR
-@@ -198,7 +212,7 @@ fi
-
- echo "test regular unconfined container"
- echo "lxc.apparmor.profile = unconfined" >> $HDIR/.local/share/lxc/$cname/config
--run_cmd lxc-start -n $cname -d
-+run_cmd lxc-start -n $cname -d -lDEBUG -o "$logfile"
- run_cmd lxc-wait -n $cname -s RUNNING
- pid=`run_cmd lxc-info -p -H -n $cname`
- if [ "$pid" = "-1" ]; then
-@@ -211,11 +225,12 @@ if [ "x$profile" != "xunconfined" ]; then
- exit 1
- fi
- run_cmd lxc-stop -n $cname -k
-+clear_log
-
- echo "testing override"
- sed -i '/apparmor.profile/d' $HDIR/.local/share/lxc/$cname/config
- echo "lxc.apparmor.allow_incomplete = 1" >> $HDIR/.local/share/lxc/$cname/config
--run_cmd lxc-start -n $cname -d
-+run_cmd lxc-start -n $cname -d -lDEBUG -o "$logfile"
- run_cmd lxc-wait -n $cname -s RUNNING
- pid=`run_cmd lxc-info -p -H -n $cname`
- if [ "$pid" = "-1" ]; then
-@@ -228,5 +243,6 @@ if [ "x$profile" != "x${default_profile}" ]; then
- exit 1
- fi
- run_cmd lxc-stop -n $cname -k
-+clear_log
-
- DONE=1
---
-2.11.0
-
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Tue, 24 Jul 2018 13:59:04 +0200
+Subject: [PATCH] tests: add test for generated apparmor profiles
+
+Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
+(cherry picked from commit e7311a84e5bd0758931033b1a0ce649baa720a58)
+---
+ src/tests/Makefile.am | 2 +
+ src/tests/lxc-test-apparmor-generated | 84 +++++++++++++++++++++++++++++++++++
+ 2 files changed, 86 insertions(+)
+ create mode 100755 src/tests/lxc-test-apparmor-generated
+
+diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
+index a2179c3c..df5d35df 100644
+--- a/src/tests/Makefile.am
++++ b/src/tests/Makefile.am
+@@ -79,6 +79,7 @@ if DISTRO_UBUNTU
+ bin_SCRIPTS += \
+ lxc-test-lxc-attach \
+ lxc-test-apparmor-mount \
++ lxc-test-apparmor-generated \
+ lxc-test-checkpoint-restore \
+ lxc-test-snapdeps \
+ lxc-test-symlink \
+@@ -112,6 +113,7 @@ EXTRA_DIST = \
+ lxc-test-rootfs \
+ lxc-test-autostart \
+ lxc-test-apparmor-mount \
++ lxc-test-apparmor-generated \
+ lxc-test-checkpoint-restore \
+ lxc-test-cloneconfig \
+ lxc-test-createconfig \
+diff --git a/src/tests/lxc-test-apparmor-generated b/src/tests/lxc-test-apparmor-generated
+new file mode 100755
+index 00000000..be2e3261
+--- /dev/null
++++ b/src/tests/lxc-test-apparmor-generated
+@@ -0,0 +1,84 @@
++#!/bin/sh
++
++# lxc: linux Container library
++
++# This is a test script for generated apparmor profiles
++
++# 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 ! which apparmor_parser >/dev/null 2>&1; then
++ echo 'SKIP: test for generated apparmor profiles: apparmor_parser missing'
++fi
++exit 0
++
++DONE=0
++KNOWN_RELEASES="precise trusty xenial yakkety zesty"
++LOGFILE="/tmp/lxc-test-$$.log"
++cleanup() {
++ lxc-destroy -n $CONTAINER_NAME >/dev/null 2>&1 || true
++
++ if [ $DONE -eq 0 ]; then
++ [ -f "$LOGFILE" ] && cat "$LOGFILE" >&2
++ rm -f "$LOGFILE"
++ echo "FAIL"
++ exit 1
++ fi
++ rm -f "$LOGFILE"
++ echo "PASS"
++}
++
++ARCH=i386
++if type dpkg >/dev/null 2>&1; then
++ ARCH=$(dpkg --print-architecture)
++fi
++
++trap cleanup EXIT HUP INT TERM
++set -eu
++
++# Create a container
++CONTAINER_NAME=lxc-test-apparmor-generated
++
++# default release is trusty, or the systems release if recognized
++release=trusty
++if [ -f /etc/lsb-release ]; then
++ . /etc/lsb-release
++ rels=$(ubuntu-distro-info --supported 2>/dev/null) ||
++ rels="$KNOWN_RELEASES"
++ for r in $rels; do
++ [ "$DISTRIB_CODENAME" = "$r" ] && release="$r"
++ done
++fi
++
++lxc-create -t download -n $CONTAINER_NAME -B dir -- -d ubuntu -r $release -a $ARCH
++CONTAINER_PATH=$(dirname $(lxc-info -n $CONTAINER_NAME -c lxc.rootfs.path -H) | sed -e 's/dir://')
++cp $CONTAINER_PATH/config $CONTAINER_PATH/config.bak
++
++# Set the profile to be auto-generated
++echo "lxc.apparmor.profile = generated" >> $CONTAINER_PATH/config
++
++# Start it
++lxc-start -n $CONTAINER_NAME -lDEBUG -o "$LOGFILE"
++lxc-wait -n $CONTAINER_NAME -t 5 -s RUNNING || (echo "Container didn't start" && exit 1)
++pid=`lxc-info -p -H -n $CONTAINER_NAME`
++profile=`cat /proc/$pid/attr/current`
++expected_profile="lxc-${CONTAINER_NAME}_</var/lib/lxc>//&:lxc-${CONTAINER_NAME}_<-var-lib-lxc>:unconfined (enforce)"
++lxc-stop -n $CONTAINER_NAME -k
++if [ "x$profile" != "x$expected_profile" ]; then
++ echo "FAIL: container was in profile $profile" >&2
++ echo "expected profile: $expected_profile" >&2
++ exit 1
++fi
++
++DONE=1
+--
+2.11.0
+
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Tue, 24 Jul 2018 11:49:14 +0200
-Subject: [PATCH] tests: lxc-test-apparmor-mount: check environment early
-
-don't kill all my processes when running it as user...
-
-Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
-(cherry picked from commit 39e2cbec3cc9f49e3edd65b2aa4bdd3983a25fea)
----
- src/tests/lxc-test-apparmor-mount | 20 ++++++++++----------
- 1 file changed, 10 insertions(+), 10 deletions(-)
-
-diff --git a/src/tests/lxc-test-apparmor-mount b/src/tests/lxc-test-apparmor-mount
-index 144467c8..56d598f4 100755
---- a/src/tests/lxc-test-apparmor-mount
-+++ b/src/tests/lxc-test-apparmor-mount
-@@ -23,6 +23,16 @@
-
- set -e
-
-+# Only run on a normally configured ubuntu lxc system
-+if [ ! -d /sys/class/net/lxcbr0 ]; then
-+ echo "lxcbr0 is not configured."
-+ exit 1
-+fi
-+if [ "$(id -u)" != "0" ]; then
-+ echo "ERROR: Must run as root."
-+ exit 1
-+fi
-+
- if [ -f /proc/self/ns/cgroup ]; then
- default_profile="lxc-container-default-cgns (enforce)"
- else
-@@ -74,16 +84,6 @@ clear_log() {
-
- trap cleanup exit
-
--# Only run on a normally configured ubuntu lxc system
--if [ ! -d /sys/class/net/lxcbr0 ]; then
-- echo "lxcbr0 is not configured."
-- exit 1
--fi
--if [ "$(id -u)" != "0" ]; then
-- echo "ERROR: Must run as root."
-- exit 1
--fi
--
- chmod 0666 "$logfile"
-
- # This would be much simpler if we could run it as
---
-2.11.0
-
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Date: Mon, 20 Aug 2018 10:02:35 +0200
+Subject: [PATCH] conf: fix path/lxcpath mixups in tty setup
+
+Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
+Fixes: 6947153da ("conf: use mknod() to create dummy mount target")
+---
+ src/lxc/conf.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/src/lxc/conf.c b/src/lxc/conf.c
+index 8c9dce36..0f05bf4e 100644
+--- a/src/lxc/conf.c
++++ b/src/lxc/conf.c
+@@ -902,7 +902,7 @@ static int lxc_setup_ttys(struct lxc_conf *conf)
+ if (ret < 0 || (size_t)ret >= sizeof(lxcpath))
+ return -1;
+
+- ret = mknod(path, S_IFREG | 0000, 0);
++ ret = mknod(lxcpath, S_IFREG | 0000, 0);
+ if (ret < 0 && errno != EEXIST) {
+ SYSERROR("Failed to create \"%s\"", lxcpath);
+ return -1;
+@@ -916,12 +916,12 @@ static int lxc_setup_ttys(struct lxc_conf *conf)
+
+ ret = mount(tty->name, lxcpath, "none", MS_BIND, 0);
+ if (ret < 0) {
+- WARN("Failed to bind mount \"%s\" onto \"%s\"",
+- tty->name, path);
++ SYSWARN("Failed to bind mount \"%s\" onto \"%s\"",
++ tty->name, lxcpath);
+ continue;
+ }
+ DEBUG("Bind mounted \"%s\" onto \"%s\"", tty->name,
+- path);
++ lxcpath);
+
+ ret = snprintf(lxcpath, sizeof(lxcpath), "%s/tty%d",
+ ttydir, i + 1);
+--
+2.11.0
+
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Thu, 12 Jul 2018 15:16:40 +0200
-Subject: [PATCH] lsm: fixup lsm_process_label_set_at return values
-
-Always return -1 on error (some code paths returned -1, some
-returned negative error codes), don't assume 'errno' is set
-afterwards, as the function already prints errors and not
-all code paths will have a usable errno value.
-
-Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
-(cherry picked from commit c68d5b0dd63ea8226698ae3ff8a5336a60c171c3)
----
- src/lxc/lsm/apparmor.c | 2 +-
- src/lxc/lsm/lsm.c | 12 ++++++++----
- 2 files changed, 9 insertions(+), 5 deletions(-)
-
-diff --git a/src/lxc/lsm/apparmor.c b/src/lxc/lsm/apparmor.c
-index 1507917c..95b61943 100644
---- a/src/lxc/lsm/apparmor.c
-+++ b/src/lxc/lsm/apparmor.c
-@@ -241,7 +241,7 @@ static int apparmor_process_label_set(const char *inlabel, struct lxc_conf *conf
- ret = lsm_process_label_set_at(label_fd, label, on_exec);
- close(label_fd);
- if (ret < 0) {
-- SYSERROR("Failed to change apparmor profile to %s", label);
-+ ERROR("Failed to change apparmor profile to %s", label);
- return -1;
- }
-
-diff --git a/src/lxc/lsm/lsm.c b/src/lxc/lsm/lsm.c
-index f4500ae2..8d7de2db 100644
---- a/src/lxc/lsm/lsm.c
-+++ b/src/lxc/lsm/lsm.c
-@@ -142,18 +142,20 @@ int lsm_process_label_set_at(int label_fd, const char *label, bool on_exec)
-
- if (on_exec) {
- ERROR("Changing AppArmor profile on exec not supported");
-- return -EINVAL;
-+ return -1;
- }
-
- len = strlen(label) + strlen("changeprofile ") + 1;
- command = malloc(len);
- if (!command)
-- return -1;
-+ goto on_error;
-
- ret = snprintf(command, len, "changeprofile %s", label);
- if (ret < 0 || (size_t)ret >= len) {
-+ int saved_errno = errno;
- free(command);
-- return -1;
-+ errno = saved_errno;
-+ goto on_error;
- }
-
- ret = lxc_write_nointr(label_fd, command, len - 1);
-@@ -161,9 +163,11 @@ int lsm_process_label_set_at(int label_fd, const char *label, bool on_exec)
- } else if (strcmp(name, "SELinux") == 0) {
- ret = lxc_write_nointr(label_fd, label, strlen(label));
- } else {
-- ret = -EINVAL;
-+ errno = EINVAL;
-+ ret = -1;
- }
- if (ret < 0) {
-+on_error:
- SYSERROR("Failed to set %s label \"%s\"", name, label);
- return -1;
- }
---
-2.11.0
-
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 25 Jul 2018 12:06:16 +0200
-Subject: [PATCH] apparmor: use fopen_cloexec
-
-Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
-(cherry picked from commit 7e556d185c49ff99825612bc7d6c93afc34113c8)
----
- src/lxc/lsm/apparmor.c | 5 +++--
- 1 file changed, 3 insertions(+), 2 deletions(-)
-
-diff --git a/src/lxc/lsm/apparmor.c b/src/lxc/lsm/apparmor.c
-index 95b61943..5fe6d525 100644
---- a/src/lxc/lsm/apparmor.c
-+++ b/src/lxc/lsm/apparmor.c
-@@ -32,6 +32,7 @@
- #include "lsm.h"
- #include "conf.h"
- #include "utils.h"
-+#include "initutils.h"
-
- lxc_log_define(apparmor, lsm);
-
-@@ -68,7 +69,7 @@ static int apparmor_enabled(void)
- char e;
- int ret;
-
-- fin = fopen(AA_ENABLED_FILE, "r");
-+ fin = fopen_cloexec(AA_ENABLED_FILE, "r");
- if (!fin)
- return 0;
- ret = fscanf(fin, "%c", &e);
-@@ -95,7 +96,7 @@ static char *apparmor_process_label_get(pid_t pid)
- return NULL;
- }
- again:
-- f = fopen(path, "r");
-+ f = fopen_cloexec(path, "r");
- if (!f) {
- SYSERROR("opening %s", path);
- free(buf);
---
-2.11.0
-
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 18 Jul 2018 12:43:37 +0200
-Subject: [PATCH] utils: add must_concat helper
-
-Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
-(cherry picked from commit eb5c2e6aeef233c4f3349d182e5c25315cca8de8)
----
- src/lxc/utils.c | 24 ++++++++++++++++++++++++
- src/lxc/utils.h | 1 +
- 2 files changed, 25 insertions(+)
-
-diff --git a/src/lxc/utils.c b/src/lxc/utils.c
-index dd6cdc91..9771b900 100644
---- a/src/lxc/utils.c
-+++ b/src/lxc/utils.c
-@@ -2426,6 +2426,30 @@ int run_command(char *buf, size_t buf_size, int (*child_fn)(void *), void *args)
- return fret;
- }
-
-+char *must_concat(const char *first, ...)
-+{
-+ va_list args;
-+ char *cur, *dest;
-+ size_t cur_len, it_len;
-+
-+ dest = must_copy_string(first);
-+ cur_len = it_len = strlen(first);
-+
-+ va_start(args, first);
-+ while ((cur = va_arg(args, char *)) != NULL) {
-+ it_len = strlen(cur);
-+
-+ dest = must_realloc(dest, cur_len + it_len + 1);
-+
-+ (void)memcpy(dest + cur_len, cur, it_len);
-+ cur_len += it_len;
-+ }
-+ va_end(args);
-+
-+ dest[cur_len] = 0;
-+ return dest;
-+}
-+
- char *must_make_path(const char *first, ...)
- {
- va_list args;
-diff --git a/src/lxc/utils.h b/src/lxc/utils.h
-index 295e7862..ed486853 100644
---- a/src/lxc/utils.h
-+++ b/src/lxc/utils.h
-@@ -567,6 +567,7 @@ extern int run_command(char *buf, size_t buf_size, int (*child_fn)(void *),
- /* Concatenate all passed-in strings into one path. Do not fail. If any piece
- * is not prefixed with '/', add a '/'.
- */
-+__attribute__((sentinel)) extern char *must_concat(const char *first, ...);
- __attribute__((sentinel)) extern char *must_make_path(const char *first, ...);
- __attribute__((sentinel)) extern char *must_append_path(char *first, ...);
-
---
-2.11.0
-
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 25 Jul 2018 12:11:23 +0200
-Subject: [PATCH] apparmor: update current profiles
-
-remove cgmanager rules and add fstype=cgroup2 variants for
-the existing fstype=cgroup rules
-
-Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
-(cherry picked from commit 6e6aca3e3e71ae0cfad69456acd1dc503feaf964)
----
- config/apparmor/abstractions/container-base.in | 1 -
- config/apparmor/profiles/lxc-default-cgns | 1 +
- config/apparmor/profiles/lxc-default-with-nesting | 1 +
- 3 files changed, 2 insertions(+), 1 deletion(-)
-
-diff --git a/config/apparmor/abstractions/container-base.in b/config/apparmor/abstractions/container-base.in
-index 54f9ddf0..0844fdbb 100644
---- a/config/apparmor/abstractions/container-base.in
-+++ b/config/apparmor/abstractions/container-base.in
-@@ -84,7 +84,6 @@
- mount fstype=sysfs -> /sys/,
- deny /sys/firmware/efi/efivars/** rwklx,
- deny /sys/kernel/security/** rwklx,
-- mount options=(move) /sys/fs/cgroup/cgmanager/ -> /sys/fs/cgroup/cgmanager.lower/,
- mount options=(ro, nosuid, nodev, noexec, remount, strictatime) -> /sys/fs/cgroup/,
-
- # deny reads from debugfs
-diff --git a/config/apparmor/profiles/lxc-default-cgns b/config/apparmor/profiles/lxc-default-cgns
-index ff599ef8..f69eb994 100644
---- a/config/apparmor/profiles/lxc-default-cgns
-+++ b/config/apparmor/profiles/lxc-default-cgns
-@@ -9,4 +9,5 @@ profile lxc-container-default-cgns flags=(attach_disconnected,mediate_deleted) {
- # the newinstance option (but, right now, we don't).
- deny mount fstype=devpts,
- mount fstype=cgroup -> /sys/fs/cgroup/**,
-+ mount fstype=cgroup2 -> /sys/fs/cgroup/**,
- }
-diff --git a/config/apparmor/profiles/lxc-default-with-nesting b/config/apparmor/profiles/lxc-default-with-nesting
-index 6e5745f9..cd198beb 100644
---- a/config/apparmor/profiles/lxc-default-with-nesting
-+++ b/config/apparmor/profiles/lxc-default-with-nesting
-@@ -11,4 +11,5 @@ profile lxc-container-default-with-nesting flags=(attach_disconnected,mediate_de
- mount fstype=sysfs -> /var/cache/lxc/**,
- mount options=(rw,bind),
- mount fstype=cgroup -> /sys/fs/cgroup/**,
-+ mount fstype=cgroup2 -> /sys/fs/cgroup/**,
- }
---
-2.11.0
-
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Wed, 25 Jul 2018 12:11:31 +0200
-Subject: [PATCH] apparmor: profile generation
-
-This copies lxd's apparmor profile generation. This tries to
-detect features such as cgroup namespaces, apparmor
-namespaces and stacking support, and has profile parts
-conditionally for unprivileged containers.
-
-This introduces the following changes to the configuration:
- lxc.apparmor.profile = generated
- The fixed value 'generated' will cause this
- functionality to be used, otherwise there should be no
- functional changes happening unless specifically
- requested with the next key:
- lxc.apparmor.allow_nesting
- This is a boolean which, if enabled, causes the
- following changes: When generated apparmor profiles are
- used, they will contain the necessary changes to allow
- creating a nested container. In addition to the usual
- mount points, /dev/.lxc/proc and /dev/.lxc/sys will
- contain procfs and sysfs mount points without the lxcfs
- overlays, which, if generated apparmor profiles are
- being used, will not be read/writable directly.
- lxc.apparmor.raw
- A list of raw apparmor profile lines to append to the
- profile. Only valid when using generated profiles.
-
-The following apparmor profile lines have not been copied
-from lxd:
-
- mount /var/lib/lxd/shmounts/ -> /var/lib/lxd/shmounts/,
- mount none -> /var/lib/lxd/shmounts/,
- mount options=bind /var/lib/lxd/shmounts/** -> /var/lib/lxd/**,
-
-They should be added via lxc.apparmor.raw entries by lxd.
-
-In order for apparmor_parser's cache to be of use, this adds
-a --with-apparmor-cache-dir ./configure option.
-
-Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
-(cherry picked from commit 1800f9247357948fd11c9da73b1943a8a7b6882b)
----
- configure.ac | 8 +
- src/lxc/Makefile.am | 1 +
- src/lxc/conf.c | 43 ++-
- src/lxc/conf.h | 8 +-
- src/lxc/confile.c | 95 +++++
- src/lxc/criu.c | 3 +-
- src/lxc/lsm/apparmor.c | 974 ++++++++++++++++++++++++++++++++++++++++++++++---
- src/lxc/lsm/lsm.c | 30 +-
- src/lxc/lsm/lsm.h | 8 +-
- src/lxc/lsm/nop.c | 2 +-
- src/lxc/lsm/selinux.c | 4 +-
- src/lxc/start.c | 14 +-
- 12 files changed, 1134 insertions(+), 56 deletions(-)
-
-diff --git a/configure.ac b/configure.ac
-index ea312bf3..739e100a 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -469,6 +469,13 @@ AC_ARG_WITH([cgroup-pattern],
- [pattern for container cgroups]
- )], [], [with_cgroup_pattern=['lxc/%n']])
-
-+# The path for the apparmor_parser's cache for generated apparmor profiles
-+AC_ARG_WITH([apparmor-cache-dir],
-+ [AC_HELP_STRING(
-+ [--with-apparmor-cache-dir=dir],
-+ [path for apparmor_parser cache]
-+ )], [], [with_apparmor_cache_dir=['${localstatedir}/cache/lxc/apparmor']])
-+
- # Container log path. By default, use $lxcpath.
- AC_MSG_CHECKING([Whether to place logfiles in container config path])
- AC_ARG_ENABLE([configpath-log],
-@@ -515,6 +522,7 @@ AS_AC_EXPAND(LXCBINHOOKDIR, "$libexecdir/lxc/hooks")
- AS_AC_EXPAND(LXCINITDIR, "$libexecdir")
- AS_AC_EXPAND(LOGPATH, "$with_log_path")
- AS_AC_EXPAND(RUNTIME_PATH, "$with_runtime_path")
-+AS_AC_EXPAND(APPARMOR_CACHE_DIR, "$with_apparmor_cache_dir")
- AC_SUBST(DEFAULT_CGROUP_PATTERN, ["$with_cgroup_pattern"])
-
- # We need the install path so criu knows where to reference the hook scripts.
-diff --git a/src/lxc/Makefile.am b/src/lxc/Makefile.am
-index c5e46ac2..1359eb3e 100644
---- a/src/lxc/Makefile.am
-+++ b/src/lxc/Makefile.am
-@@ -174,6 +174,7 @@ AM_CFLAGS = -DLXCROOTFSMOUNT=\"$(LXCROOTFSMOUNT)\" \
- -DDEFAULT_CGROUP_PATTERN=\"$(DEFAULT_CGROUP_PATTERN)\" \
- -DRUNTIME_PATH=\"$(RUNTIME_PATH)\" \
- -DSBINDIR=\"$(SBINDIR)\" \
-+ -DAPPARMOR_CACHE_DIR=\"$(APPARMOR_CACHE_DIR)\" \
- -I $(top_srcdir)/src \
- -I $(top_srcdir)/src/lxc \
- -I $(top_srcdir)/src/lxc/storage \
-diff --git a/src/lxc/conf.c b/src/lxc/conf.c
-index d36987c8..df805e6c 100644
---- a/src/lxc/conf.c
-+++ b/src/lxc/conf.c
-@@ -2334,7 +2334,23 @@ static int setup_mount(const struct lxc_conf *conf,
- return ret;
- }
-
--FILE *make_anonymous_mount_file(struct lxc_list *mount)
-+/*
-+ * In order for nested containers to be able to mount /proc and /sys they need
-+ * to see a "pure" proc and sysfs mount points with nothing mounted on top
-+ * (like lxcfs).
-+ * For this we provide proc and sysfs in /dev/.lxc/{proc,sys} while using an
-+ * apparmor rule to deny access to them. This is mostly for convenience: The
-+ * container's root user can mount them anyway and thus has access to the two
-+ * file systems. But a non-root user in the container should not be allowed to
-+ * access them as a side effect without explicitly allowing it.
-+ */
-+static const char nesting_helpers[] =
-+"proc dev/.lxc/proc proc create=dir,optional\n"
-+"sys dev/.lxc/sys sysfs create=dir,optional\n"
-+;
-+
-+FILE *make_anonymous_mount_file(struct lxc_list *mount,
-+ bool include_nesting_helpers)
- {
- int ret;
- char *mount_entry;
-@@ -2376,6 +2392,13 @@ FILE *make_anonymous_mount_file(struct lxc_list *mount)
- goto on_error;
- }
-
-+ if (include_nesting_helpers) {
-+ ret = lxc_write_nointr(fd, nesting_helpers,
-+ sizeof(nesting_helpers) - 1);
-+ if (ret != sizeof(nesting_helpers) - 1)
-+ goto on_error;
-+ }
-+
- ret = lseek(fd, 0, SEEK_SET);
- if (ret < 0)
- goto on_error;
-@@ -2396,7 +2419,7 @@ static int setup_mount_entries(const struct lxc_conf *conf,
- int ret;
- FILE *f;
-
-- f = make_anonymous_mount_file(mount);
-+ f = make_anonymous_mount_file(mount, conf->lsm_aa_allow_nesting);
- if (!f)
- return -1;
-
-@@ -2712,6 +2735,7 @@ struct lxc_conf *lxc_conf_init(void)
- lxc_list_init(&new->groups);
- lxc_list_init(&new->state_clients);
- new->lsm_aa_profile = NULL;
-+ lxc_list_init(&new->lsm_aa_raw);
- new->lsm_se_context = NULL;
- new->tmp_umount_proc = false;
-
-@@ -3996,6 +4020,19 @@ void lxc_clear_includes(struct lxc_conf *conf)
- }
- }
-
-+int lxc_clear_apparmor_raw(struct lxc_conf *c)
-+{
-+ struct lxc_list *it, *next;
-+
-+ lxc_list_for_each_safe (it, &c->lsm_aa_raw, next) {
-+ lxc_list_del(it);
-+ free(it->elem);
-+ free(it);
-+ }
-+
-+ return 0;
-+}
-+
- void lxc_conf_free(struct lxc_conf *conf)
- {
- if (!conf)
-@@ -4023,6 +4060,7 @@ void lxc_conf_free(struct lxc_conf *conf)
- free(conf->syslog);
- lxc_free_networks(&conf->network);
- free(conf->lsm_aa_profile);
-+ free(conf->lsm_aa_profile_computed);
- free(conf->lsm_se_context);
- lxc_seccomp_free(conf);
- lxc_clear_config_caps(conf);
-@@ -4039,6 +4077,7 @@ void lxc_conf_free(struct lxc_conf *conf)
- lxc_clear_limits(conf, "lxc.prlimit");
- lxc_clear_sysctls(conf, "lxc.sysctl");
- lxc_clear_procs(conf, "lxc.proc");
-+ lxc_clear_apparmor_raw(conf);
- free(conf->cgroup_meta.dir);
- free(conf->cgroup_meta.controllers);
- free(conf);
-diff --git a/src/lxc/conf.h b/src/lxc/conf.h
-index f7a879c3..f5085d8c 100644
---- a/src/lxc/conf.h
-+++ b/src/lxc/conf.h
-@@ -272,7 +272,11 @@ struct lxc_conf {
- };
-
- char *lsm_aa_profile;
-+ char *lsm_aa_profile_computed;
-+ bool lsm_aa_profile_created;
-+ unsigned int lsm_aa_allow_nesting;
- unsigned int lsm_aa_allow_incomplete;
-+ struct lxc_list lsm_aa_raw;
- char *lsm_se_context;
- bool tmp_umount_proc;
- char *seccomp; /* filename with the seccomp rules */
-@@ -417,7 +421,8 @@ extern int parse_mntopts(const char *mntopts, unsigned long *mntflags,
- extern void tmp_proc_unmount(struct lxc_conf *lxc_conf);
- extern void remount_all_slave(void);
- extern void suggest_default_idmap(void);
--extern FILE *make_anonymous_mount_file(struct lxc_list *mount);
-+extern FILE *make_anonymous_mount_file(struct lxc_list *mount,
-+ bool include_nesting_helpers);
- extern struct lxc_list *sort_cgroup_settings(struct lxc_list *cgroup_settings);
- extern unsigned long add_required_remount_flags(const char *s, const char *d,
- unsigned long flags);
-@@ -431,5 +436,6 @@ extern int setup_sysctl_parameters(struct lxc_list *sysctls);
- extern int lxc_clear_sysctls(struct lxc_conf *c, const char *key);
- extern int setup_proc_filesystem(struct lxc_list *procs, pid_t pid);
- extern int lxc_clear_procs(struct lxc_conf *c, const char *key);
-+extern int lxc_clear_apparmor_raw(struct lxc_conf *c);
-
- #endif /* __LXC_CONF_H */
-diff --git a/src/lxc/confile.c b/src/lxc/confile.c
-index 8a7505da..097b8806 100644
---- a/src/lxc/confile.c
-+++ b/src/lxc/confile.c
-@@ -84,7 +84,9 @@ lxc_log_define(confile, lxc);
-
- lxc_config_define(autodev);
- lxc_config_define(apparmor_allow_incomplete);
-+lxc_config_define(apparmor_allow_nesting);
- lxc_config_define(apparmor_profile);
-+lxc_config_define(apparmor_raw);
- lxc_config_define(cap_drop);
- lxc_config_define(cap_keep);
- lxc_config_define(cgroup_controller);
-@@ -158,6 +160,8 @@ static struct lxc_config_t config[] = {
- { "lxc.arch", set_config_personality, get_config_personality, clr_config_personality, },
- { "lxc.apparmor.profile", set_config_apparmor_profile, get_config_apparmor_profile, clr_config_apparmor_profile, },
- { "lxc.apparmor.allow_incomplete", set_config_apparmor_allow_incomplete, get_config_apparmor_allow_incomplete, clr_config_apparmor_allow_incomplete, },
-+ { "lxc.apparmor.allow_nesting", set_config_apparmor_allow_nesting, get_config_apparmor_allow_nesting, clr_config_apparmor_allow_nesting, },
-+ { "lxc.apparmor.raw", set_config_apparmor_raw, get_config_apparmor_raw, clr_config_apparmor_raw, },
- { "lxc.autodev", set_config_autodev, get_config_autodev, clr_config_autodev, },
- { "lxc.cap.drop", set_config_cap_drop, get_config_cap_drop, clr_config_cap_drop, },
- { "lxc.cap.keep", set_config_cap_keep, get_config_cap_keep, clr_config_cap_keep, },
-@@ -1118,6 +1122,52 @@ static int set_config_apparmor_allow_incomplete(const char *key,
- return 0;
- }
-
-+static int set_config_apparmor_allow_nesting(const char *key,
-+ const char *value,
-+ struct lxc_conf *lxc_conf,
-+ void *data)
-+{
-+ if (lxc_config_value_empty(value))
-+ return clr_config_apparmor_allow_nesting(key, lxc_conf, NULL);
-+
-+ if (lxc_safe_uint(value, &lxc_conf->lsm_aa_allow_nesting) < 0)
-+ return -1;
-+
-+ if (lxc_conf->lsm_aa_allow_nesting > 1)
-+ return -1;
-+
-+ return 0;
-+}
-+
-+static int set_config_apparmor_raw(const char *key,
-+ const char *value,
-+ struct lxc_conf *lxc_conf,
-+ void *data)
-+{
-+ char *elem;
-+ struct lxc_list *list;
-+
-+ if (lxc_config_value_empty(value))
-+ return lxc_clear_apparmor_raw(lxc_conf);
-+
-+ list = malloc(sizeof(*list));
-+ if (!list) {
-+ errno = ENOMEM;
-+ return -1;
-+ }
-+
-+ elem = strdup(value);
-+ if (!elem) {
-+ free(list);
-+ return -1;
-+ }
-+ list->elem = elem;
-+
-+ lxc_list_add_tail(&lxc_conf->lsm_aa_raw, list);
-+
-+ return 0;
-+}
-+
- static int set_config_selinux_context(const char *key, const char *value,
- struct lxc_conf *lxc_conf, void *data)
- {
-@@ -2959,6 +3009,34 @@ static int get_config_apparmor_allow_incomplete(const char *key, char *retv,
- c->lsm_aa_allow_incomplete);
- }
-
-+static int get_config_apparmor_allow_nesting(const char *key, char *retv,
-+ int inlen, struct lxc_conf *c,
-+ void *data)
-+{
-+ return lxc_get_conf_int(c, retv, inlen,
-+ c->lsm_aa_allow_nesting);
-+}
-+
-+static int get_config_apparmor_raw(const char *key, char *retv,
-+ int inlen, struct lxc_conf *c,
-+ void *data)
-+{
-+ int len;
-+ struct lxc_list *it;
-+ int fulllen = 0;
-+
-+ if (!retv)
-+ inlen = 0;
-+ else
-+ memset(retv, 0, inlen);
-+
-+ lxc_list_for_each(it, &c->lsm_aa_raw) {
-+ strprint(retv, inlen, "%s\n", (char *)it->elem);
-+ }
-+
-+ return fulllen;
-+}
-+
- static int get_config_selinux_context(const char *key, char *retv, int inlen,
- struct lxc_conf *c, void *data)
- {
-@@ -3749,6 +3827,21 @@ static inline int clr_config_apparmor_allow_incomplete(const char *key,
- return 0;
- }
-
-+static inline int clr_config_apparmor_allow_nesting(const char *key,
-+ struct lxc_conf *c,
-+ void *data)
-+{
-+ c->lsm_aa_allow_nesting = 0;
-+ return 0;
-+}
-+
-+static inline int clr_config_apparmor_raw(const char *key,
-+ struct lxc_conf *c,
-+ void *data)
-+{
-+ return lxc_clear_apparmor_raw(c);
-+}
-+
- static inline int clr_config_selinux_context(const char *key,
- struct lxc_conf *c, void *data)
- {
-@@ -4941,7 +5034,9 @@ int lxc_list_subkeys(struct lxc_conf *conf, const char *key, char *retv,
-
- if (!strcmp(key, "lxc.apparmor")) {
- strprint(retv, inlen, "allow_incomplete\n");
-+ strprint(retv, inlen, "allow_nesting\n");
- strprint(retv, inlen, "profile\n");
-+ strprint(retv, inlen, "raw\n");
- } else if (!strcmp(key, "lxc.cgroup")) {
- strprint(retv, inlen, "dir\n");
- } else if (!strcmp(key, "lxc.selinux")) {
-diff --git a/src/lxc/criu.c b/src/lxc/criu.c
-index 456d19cf..02d301ac 100644
---- a/src/lxc/criu.c
-+++ b/src/lxc/criu.c
-@@ -378,7 +378,8 @@ static void exec_criu(struct cgroup_ops *cgroup_ops, struct criu_opts *opts)
- DECLARE_ARG(opts->user->action_script);
- }
-
-- mnts = make_anonymous_mount_file(&opts->c->lxc_conf->mount_list);
-+ mnts = make_anonymous_mount_file(&opts->c->lxc_conf->mount_list,
-+ opts->c->lxc_conf->lsm_aa_allow_nesting);
- if (!mnts)
- goto err;
-
-diff --git a/src/lxc/lsm/apparmor.c b/src/lxc/lsm/apparmor.c
-index 5fe6d525..ec3f805d 100644
---- a/src/lxc/lsm/apparmor.c
-+++ b/src/lxc/lsm/apparmor.c
-@@ -33,11 +33,18 @@
- #include "conf.h"
- #include "utils.h"
- #include "initutils.h"
-+#include "caps.h"
-+#include "parse.h"
-
- lxc_log_define(apparmor, lsm);
-
- /* set by lsm_apparmor_drv_init if true */
- static int aa_enabled = 0;
-+static bool aa_parser_available = false;
-+static bool aa_supports_unix = false;
-+static bool aa_can_stack = false;
-+static bool aa_is_stacked = false;
-+static bool aa_admin = false;
-
- static int mount_features_enabled = 0;
-
-@@ -46,6 +53,332 @@ static int mount_features_enabled = 0;
- #define AA_MOUNT_RESTR "/sys/kernel/security/apparmor/features/mount/mask"
- #define AA_ENABLED_FILE "/sys/module/apparmor/parameters/enabled"
- #define AA_UNCHANGED "unchanged"
-+#define AA_GENERATED "generated"
-+
-+#define AA_CMD_LOAD 'r'
-+#define AA_CMD_UNLOAD 'R'
-+#define AA_CMD_PARSE 'Q'
-+
-+static const char AA_PROFILE_BASE[] =
-+" ### Base profile\n"
-+" capability,\n"
-+" dbus,\n"
-+" file,\n"
-+" network,\n"
-+" umount,\n"
-+"\n"
-+" # Allow us to receive signals from anywhere.\n"
-+" signal (receive),\n"
-+"\n"
-+" # Allow us to send signals to ourselves\n"
-+" signal peer=@{profile_name},\n"
-+"\n"
-+" # Allow other processes to read our /proc entries, futexes, perf tracing and\n"
-+" # kcmp for now (they will need 'read' in the first place). Administrators can\n"
-+" # override with:\n"
-+" # deny ptrace (readby) ...\n"
-+" ptrace (readby),\n"
-+"\n"
-+" # Allow other processes to trace us by default (they will need 'trace' in\n"
-+" # the first place). Administrators can override with:\n"
-+" # deny ptrace (tracedby) ...\n"
-+" ptrace (tracedby),\n"
-+"\n"
-+" # Allow us to ptrace ourselves\n"
-+" ptrace peer=@{profile_name},\n"
-+"\n"
-+" # ignore DENIED message on / remount\n"
-+" deny mount options=(ro, remount) -> /,\n"
-+" deny mount options=(ro, remount, silent) -> /,\n"
-+"\n"
-+" # allow tmpfs mounts everywhere\n"
-+" mount fstype=tmpfs,\n"
-+"\n"
-+" # allow hugetlbfs mounts everywhere\n"
-+" mount fstype=hugetlbfs,\n"
-+"\n"
-+" # allow mqueue mounts everywhere\n"
-+" mount fstype=mqueue,\n"
-+"\n"
-+" # allow fuse mounts everywhere\n"
-+" mount fstype=fuse,\n"
-+" mount fstype=fuse.*,\n"
-+"\n"
-+" # deny access under /proc/bus to avoid e.g. messing with pci devices directly\n"
-+" deny @{PROC}/bus/** wklx,\n"
-+"\n"
-+" # deny writes in /proc/sys/fs but allow binfmt_misc to be mounted\n"
-+" mount fstype=binfmt_misc -> /proc/sys/fs/binfmt_misc/,\n"
-+" deny @{PROC}/sys/fs/** wklx,\n"
-+"\n"
-+" # allow efivars to be mounted, writing to it will be blocked though\n"
-+" mount fstype=efivarfs -> /sys/firmware/efi/efivars/,\n"
-+"\n"
-+" # block some other dangerous paths\n"
-+" deny @{PROC}/kcore rwklx,\n"
-+" deny @{PROC}/sysrq-trigger rwklx,\n"
-+"\n"
-+" # deny writes in /sys except for /sys/fs/cgroup, also allow\n"
-+" # fusectl, securityfs and debugfs to be mounted there (read-only)\n"
-+" mount fstype=fusectl -> /sys/fs/fuse/connections/,\n"
-+" mount fstype=securityfs -> /sys/kernel/security/,\n"
-+" mount fstype=debugfs -> /sys/kernel/debug/,\n"
-+" deny mount fstype=debugfs -> /var/lib/ureadahead/debugfs/,\n"
-+" mount fstype=proc -> /proc/,\n"
-+" mount fstype=sysfs -> /sys/,\n"
-+" mount options=(rw, nosuid, nodev, noexec, remount) -> /sys/,\n"
-+" deny /sys/firmware/efi/efivars/** rwklx,\n"
-+" # note, /sys/kernel/security/** handled below\n"
-+" mount options=(ro, nosuid, nodev, noexec, remount, strictatime) -> /sys/fs/cgroup/,\n"
-+"\n"
-+" # deny reads from debugfs\n"
-+" deny /sys/kernel/debug/{,**} rwklx,\n"
-+"\n"
-+" # allow paths to be made slave, shared, private or unbindable\n"
-+" # FIXME: This currently doesn't work due to the apparmor parser treating those as allowing all mounts.\n"
-+"# mount options=(rw,make-slave) -> **,\n"
-+"# mount options=(rw,make-rslave) -> **,\n"
-+"# mount options=(rw,make-shared) -> **,\n"
-+"# mount options=(rw,make-rshared) -> **,\n"
-+"# mount options=(rw,make-private) -> **,\n"
-+"# mount options=(rw,make-rprivate) -> **,\n"
-+"# mount options=(rw,make-unbindable) -> **,\n"
-+"# mount options=(rw,make-runbindable) -> **,\n"
-+"\n"
-+" # allow bind-mounts of anything except /proc, /sys and /dev\n"
-+" mount options=(rw,bind) /[^spd]*{,/**},\n"
-+" mount options=(rw,bind) /d[^e]*{,/**},\n"
-+" mount options=(rw,bind) /de[^v]*{,/**},\n"
-+" mount options=(rw,bind) /dev/.[^l]*{,/**},\n"
-+" mount options=(rw,bind) /dev/.l[^x]*{,/**},\n"
-+" mount options=(rw,bind) /dev/.lx[^c]*{,/**},\n"
-+" mount options=(rw,bind) /dev/.lxc?*{,/**},\n"
-+" mount options=(rw,bind) /dev/[^.]*{,/**},\n"
-+" mount options=(rw,bind) /dev?*{,/**},\n"
-+" mount options=(rw,bind) /p[^r]*{,/**},\n"
-+" mount options=(rw,bind) /pr[^o]*{,/**},\n"
-+" mount options=(rw,bind) /pro[^c]*{,/**},\n"
-+" mount options=(rw,bind) /proc?*{,/**},\n"
-+" mount options=(rw,bind) /s[^y]*{,/**},\n"
-+" mount options=(rw,bind) /sy[^s]*{,/**},\n"
-+" mount options=(rw,bind) /sys?*{,/**},\n"
-+"\n"
-+" # allow read-only bind-mounts of anything except /proc, /sys and /dev\n"
-+" mount options=(ro,remount,bind) -> /[^spd]*{,/**},\n"
-+" mount options=(ro,remount,bind) -> /d[^e]*{,/**},\n"
-+" mount options=(ro,remount,bind) -> /de[^v]*{,/**},\n"
-+" mount options=(ro,remount,bind) -> /dev/.[^l]*{,/**},\n"
-+" mount options=(ro,remount,bind) -> /dev/.l[^x]*{,/**},\n"
-+" mount options=(ro,remount,bind) -> /dev/.lx[^c]*{,/**},\n"
-+" mount options=(ro,remount,bind) -> /dev/.lxc?*{,/**},\n"
-+" mount options=(ro,remount,bind) -> /dev/[^.]*{,/**},\n"
-+" mount options=(ro,remount,bind) -> /dev?*{,/**},\n"
-+" mount options=(ro,remount,bind) -> /p[^r]*{,/**},\n"
-+" mount options=(ro,remount,bind) -> /pr[^o]*{,/**},\n"
-+" mount options=(ro,remount,bind) -> /pro[^c]*{,/**},\n"
-+" mount options=(ro,remount,bind) -> /proc?*{,/**},\n"
-+" mount options=(ro,remount,bind) -> /s[^y]*{,/**},\n"
-+" mount options=(ro,remount,bind) -> /sy[^s]*{,/**},\n"
-+" mount options=(ro,remount,bind) -> /sys?*{,/**},\n"
-+"\n"
-+" # allow moving mounts except for /proc, /sys and /dev\n"
-+" mount options=(rw,move) /[^spd]*{,/**},\n"
-+" mount options=(rw,move) /d[^e]*{,/**},\n"
-+" mount options=(rw,move) /de[^v]*{,/**},\n"
-+" mount options=(rw,move) /dev/.[^l]*{,/**},\n"
-+" mount options=(rw,move) /dev/.l[^x]*{,/**},\n"
-+" mount options=(rw,move) /dev/.lx[^c]*{,/**},\n"
-+" mount options=(rw,move) /dev/.lxc?*{,/**},\n"
-+" mount options=(rw,move) /dev/[^.]*{,/**},\n"
-+" mount options=(rw,move) /dev?*{,/**},\n"
-+" mount options=(rw,move) /p[^r]*{,/**},\n"
-+" mount options=(rw,move) /pr[^o]*{,/**},\n"
-+" mount options=(rw,move) /pro[^c]*{,/**},\n"
-+" mount options=(rw,move) /proc?*{,/**},\n"
-+" mount options=(rw,move) /s[^y]*{,/**},\n"
-+" mount options=(rw,move) /sy[^s]*{,/**},\n"
-+" mount options=(rw,move) /sys?*{,/**},\n"
-+"\n"
-+" # generated by: lxc-generate-aa-rules.py container-rules.base\n"
-+" deny /proc/sys/[^kn]*{,/**} wklx,\n"
-+" deny /proc/sys/k[^e]*{,/**} wklx,\n"
-+" deny /proc/sys/ke[^r]*{,/**} wklx,\n"
-+" deny /proc/sys/ker[^n]*{,/**} wklx,\n"
-+" deny /proc/sys/kern[^e]*{,/**} wklx,\n"
-+" deny /proc/sys/kerne[^l]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/[^smhd]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/d[^o]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/do[^m]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/dom[^a]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/doma[^i]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/domai[^n]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/domain[^n]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/domainn[^a]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/domainna[^m]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/domainnam[^e]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/domainname?*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/h[^o]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/ho[^s]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/hos[^t]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/host[^n]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/hostn[^a]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/hostna[^m]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/hostnam[^e]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/hostname?*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/m[^s]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/ms[^g]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/msg*/** wklx,\n"
-+" deny /proc/sys/kernel/s[^he]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/se[^m]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/sem*/** wklx,\n"
-+" deny /proc/sys/kernel/sh[^m]*{,/**} wklx,\n"
-+" deny /proc/sys/kernel/shm*/** wklx,\n"
-+" deny /proc/sys/kernel?*{,/**} wklx,\n"
-+" deny /proc/sys/n[^e]*{,/**} wklx,\n"
-+" deny /proc/sys/ne[^t]*{,/**} wklx,\n"
-+" deny /proc/sys/net?*{,/**} wklx,\n"
-+" deny /sys/[^fdck]*{,/**} wklx,\n"
-+" deny /sys/c[^l]*{,/**} wklx,\n"
-+" deny /sys/cl[^a]*{,/**} wklx,\n"
-+" deny /sys/cla[^s]*{,/**} wklx,\n"
-+" deny /sys/clas[^s]*{,/**} wklx,\n"
-+" deny /sys/class/[^n]*{,/**} wklx,\n"
-+" deny /sys/class/n[^e]*{,/**} wklx,\n"
-+" deny /sys/class/ne[^t]*{,/**} wklx,\n"
-+" deny /sys/class/net?*{,/**} wklx,\n"
-+" deny /sys/class?*{,/**} wklx,\n"
-+" deny /sys/d[^e]*{,/**} wklx,\n"
-+" deny /sys/de[^v]*{,/**} wklx,\n"
-+" deny /sys/dev[^i]*{,/**} wklx,\n"
-+" deny /sys/devi[^c]*{,/**} wklx,\n"
-+" deny /sys/devic[^e]*{,/**} wklx,\n"
-+" deny /sys/device[^s]*{,/**} wklx,\n"
-+" deny /sys/devices/[^v]*{,/**} wklx,\n"
-+" deny /sys/devices/v[^i]*{,/**} wklx,\n"
-+" deny /sys/devices/vi[^r]*{,/**} wklx,\n"
-+" deny /sys/devices/vir[^t]*{,/**} wklx,\n"
-+" deny /sys/devices/virt[^u]*{,/**} wklx,\n"
-+" deny /sys/devices/virtu[^a]*{,/**} wklx,\n"
-+" deny /sys/devices/virtua[^l]*{,/**} wklx,\n"
-+" deny /sys/devices/virtual/[^n]*{,/**} wklx,\n"
-+" deny /sys/devices/virtual/n[^e]*{,/**} wklx,\n"
-+" deny /sys/devices/virtual/ne[^t]*{,/**} wklx,\n"
-+" deny /sys/devices/virtual/net?*{,/**} wklx,\n"
-+" deny /sys/devices/virtual?*{,/**} wklx,\n"
-+" deny /sys/devices?*{,/**} wklx,\n"
-+" deny /sys/f[^s]*{,/**} wklx,\n"
-+" deny /sys/fs/[^c]*{,/**} wklx,\n"
-+" deny /sys/fs/c[^g]*{,/**} wklx,\n"
-+" deny /sys/fs/cg[^r]*{,/**} wklx,\n"
-+" deny /sys/fs/cgr[^o]*{,/**} wklx,\n"
-+" deny /sys/fs/cgro[^u]*{,/**} wklx,\n"
-+" deny /sys/fs/cgrou[^p]*{,/**} wklx,\n"
-+" deny /sys/fs/cgroup?*{,/**} wklx,\n"
-+" deny /sys/fs?*{,/**} wklx,\n"
-+;
-+
-+static const char AA_PROFILE_UNIX_SOCKETS[] =
-+"\n"
-+" ### Feature: unix\n"
-+" # Allow receive via unix sockets from anywhere\n"
-+" unix (receive),\n"
-+"\n"
-+" # Allow all unix sockets in the container\n"
-+" unix peer=(label=@{profile_name}),\n"
-+;
-+
-+static const char AA_PROFILE_CGROUP_NAMESPACES[] =
-+"\n"
-+" ### Feature: cgroup namespace\n"
-+" mount fstype=cgroup -> /sys/fs/cgroup/**,\n"
-+" mount fstype=cgroup2 -> /sys/fs/cgroup/**,\n"
-+;
-+
-+/* '_BASE' because we still need to append generated change_profile rules */
-+static const char AA_PROFILE_STACKING_BASE[] =
-+"\n"
-+" ### Feature: apparmor stacking\n"
-+" ### Configuration: apparmor profile loading (in namespace)\n"
-+" deny /sys/k[^e]*{,/**} wklx,\n"
-+" deny /sys/ke[^r]*{,/**} wklx,\n"
-+" deny /sys/ker[^n]*{,/**} wklx,\n"
-+" deny /sys/kern[^e]*{,/**} wklx,\n"
-+" deny /sys/kerne[^l]*{,/**} wklx,\n"
-+" deny /sys/kernel/[^s]*{,/**} wklx,\n"
-+" deny /sys/kernel/s[^e]*{,/**} wklx,\n"
-+" deny /sys/kernel/se[^c]*{,/**} wklx,\n"
-+" deny /sys/kernel/sec[^u]*{,/**} wklx,\n"
-+" deny /sys/kernel/secu[^r]*{,/**} wklx,\n"
-+" deny /sys/kernel/secur[^i]*{,/**} wklx,\n"
-+" deny /sys/kernel/securi[^t]*{,/**} wklx,\n"
-+" deny /sys/kernel/securit[^y]*{,/**} wklx,\n"
-+" deny /sys/kernel/security/[^a]*{,/**} wklx,\n"
-+" deny /sys/kernel/security/a[^p]*{,/**} wklx,\n"
-+" deny /sys/kernel/security/ap[^p]*{,/**} wklx,\n"
-+" deny /sys/kernel/security/app[^a]*{,/**} wklx,\n"
-+" deny /sys/kernel/security/appa[^r]*{,/**} wklx,\n"
-+" deny /sys/kernel/security/appar[^m]*{,/**} wklx,\n"
-+" deny /sys/kernel/security/apparm[^o]*{,/**} wklx,\n"
-+" deny /sys/kernel/security/apparmo[^r]*{,/**} wklx,\n"
-+" deny /sys/kernel/security/apparmor?*{,/**} wklx,\n"
-+" deny /sys/kernel/security?*{,/**} wklx,\n"
-+" deny /sys/kernel?*{,/**} wklx,\n"
-+;
-+
-+static const char AA_PROFILE_NO_STACKING[] =
-+"\n"
-+" ### Feature: apparmor stacking (not present)\n"
-+" deny /sys/k*{,/**} rwklx,\n"
-+;
-+
-+/* '_BASE' because we need to append change_profile for stacking */
-+static const char AA_PROFILE_NESTING_BASE[] =
-+"\n"
-+" ### Configuration: nesting\n"
-+" pivot_root,\n"
-+" ptrace,\n"
-+" signal,\n"
-+"\n"
-+ /* NOTE: See conf.c's "nesting_helpers" for details. */
-+" deny /dev/.lxc/proc/** rw,\n"
-+" deny /dev/.lxc/sys/** rw,\n"
-+"\n"
-+" mount fstype=proc -> /usr/lib/*/lxc/**,\n"
-+" mount fstype=sysfs -> /usr/lib/*/lxc/**,\n"
-+" mount options=(rw,bind),\n"
-+" mount options=(rw,rbind),\n"
-+" mount options=(rw,make-rshared),\n"
-+"\n"
-+ /* FIXME: What's the state here on apparmor's side? */
-+" # there doesn't seem to be a way to ask for:\n"
-+" # mount options=(ro,nosuid,nodev,noexec,remount,bind),\n"
-+" # as we always get mount to $cdir/proc/sys with those flags denied\n"
-+" # So allow all mounts until that is straightened out:\n"
-+" mount,\n"
-+;
-+
-+static const char AA_PROFILE_UNPRIVILEGED[] =
-+"\n"
-+" ### Configuration: unprivileged container\n"
-+" pivot_root,\n"
-+"\n"
-+" # Allow modifying mount propagation\n"
-+" mount options=(rw,make-slave) -> **,\n"
-+" mount options=(rw,make-rslave) -> **,\n"
-+" mount options=(rw,make-shared) -> **,\n"
-+" mount options=(rw,make-rshared) -> **,\n"
-+" mount options=(rw,make-private) -> **,\n"
-+" mount options=(rw,make-rprivate) -> **,\n"
-+" mount options=(rw,make-unbindable) -> **,\n"
-+" mount options=(rw,make-runbindable) -> **,\n"
-+"\n"
-+" # Allow all bind-mounts\n"
-+" mount options=(rw,bind),\n"
-+" mount options=(rw,rbind),\n"
-+"\n"
-+" # Allow remounting things read-only\n"
-+" mount options=(ro,remount),\n"
-+;
-
- static bool check_mount_feature_enabled(void)
- {
-@@ -144,11 +477,6 @@ static bool apparmor_am_unconfined(void)
- return ret;
- }
-
--/* aa stacking is not yet supported */
--static bool aa_stacking_supported(void) {
-- return false;
--}
--
- static bool aa_needs_transition(char *curlabel)
- {
- if (!curlabel)
-@@ -160,61 +488,546 @@ static bool aa_needs_transition(char *curlabel)
- return true;
- }
-
-+static inline void uint64hex(char *buf, uint64_t num)
-+{
-+ size_t i;
-+
-+ buf[16] = 0;
-+ for (i = 16; i--;) {
-+ char c = (char)(num & 0xf);
-+ buf[i] = c + (c < 0xa ? '0' : 'a' - 0xa);
-+ num >>= 4;
-+ }
-+}
-+
-+static inline char *shorten_apparmor_name(char *name)
-+{
-+ size_t len = strlen(name);
-+ if (len + 7 > 253) {
-+ uint64_t hash;
-+ hash = fnv_64a_buf(name, len, FNV1A_64_INIT);
-+ name = must_realloc(name, 16 + 1);
-+ uint64hex(name, hash);
-+ }
-+
-+ return name;
-+}
-+
-+/* Replace slashes with hyphens */
-+static inline void sanitize_path(char *path)
-+{
-+ size_t i;
-+
-+ for (i = 0; path[i]; i++)
-+ if (path[i] == '/')
-+ path[i] = '-';
-+}
-+
-+static inline char *apparmor_dir(const char *ctname, const char *lxcpath)
-+{
-+ return must_make_path(lxcpath, ctname, "apparmor", NULL);
-+}
-+
-+
-+static inline char *apparmor_profile_full(const char *ctname, const char *lxcpath)
-+{
-+ return shorten_apparmor_name(must_concat("lxc-", ctname, "_<", lxcpath, ">", NULL));
-+}
-+
-+/* Like apparmor_profile_full() but with slashes replaced by hyphens */
-+static inline char *apparmor_namespace(const char *ctname, const char *lxcpath)
-+{
-+ char *full;
-+
-+ full = apparmor_profile_full(ctname, lxcpath);
-+ sanitize_path(full);
-+
-+ return full;
-+}
-+
-+/* FIXME: This is currently run only in the context of a constructor (via the
-+ * initial lsm_init() called due to its __attribute__((constructor)), so we
-+ * do not have ERROR/... macros available, so there are some fprintf(stderr)s
-+ * in there.
-+ */
-+static bool check_apparmor_parser_version()
-+{
-+ struct lxc_popen_FILE *parserpipe;
-+ int rc;
-+ int major = 0, minor = 0, micro = 0;
-+
-+ parserpipe = lxc_popen("apparmor_parser --version");
-+ if (!parserpipe) {
-+ fprintf(stderr, "Failed to run check for apparmor_parser\n");
-+ return false;
-+ }
-+
-+ rc = fscanf(parserpipe->f, "AppArmor parser version %d.%d.%d", &major, &minor, µ);
-+ if (rc < 1) {
-+ lxc_pclose(parserpipe);
-+ /* We stay silent for now as this most likely means the shell
-+ * lxc_popen executed failed to find the apparmor_parser binary.
-+ * See the FIXME comment above for details.
-+ */
-+ return false;
-+ }
-+
-+ rc = lxc_pclose(parserpipe);
-+ if (rc < 0) {
-+ fprintf(stderr, "Error waiting for child process\n");
-+ return false;
-+ }
-+ if (rc != 0) {
-+ fprintf(stderr, "'apparmor_parser --version' executed with an error status\n");
-+ return false;
-+ }
-+
-+ aa_supports_unix = (major > 2) ||
-+ (major == 2 && minor > 10) ||
-+ (major == 2 && minor == 10 && micro >= 95);
-+
-+ return true;
-+}
-+
-+static bool file_is_yes(const char *path)
-+{
-+ ssize_t rd;
-+ int fd;
-+ char buf[8]; /* we actually just expect "yes" or "no" */
-+
-+ fd = open(path, O_RDONLY | O_CLOEXEC);
-+ if (fd < 0)
-+ return false;
-+
-+ rd = read(fd, buf, sizeof(buf));
-+ close(fd);
-+
-+ return rd >= 4 && strncmp(buf, "yes\n", 4) == 0;
-+}
-+
-+static bool apparmor_can_stack()
-+{
-+ int major, minor, scanned;
-+ FILE *f;
-+
-+ if (!file_is_yes("/sys/kernel/security/apparmor/features/domain/stack"))
-+ return false;
-+
-+ f = fopen_cloexec("/sys/kernel/security/apparmor/features/domain/version", "r");
-+ if (!f)
-+ return false;
-+
-+ scanned = fscanf(f, "%d.%d", &major, &minor);
-+ fclose(f);
-+ if (scanned != 2)
-+ return false;
-+
-+ return major > 1 || (major == 1 && minor >= 2);
-+}
-+
-+static void must_append_sized_full(char **buf, size_t *bufsz, const char *data,
-+ size_t size, bool append_newline)
-+{
-+ size_t newsize = *bufsz + size;
-+
-+ if (append_newline)
-+ ++newsize;
-+
-+ *buf = must_realloc(*buf, newsize);
-+ memcpy(*buf + *bufsz, data, size);
-+
-+ if (append_newline)
-+ (*buf)[newsize - 1] = '\n';
-+
-+ *bufsz = newsize;
-+}
-+
-+static void must_append_sized(char **buf, size_t *bufsz, const char *data, size_t size)
-+{
-+ return must_append_sized_full(buf, bufsz, data, size, false);
-+}
-+
-+static bool is_privileged(struct lxc_conf *conf)
-+{
-+ return lxc_list_empty(&conf->id_map);
-+}
-+
-+static char *get_apparmor_profile_content(struct lxc_conf *conf, const char *lxcpath)
-+{
-+ char *profile, *profile_name_full;
-+ size_t size;
-+ struct lxc_list *it;
-+
-+ profile_name_full = apparmor_profile_full(conf->name, lxcpath);
-+
-+ profile = must_concat(
-+"#include <tunables/global>\n"
-+"profile \"", profile_name_full, "\" flags=(attach_disconnected,mediate_deleted) {\n",
-+ NULL);
-+ size = strlen(profile);
-+
-+ must_append_sized(&profile, &size, AA_PROFILE_BASE,
-+ sizeof(AA_PROFILE_BASE) - 1);
-+
-+ if (aa_supports_unix)
-+ must_append_sized(&profile, &size, AA_PROFILE_UNIX_SOCKETS,
-+ sizeof(AA_PROFILE_UNIX_SOCKETS) - 1);
-+
-+ if (file_exists("/proc/self/ns/cgroup"))
-+ must_append_sized(&profile, &size, AA_PROFILE_CGROUP_NAMESPACES,
-+ sizeof(AA_PROFILE_CGROUP_NAMESPACES) - 1);
-+
-+ if (aa_can_stack && !aa_is_stacked) {
-+ char *namespace, *temp;
-+
-+ must_append_sized(&profile, &size, AA_PROFILE_STACKING_BASE,
-+ sizeof(AA_PROFILE_STACKING_BASE) - 1);
-+
-+ namespace = apparmor_namespace(conf->name, lxcpath);
-+ temp = must_concat(" change_profile -> \":", namespace, ":*\",\n"
-+ " change_profile -> \":", namespace, "://*\",\n",
-+ NULL);
-+ free(namespace);
-+
-+ must_append_sized(&profile, &size, temp, strlen(temp));
-+ free(temp);
-+ } else {
-+ must_append_sized(&profile, &size, AA_PROFILE_NO_STACKING,
-+ sizeof(AA_PROFILE_NO_STACKING) - 1);
-+ }
-+
-+ if (conf->lsm_aa_allow_nesting) {
-+ must_append_sized(&profile, &size, AA_PROFILE_NESTING_BASE,
-+ sizeof(AA_PROFILE_NESTING_BASE) - 1);
-+
-+ if (!aa_can_stack || aa_is_stacked) {
-+ char *temp;
-+
-+ temp = must_concat(" change_profile -> \"",
-+ profile_name_full, "\",\n", NULL);
-+ must_append_sized(&profile, &size, temp, strlen(temp));
-+ free(temp);
-+ }
-+ }
-+
-+ if (!is_privileged(conf) || am_host_unpriv())
-+ must_append_sized(&profile, &size, AA_PROFILE_UNPRIVILEGED,
-+ sizeof(AA_PROFILE_UNPRIVILEGED) - 1);
-+
-+ lxc_list_for_each(it, &conf->lsm_aa_raw) {
-+ const char *line = it->elem;
-+
-+ must_append_sized_full(&profile, &size, line, strlen(line), true);
-+ }
-+
-+ /* include terminating \0 byte */
-+ must_append_sized(&profile, &size, "}\n", 3);
-+
-+ free(profile_name_full);
-+
-+ return profile;
-+}
-+
- /*
-- * apparmor_process_label_set: Set AppArmor process profile
-- *
-- * @label : the profile to set
-- * @conf : the container configuration to use if @label is NULL
-- * @default : use the default profile if @label is NULL
-- * @on_exec : this is ignored. Apparmor profile will be changed immediately
-- *
-- * Returns 0 on success, < 0 on failure
-- *
-- * Notes: This relies on /proc being available.
-+ * apparmor_parser creates a cache file using the parsed file's name as a name.
-+ * This means there may be multiple containers with the same name but different
-+ * lxcpaths. Therefore we need a sanitized version of the complete profile name
-+ * as profile file-name.
-+ * We already get this exactly from apparmor_namespace().
- */
--static int apparmor_process_label_set(const char *inlabel, struct lxc_conf *conf,
-- bool use_default, bool on_exec)
-+static char *make_apparmor_profile_path(const char *ctname, const char *lxcpath)
- {
-- int label_fd, ret;
-- pid_t tid;
-- const char *label = inlabel ? inlabel : conf->lsm_aa_profile;
-- char *curlabel;
-+ char *ret, *filename;
-
-- if (!aa_enabled)
-- return 0;
-+ filename = apparmor_namespace(ctname, lxcpath);
-+ ret = must_make_path(lxcpath, ctname, "apparmor", filename, NULL);
-+ free(filename);
-+
-+ return ret;
-+}
-+
-+static char *make_apparmor_namespace_path(const char *ctname, const char *lxcpath)
-+{
-+ char *ret, *namespace;
-+
-+ namespace = apparmor_namespace(ctname, lxcpath);
-+ ret = must_make_path("/sys/kernel/security/apparmor/policy/namespaces", namespace, NULL);
-+ free(namespace);
-+
-+ return ret;
-+}
-+
-+static bool make_apparmor_namespace(struct lxc_conf *conf, const char *lxcpath)
-+{
-+ char *path;
-+
-+ if (!aa_can_stack || aa_is_stacked)
-+ return true;
-+
-+ path = make_apparmor_namespace_path(conf->name, lxcpath);
-+ errno = 0;
-+ if (mkdir(path, 0755) < 0 && errno != EEXIST) {
-+ SYSERROR("Error creating AppArmor namespace: %s", path);
-+ free(path);
-+ return false;
-+ }
-+ free(path);
-+
-+ return true;
-+}
-+
-+static void remove_apparmor_namespace(struct lxc_conf *conf, const char *lxcpath)
-+{
-+ char *path;
-+
-+ path = make_apparmor_namespace_path(conf->name, lxcpath);
-+ if (rmdir(path) != 0)
-+ SYSERROR("Error removing AppArmor namespace");
-+ free(path);
-+}
-+
-+struct apparmor_parser_args {
-+ char cmd;
-+ char *file;
-+};
-+
-+static int apparmor_parser_exec(void *data)
-+{
-+ struct apparmor_parser_args *args = data;
-+ char cmdbuf[] = { '-', args->cmd, 'W', 'L', 0 };
-+
-+ execlp("apparmor_parser", "apparmor_parser", cmdbuf, APPARMOR_CACHE_DIR, args->file, NULL);
-+
-+ return -1;
-+}
-+
-+static int run_apparmor_parser(char command,
-+ struct lxc_conf *conf,
-+ const char *lxcpath)
-+{
-+ char output[MAXPATHLEN];
-+ int ret;
-+ struct apparmor_parser_args args = {
-+ .cmd = command,
-+ .file = make_apparmor_profile_path(conf->name, lxcpath),
-+ };
-+
-+ ret = run_command(output, sizeof(output), apparmor_parser_exec, (void*)&args);
-+ if (ret < 0) {
-+ ERROR("Failed to run apparmor_parser on \"%s\": %s", args.file, output);
-+ ret = -1;
-+ }
-+
-+
-+ free(args.file);
-+ return ret;
-+}
-+
-+static void remove_apparmor_profile(struct lxc_conf *conf, const char *lxcpath)
-+{
-+ char *path;
-+
-+ /* It's ok if these deletes fail: if the container was never started,
-+ * we'll have never written a profile or cached it.
-+ */
-+
-+ path = make_apparmor_profile_path(conf->name, lxcpath);
-+ (void)unlink(path);
-+ free(path);
-+
-+ /* Also remove the apparmor/ subdirectory */
-+ path = apparmor_dir(conf->name, lxcpath);
-+ (void)rmdir(path);
-+ free(path);
-+}
-+
-+static int load_apparmor_profile(struct lxc_conf *conf, const char *lxcpath)
-+{
-+ struct stat profile_sb;
-+ size_t content_len;
-+ int ret = -1;
-+ size_t old_len = 0;
-+ char *profile_path = NULL, *old_content = NULL, *new_content = NULL;
-+ int profile_fd = -1;
-+
-+ if (!make_apparmor_namespace(conf, lxcpath))
-+ return -1;
-+
-+ /* In order to avoid forcing a profile parse (potentially slow) on
-+ * every container start, let's use apparmor's binary policy cache,
-+ * which checks mtime of the files to figure out if the policy needs to
-+ * be regenerated.
-+ *
-+ * Since it uses mtimes, we shouldn't just always write out our local
-+ * apparmor template; instead we should check to see whether the
-+ * template is the same as ours. If it isn't we should write our
-+ * version out so that the new changes are reflected and we definitely
-+ * force a recompile.
-+ */
-+
-+ profile_path = make_apparmor_profile_path(conf->name, lxcpath);
-+ profile_fd = open(profile_path, O_RDONLY | O_CLOEXEC);
-+ if (profile_fd >= 0) {
-+ if (fstat(profile_fd, &profile_sb) < 0) {
-+ SYSERROR("Error accessing old profile from %s",
-+ profile_path);
-+ goto out;
-+ }
-+ old_len = profile_sb.st_size;
-+ old_content = lxc_strmmap(NULL, old_len, PROT_READ,
-+ MAP_PRIVATE, profile_fd, 0);
-+ if (!old_content) {
-+ SYSERROR("Failed to mmap old profile from %s",
-+ profile_path);
-+ goto out;
-+ }
-+ } else if (errno != ENOENT) {
-+ SYSERROR("Error reading old profile from %s", profile_path);
-+ goto out;
-+ }
-+
-+ new_content = get_apparmor_profile_content(conf, lxcpath);
-+ if (!new_content)
-+ goto out;
-+
-+ content_len = strlen(new_content);
-+
-+ if (!old_content || old_len != content_len || memcmp(old_content, new_content, content_len) != 0) {
-+ char *path;
-+
-+ ret = mkdir_p(APPARMOR_CACHE_DIR, 0755);
-+ if (ret < 0) {
-+ SYSERROR("Error creating AppArmor profile cache directory " APPARMOR_CACHE_DIR);
-+ goto out;
-+ }
-+
-+ path = apparmor_dir(conf->name, lxcpath);
-+ ret = mkdir_p(path, 0755);
-+ if (ret < 0) {
-+ SYSERROR("Error creating AppArmor profile directory: %s", path);
-+ free(path);
-+ goto out;
-+ }
-+ free(path);
-+
-+ ret = lxc_write_to_file(profile_path, new_content, content_len, false, 0600);
-+ if (ret < 0) {
-+ SYSERROR("Error writing profile to %s", profile_path);
-+ goto out;
-+ }
-+ }
-+
-+ ret = run_apparmor_parser(AA_CMD_LOAD, conf, lxcpath);
-+ if (ret != 0)
-+ goto out_remove_profile;
-+
-+ conf->lsm_aa_profile_created = true;
-+
-+ goto out_ok;
-+
-+out_remove_profile:
-+ remove_apparmor_profile(conf, lxcpath);
-+out:
-+ remove_apparmor_namespace(conf, lxcpath);
-+out_ok:
-+ if (profile_fd >= 0) {
-+ if (old_content)
-+ lxc_strmunmap(old_content, old_len);
-+ close(profile_fd);
-+ }
-+ free(profile_path);
-+ free(new_content);
-+ return ret;
-+}
-+
-+/*
-+ * Ensure that the container's policy namespace is unloaded to free kernel
-+ * memory. This does not delete the policy from disk or cache.
-+ */
-+static void apparmor_cleanup(struct lxc_conf *conf, const char *lxcpath)
-+{
-+ if (!aa_admin)
-+ return;
-+
-+ if (!conf->lsm_aa_profile_created)
-+ return;
-+
-+ remove_apparmor_namespace(conf, lxcpath);
-+ (void)run_apparmor_parser(AA_CMD_UNLOAD, conf, lxcpath);
-+
-+ remove_apparmor_profile(conf, lxcpath);
-+}
-+
-+static int apparmor_prepare(struct lxc_conf *conf, const char *lxcpath)
-+{
-+ int ret = -1;
-+ const char *label;
-+ char *curlabel = NULL, *genlabel = NULL;
-+
-+ if (!aa_enabled) {
-+ ERROR("AppArmor not enabled");
-+ return -1;
-+ }
-+
-+ label = conf->lsm_aa_profile;
-
- /* user may request that we just ignore apparmor */
- if (label && strcmp(label, AA_UNCHANGED) == 0) {
-- INFO("apparmor profile unchanged per user request");
-+ INFO("AppArmor profile unchanged per user request");
-+ conf->lsm_aa_profile_computed = must_copy_string(label);
- return 0;
- }
-
-+ if (label && strcmp(label, AA_GENERATED) == 0) {
-+ if (!aa_parser_available) {
-+ ERROR("Cannot use generated profile: apparmor_parser not available");
-+ goto out;
-+ }
-+
-+ /* auto-generate profile based on available/requested security features */
-+ if (load_apparmor_profile(conf, lxcpath) != 0) {
-+ ERROR("Failed to load generated AppArmor profile");
-+ goto out;
-+ }
-+
-+ genlabel = apparmor_profile_full(conf->name, lxcpath);
-+ if (!genlabel) {
-+ ERROR("Failed to build AppArmor profile name");
-+ goto out;
-+ }
-+
-+ if (aa_can_stack && !aa_is_stacked) {
-+ char *namespace = apparmor_namespace(conf->name, lxcpath);
-+ size_t llen = strlen(genlabel);
-+ must_append_sized(&genlabel, &llen, "//&:", sizeof("//&:") - 1);
-+ must_append_sized(&genlabel, &llen, namespace, strlen(namespace));
-+ must_append_sized(&genlabel, &llen, ":", sizeof(":")); /* with the nul byte */
-+ free(namespace);
-+ }
-+
-+ label = genlabel;
-+ }
-+
- curlabel = apparmor_process_label_get(lxc_raw_getpid());
-
-- if (!aa_stacking_supported() && aa_needs_transition(curlabel)) {
-+ if (!aa_can_stack && aa_needs_transition(curlabel)) {
- /* we're already confined, and stacking isn't supported */
-
- if (!label || strcmp(curlabel, label) == 0) {
- /* no change requested */
-- free(curlabel);
-- return 0;
-+ ret = 0;
-+ goto out;
- }
-
-- ERROR("already apparmor confined, but new label requested.");
-- free(curlabel);
-- return -1;
-+ ERROR("Already AppArmor confined, but new label requested.");
-+ goto out;
- }
-- free(curlabel);
-
- if (!label) {
-- if (use_default) {
-- if (cgns_supported())
-- label = AA_DEF_PROFILE_CGNS;
-- else
-- label = AA_DEF_PROFILE;
-- }
-+ if (cgns_supported())
-+ label = AA_DEF_PROFILE_CGNS;
- else
-- label = "unconfined";
-+ label = AA_DEF_PROFILE;
- }
-
- if (!check_mount_feature_enabled() && strcmp(label, "unconfined") != 0) {
-@@ -223,30 +1036,78 @@ static int apparmor_process_label_set(const char *inlabel, struct lxc_conf *conf
- ERROR("If you really want to start this container, set");
- ERROR("lxc.apparmor.allow_incomplete = 1");
- ERROR("in your container configuration file");
-- return -1;
-+ goto out;
- }
- }
-
-+ conf->lsm_aa_profile_computed = must_copy_string(label);
-+ ret = 0;
-+
-+out:
-+ if (genlabel) {
-+ free(genlabel);
-+ if (ret != 0)
-+ apparmor_cleanup(conf, lxcpath);
-+ }
-+ free(curlabel);
-+ return ret;
-+}
-+
-+/*
-+ * apparmor_process_label_set: Set AppArmor process profile
-+ *
-+ * @label : the profile to set
-+ * @conf : the container configuration to use if @label is NULL
-+ * @default : use the default profile if @label is NULL
-+ * @on_exec : this is ignored. Apparmor profile will be changed immediately
-+ *
-+ * Returns 0 on success, < 0 on failure
-+ *
-+ * Notes: This relies on /proc being available.
-+ */
-+static int apparmor_process_label_set(const char *inlabel, struct lxc_conf *conf,
-+ bool on_exec)
-+{
-+ int label_fd, ret;
-+ pid_t tid;
-+ const char *label;
-+
-+ if (!aa_enabled) {
-+ ERROR("AppArmor not enabled");
-+ return -1;
-+ }
-+
-+ label = inlabel ? inlabel : conf->lsm_aa_profile_computed;
-+ if (!label) {
-+ ERROR("LSM wasn't prepared");
-+ return -1;
-+ }
-+
-+ /* user may request that we just ignore apparmor */
-+ if (strcmp(label, AA_UNCHANGED) == 0) {
-+ INFO("AppArmor profile unchanged per user request");
-+ return 0;
-+ }
-
- if (strcmp(label, "unconfined") == 0 && apparmor_am_unconfined()) {
-- INFO("apparmor profile unchanged");
-+ INFO("AppArmor profile unchanged");
- return 0;
- }
- tid = lxc_raw_gettid();
- label_fd = lsm_process_label_fd_get(tid, on_exec);
- if (label_fd < 0) {
-- SYSERROR("Failed to change apparmor profile to %s", label);
-+ SYSERROR("Failed to change AppArmor profile to %s", label);
- return -1;
- }
-
- ret = lsm_process_label_set_at(label_fd, label, on_exec);
- close(label_fd);
- if (ret < 0) {
-- ERROR("Failed to change apparmor profile to %s", label);
-+ ERROR("Failed to change AppArmor profile to %s", label);
- return -1;
- }
-
-- INFO("Changed apparmor profile to %s", label);
-+ INFO("Changed AppArmor profile to %s", label);
- return 0;
- }
-
-@@ -255,12 +1116,39 @@ static struct lsm_drv apparmor_drv = {
- .enabled = apparmor_enabled,
- .process_label_get = apparmor_process_label_get,
- .process_label_set = apparmor_process_label_set,
-+ .prepare = apparmor_prepare,
-+ .cleanup = apparmor_cleanup,
- };
-
- struct lsm_drv *lsm_apparmor_drv_init(void)
- {
-+ bool have_mac_admin = false;
-+
- if (!apparmor_enabled())
- return NULL;
-+
-+ /* We only support generated profiles when apparmor_parser is usable */
-+ if (!check_apparmor_parser_version())
-+ goto out;
-+
-+ aa_parser_available = true;
-+
-+ aa_can_stack = apparmor_can_stack();
-+ if (aa_can_stack)
-+ aa_is_stacked = file_is_yes("/sys/kernel/security/apparmor/.ns_stacked");
-+
-+ #if HAVE_LIBCAP
-+ have_mac_admin = lxc_proc_cap_is_set(CAP_SETGID, CAP_EFFECTIVE);
-+ #endif
-+
-+ if (!have_mac_admin)
-+ WARN("Per-container AppArmor profiles are disabled because the mac_admin capability is missing");
-+ else if (am_host_unpriv() && !aa_is_stacked)
-+ WARN("Per-container AppArmor profiles are disabled because LXC is running in an unprivileged container without stacking");
-+ else
-+ aa_admin = true;
-+
-+out:
- aa_enabled = 1;
- return &apparmor_drv;
- }
-diff --git a/src/lxc/lsm/lsm.c b/src/lxc/lsm/lsm.c
-index 8d7de2db..46e21206 100644
---- a/src/lxc/lsm/lsm.c
-+++ b/src/lxc/lsm/lsm.c
-@@ -177,11 +177,37 @@ on_error:
- }
-
- int lsm_process_label_set(const char *label, struct lxc_conf *conf,
-- bool use_default, bool on_exec)
-+ bool on_exec)
- {
- if (!drv) {
- ERROR("LSM driver not inited");
- return -1;
- }
-- return drv->process_label_set(label, conf, use_default, on_exec);
-+ return drv->process_label_set(label, conf, on_exec);
-+}
-+
-+int lsm_process_prepare(struct lxc_conf *conf, const char *lxcpath)
-+{
-+ if (!drv) {
-+ ERROR("LSM driver not inited");
-+ return 0;
-+ }
-+
-+ if (!drv->prepare)
-+ return 0;
-+
-+ return drv->prepare(conf, lxcpath);
-+}
-+
-+void lsm_process_cleanup(struct lxc_conf *conf, const char *lxcpath)
-+{
-+ if (!drv) {
-+ ERROR("LSM driver not inited");
-+ return;
-+ }
-+
-+ if (!drv->cleanup)
-+ return;
-+
-+ drv->cleanup(conf, lxcpath);
- }
-diff --git a/src/lxc/lsm/lsm.h b/src/lxc/lsm/lsm.h
-index cafb2ac7..52e656d6 100644
---- a/src/lxc/lsm/lsm.h
-+++ b/src/lxc/lsm/lsm.h
-@@ -38,17 +38,21 @@ struct lsm_drv {
- int (*enabled)(void);
- char *(*process_label_get)(pid_t pid);
- int (*process_label_set)(const char *label, struct lxc_conf *conf,
-- bool use_default, bool on_exec);
-+ bool on_exec);
-+ int (*prepare)(struct lxc_conf *conf, const char *lxcpath);
-+ void (*cleanup)(struct lxc_conf *conf, const char *lxcpath);
- };
-
- extern void lsm_init(void);
- extern int lsm_enabled(void);
- extern const char *lsm_name(void);
- extern char *lsm_process_label_get(pid_t pid);
-+extern int lsm_process_prepare(struct lxc_conf *conf, const char *lxcpath);
- extern int lsm_process_label_set(const char *label, struct lxc_conf *conf,
-- bool use_default, bool on_exec);
-+ bool on_exec);
- extern int lsm_process_label_fd_get(pid_t pid, bool on_exec);
- extern int lsm_process_label_set_at(int label_fd, const char *label,
- bool on_exec);
-+extern void lsm_process_cleanup(struct lxc_conf *conf, const char *lxcpath);
-
- #endif /* __LXC_LSM_H */
-diff --git a/src/lxc/lsm/nop.c b/src/lxc/lsm/nop.c
-index 7bb8121b..9397f2bf 100644
---- a/src/lxc/lsm/nop.c
-+++ b/src/lxc/lsm/nop.c
-@@ -30,7 +30,7 @@ static char *nop_process_label_get(pid_t pid)
- }
-
- static int nop_process_label_set(const char *label, struct lxc_conf *conf,
-- bool use_default, bool on_exec)
-+ bool on_exec)
- {
- return 0;
- }
-diff --git a/src/lxc/lsm/selinux.c b/src/lxc/lsm/selinux.c
-index c88c18e3..9f7b7bc3 100644
---- a/src/lxc/lsm/selinux.c
-+++ b/src/lxc/lsm/selinux.c
-@@ -75,15 +75,13 @@ static char *selinux_process_label_get(pid_t pid)
- * Notes: This relies on /proc being available.
- */
- static int selinux_process_label_set(const char *inlabel, struct lxc_conf *conf,
-- bool use_default, bool on_exec)
-+ bool on_exec)
- {
- int ret;
- const char *label;
-
- label = inlabel ? inlabel : conf->lsm_se_context;
- if (!label) {
-- if (!use_default)
-- return -EINVAL;
-
- label = DEFAULT_LABEL;
- }
-diff --git a/src/lxc/start.c b/src/lxc/start.c
-index 3343f9bf..6d6dc7ee 100644
---- a/src/lxc/start.c
-+++ b/src/lxc/start.c
-@@ -863,9 +863,19 @@ int lxc_init(const char *name, struct lxc_handler *handler)
- }
- TRACE("Initialized cgroup driver");
-
-+ ret = lsm_process_prepare(conf, handler->lxcpath);
-+ if (ret < 0) {
-+ ERROR("Failed to initialize LSM");
-+ goto out_destroy_cgroups;
-+ }
-+ TRACE("Initialized LSM");
-+
- INFO("Container \"%s\" is initialized", name);
- return 0;
-
-+out_destroy_cgroups:
-+ handler->cgroup_ops->destroy(handler->cgroup_ops, handler);
-+
- out_delete_terminal:
- lxc_terminal_delete(&handler->conf->console);
-
-@@ -956,6 +966,8 @@ void lxc_fini(const char *name, struct lxc_handler *handler)
- while (namespace_count--)
- free(namespaces[namespace_count]);
-
-+ lsm_process_cleanup(handler->conf, handler->lxcpath);
-+
- cgroup_ops->destroy(cgroup_ops, handler);
- cgroup_exit(cgroup_ops);
-
-@@ -1235,7 +1247,7 @@ static int do_start(void *data)
- }
-
- /* Set the label to change to when we exec(2) the container's init. */
-- ret = lsm_process_label_set(NULL, handler->conf, 1, 1);
-+ ret = lsm_process_label_set(NULL, handler->conf, true);
- if (ret < 0)
- goto out_warn_father;
-
---
-2.11.0
-
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Tue, 24 Jul 2018 16:42:26 +0200
-Subject: [PATCH] apparmor: allow start-container to change to lxc-**
-
-For generated profiles with apparmor namespaces we get
-profile names with slashes in them. To match those, we need
-to allow changing to lxc-**, not just lxc-*.
-
-Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
-(cherry picked from commit 242a9fa7ee7e9f524de5a23917faa846ea525622)
----
- config/apparmor/abstractions/start-container | 1 +
- 1 file changed, 1 insertion(+)
-
-diff --git a/config/apparmor/abstractions/start-container b/config/apparmor/abstractions/start-container
-index 414d058b..3df9883e 100644
---- a/config/apparmor/abstractions/start-container
-+++ b/config/apparmor/abstractions/start-container
-@@ -40,5 +40,6 @@
- pivot_root /usr/lib*/*/lxc/**,
-
- change_profile -> lxc-*,
-+ change_profile -> lxc-**,
- change_profile -> unconfined,
- change_profile -> :lxc-*:unconfined,
---
-2.11.0
-
+++ /dev/null
-From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
-From: Wolfgang Bumiller <w.bumiller@proxmox.com>
-Date: Tue, 24 Jul 2018 13:59:04 +0200
-Subject: [PATCH] tests: add test for generated apparmor profiles
-
-Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
-(cherry picked from commit e7311a84e5bd0758931033b1a0ce649baa720a58)
----
- src/tests/Makefile.am | 2 +
- src/tests/lxc-test-apparmor-generated | 84 +++++++++++++++++++++++++++++++++++
- 2 files changed, 86 insertions(+)
- create mode 100755 src/tests/lxc-test-apparmor-generated
-
-diff --git a/src/tests/Makefile.am b/src/tests/Makefile.am
-index a2179c3c..df5d35df 100644
---- a/src/tests/Makefile.am
-+++ b/src/tests/Makefile.am
-@@ -79,6 +79,7 @@ if DISTRO_UBUNTU
- bin_SCRIPTS += \
- lxc-test-lxc-attach \
- lxc-test-apparmor-mount \
-+ lxc-test-apparmor-generated \
- lxc-test-checkpoint-restore \
- lxc-test-snapdeps \
- lxc-test-symlink \
-@@ -112,6 +113,7 @@ EXTRA_DIST = \
- lxc-test-rootfs \
- lxc-test-autostart \
- lxc-test-apparmor-mount \
-+ lxc-test-apparmor-generated \
- lxc-test-checkpoint-restore \
- lxc-test-cloneconfig \
- lxc-test-createconfig \
-diff --git a/src/tests/lxc-test-apparmor-generated b/src/tests/lxc-test-apparmor-generated
-new file mode 100755
-index 00000000..be2e3261
---- /dev/null
-+++ b/src/tests/lxc-test-apparmor-generated
-@@ -0,0 +1,84 @@
-+#!/bin/sh
-+
-+# lxc: linux Container library
-+
-+# This is a test script for generated apparmor profiles
-+
-+# 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 ! which apparmor_parser >/dev/null 2>&1; then
-+ echo 'SKIP: test for generated apparmor profiles: apparmor_parser missing'
-+fi
-+exit 0
-+
-+DONE=0
-+KNOWN_RELEASES="precise trusty xenial yakkety zesty"
-+LOGFILE="/tmp/lxc-test-$$.log"
-+cleanup() {
-+ lxc-destroy -n $CONTAINER_NAME >/dev/null 2>&1 || true
-+
-+ if [ $DONE -eq 0 ]; then
-+ [ -f "$LOGFILE" ] && cat "$LOGFILE" >&2
-+ rm -f "$LOGFILE"
-+ echo "FAIL"
-+ exit 1
-+ fi
-+ rm -f "$LOGFILE"
-+ echo "PASS"
-+}
-+
-+ARCH=i386
-+if type dpkg >/dev/null 2>&1; then
-+ ARCH=$(dpkg --print-architecture)
-+fi
-+
-+trap cleanup EXIT HUP INT TERM
-+set -eu
-+
-+# Create a container
-+CONTAINER_NAME=lxc-test-apparmor-generated
-+
-+# default release is trusty, or the systems release if recognized
-+release=trusty
-+if [ -f /etc/lsb-release ]; then
-+ . /etc/lsb-release
-+ rels=$(ubuntu-distro-info --supported 2>/dev/null) ||
-+ rels="$KNOWN_RELEASES"
-+ for r in $rels; do
-+ [ "$DISTRIB_CODENAME" = "$r" ] && release="$r"
-+ done
-+fi
-+
-+lxc-create -t download -n $CONTAINER_NAME -B dir -- -d ubuntu -r $release -a $ARCH
-+CONTAINER_PATH=$(dirname $(lxc-info -n $CONTAINER_NAME -c lxc.rootfs.path -H) | sed -e 's/dir://')
-+cp $CONTAINER_PATH/config $CONTAINER_PATH/config.bak
-+
-+# Set the profile to be auto-generated
-+echo "lxc.apparmor.profile = generated" >> $CONTAINER_PATH/config
-+
-+# Start it
-+lxc-start -n $CONTAINER_NAME -lDEBUG -o "$LOGFILE"
-+lxc-wait -n $CONTAINER_NAME -t 5 -s RUNNING || (echo "Container didn't start" && exit 1)
-+pid=`lxc-info -p -H -n $CONTAINER_NAME`
-+profile=`cat /proc/$pid/attr/current`
-+expected_profile="lxc-${CONTAINER_NAME}_</var/lib/lxc>//&:lxc-${CONTAINER_NAME}_<-var-lib-lxc>:unconfined (enforce)"
-+lxc-stop -n $CONTAINER_NAME -k
-+if [ "x$profile" != "x$expected_profile" ]; then
-+ echo "FAIL: container was in profile $profile" >&2
-+ echo "expected profile: $expected_profile" >&2
-+ exit 1
-+fi
-+
-+DONE=1
---
-2.11.0
-
+++ /dev/null
-From 590b215c4f5225cfb7fb58d9c56fabd5197bfdac Mon Sep 17 00:00:00 2001
-From: Christian Brauner <christian.brauner@ubuntu.com>
-Date: Wed, 25 Jul 2018 19:56:54 +0200
-Subject: [PATCH] CVE 2018-6556: verify netns fd in lxc-user-nic
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
-(cherry picked from commit c1cf54ebf251fdbad1e971679614e81649f1c032)
-Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
----
- src/lxc/utils.h | 5 +++++
- src/lxc/cmd/lxc_user_nic.c | 35 ++++++++++++++++++++++++++++++++---
- src/lxc/utils.c | 12 ++++++++++++
- 3 files changed, 49 insertions(+), 3 deletions(-)
-
-diff --git a/src/lxc/utils.h b/src/lxc/utils.h
-index ed486853..4dff26ae 100644
---- a/src/lxc/utils.h
-+++ b/src/lxc/utils.h
-@@ -95,6 +95,10 @@
- #define CGROUP2_SUPER_MAGIC 0x63677270
- #endif
-
-+#ifndef NSFS_MAGIC
-+#define NSFS_MAGIC 0x6e736673
-+#endif
-+
- /* Useful macros */
- /* Maximum number for 64 bit integer is a string with 21 digits: 2^64 - 1 = 21 */
- #define LXC_NUMSTRLEN64 21
-@@ -580,6 +584,7 @@ extern void *must_realloc(void *orig, size_t sz);
- /* __typeof__ should be safe to use with all compilers. */
- typedef __typeof__(((struct statfs *)NULL)->f_type) fs_type_magic;
- extern bool has_fs_type(const char *path, fs_type_magic magic_val);
-+extern bool fhas_fs_type(int fd, fs_type_magic magic_val);
- extern bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val);
- extern bool lxc_nic_exists(char *nic);
- extern int lxc_make_tmpfile(char *template, bool rm);
-diff --git a/src/lxc/cmd/lxc_user_nic.c b/src/lxc/cmd/lxc_user_nic.c
-index ec9cd97e..c5beb6c8 100644
---- a/src/lxc/cmd/lxc_user_nic.c
-+++ b/src/lxc/cmd/lxc_user_nic.c
-@@ -1179,12 +1179,41 @@ int main(int argc, char *argv[])
- exit(EXIT_FAILURE);
- }
- } else if (request == LXC_USERNIC_DELETE) {
-- netns_fd = open(args.pid, O_RDONLY);
-+ char opath[LXC_PROC_PID_FD_LEN];
-+
-+ /* Open the path with O_PATH which will not trigger an actual
-+ * open(). Don't report an errno to the caller to not leak
-+ * information whether the path exists or not.
-+ * When stracing setuid is stripped so this is not a concern
-+ * either.
-+ */
-+ netns_fd = open(args.pid, O_PATH | O_CLOEXEC);
- if (netns_fd < 0) {
-- usernic_error("Could not open \"%s\": %s\n", args.pid,
-- strerror(errno));
-+ usernic_error("Failed to open \"%s\"\n", args.pid);
-+ exit(EXIT_FAILURE);
-+ }
-+
-+ if (!fhas_fs_type(netns_fd, NSFS_MAGIC)) {
-+ usernic_error("Path \"%s\" does not refer to a network namespace path\n", args.pid);
-+ close(netns_fd);
-+ exit(EXIT_FAILURE);
-+ }
-+
-+ ret = snprintf(opath, sizeof(opath), "/proc/self/fd/%d", netns_fd);
-+ if (ret < 0 || (size_t)ret >= sizeof(opath)) {
-+ close(netns_fd);
-+ exit(EXIT_FAILURE);
-+ }
-+
-+ /* Now get an fd that we can use in setns() calls. */
-+ ret = open(opath, O_RDONLY | O_CLOEXEC);
-+ if (ret < 0) {
-+ usernic_error("Failed to open \"%s\": %s\n", args.pid, strerror(errno));
-+ close(netns_fd);
- exit(EXIT_FAILURE);
- }
-+ close(netns_fd);
-+ netns_fd = ret;
- }
-
- if (!create_db_dir(LXC_USERNIC_DB)) {
-diff --git a/src/lxc/utils.c b/src/lxc/utils.c
-index 9771b900..e6f600f9 100644
---- a/src/lxc/utils.c
-+++ b/src/lxc/utils.c
-@@ -2551,6 +2551,18 @@ bool has_fs_type(const char *path, fs_type_magic magic_val)
- return has_type;
- }
-
-+bool fhas_fs_type(int fd, fs_type_magic magic_val)
-+{
-+ int ret;
-+ struct statfs sb;
-+
-+ ret = fstatfs(fd, &sb);
-+ if (ret < 0)
-+ return false;
-+
-+ return is_fs_type(&sb, magic_val);
-+}
-+
- bool lxc_nic_exists(char *nic)
- {
- #define __LXC_SYS_CLASS_NET_LEN 15 + IFNAMSIZ + 1
---
-2.18.0
-
1 file changed, 5 insertions(+)
diff --git a/src/lxc/network.c b/src/lxc/network.c
-index 50b5293c..07c7d204 100644
+index dd294cd9..871a6e6b 100644
--- a/src/lxc/network.c
+++ b/src/lxc/network.c
-@@ -226,6 +226,11 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd
+@@ -194,6 +194,11 @@ static int instantiate_veth(struct lxc_handler *handler, struct lxc_netdev *netd
netdev->upscript, "up", argv);
if (err < 0)
goto out_delete;
# FIXME: This currently doesn't work due to the apparmor parser treating those as allowing all mounts.
# mount options=(rw,make-slave) -> **,
diff --git a/config/apparmor/abstractions/container-base.in b/config/apparmor/abstractions/container-base.in
-index 16529bbf..54f9ddf0 100644
+index 11ec5c45..0844fdbb 100644
--- a/config/apparmor/abstractions/container-base.in
+++ b/config/apparmor/abstractions/container-base.in
@@ -82,7 +82,6 @@
- mount options=(rw, nosuid, nodev, noexec, remount) -> /sys/,
deny /sys/firmware/efi/efivars/** rwklx,
deny /sys/kernel/security/** rwklx,
- mount options=(move) /sys/fs/cgroup/cgmanager/ -> /sys/fs/cgroup/cgmanager.lower/,
-@@ -91,6 +90,11 @@
+ mount options=(ro, nosuid, nodev, noexec, remount, strictatime) -> /sys/fs/cgroup/,
+@@ -90,6 +89,11 @@
# deny reads from debugfs
deny /sys/kernel/debug/{,**} rwklx,
6 files changed, 180 insertions(+), 49 deletions(-)
diff --git a/src/lxc/cgroups/cgfsng.c b/src/lxc/cgroups/cgfsng.c
-index 935b868b..9281cee0 100644
+index 56c8db54..265cf2cb 100644
--- a/src/lxc/cgroups/cgfsng.c
+++ b/src/lxc/cgroups/cgfsng.c
-@@ -818,6 +818,7 @@ static struct hierarchy *add_hierarchy(struct hierarchy ***h, char **clist, char
+@@ -817,6 +817,7 @@ static struct hierarchy *add_hierarchy(struct hierarchy ***h, char **clist, char
new->mountpoint = mountpoint;
new->base_cgroup = base_cgroup;
new->fullcgpath = NULL;
new->version = type;
newentry = append_null_to_list((void ***)h);
-@@ -1060,6 +1061,9 @@ static int cgroup_rmdir(struct hierarchy **hierarchies,
+@@ -1057,6 +1058,9 @@ static int cgroup_rmdir(struct hierarchy **hierarchies,
free(h->fullcgpath);
h->fullcgpath = NULL;
}
return 0;
-@@ -1071,6 +1075,7 @@ struct generic_userns_exec_data {
+@@ -1068,6 +1072,7 @@ struct generic_userns_exec_data {
struct lxc_conf *conf;
uid_t origuid; /* target uid in parent namespace */
char *path;
};
static int cgroup_rmdir_wrapper(void *data)
-@@ -1112,6 +1117,7 @@ static void cgfsng_destroy(struct cgroup_ops *ops, struct lxc_handler *handler)
+@@ -1109,6 +1114,7 @@ static void cgfsng_destroy(struct cgroup_ops *ops, struct lxc_handler *handler)
wrap.container_cgroup = ops->container_cgroup;
wrap.hierarchies = ops->hierarchies;
wrap.conf = handler->conf;
if (handler->conf && !lxc_list_empty(&handler->conf->id_map))
ret = userns_exec_1(handler->conf, cgroup_rmdir_wrapper, &wrap,
-@@ -1192,22 +1198,29 @@ on_error:
+@@ -1189,22 +1195,29 @@ on_error:
return bret;
}
if (ret < 0) {
ERROR("Failed to create cgroup \"%s\"", h->fullcgpath);
return false;
-@@ -1228,11 +1241,29 @@ static void remove_path_for_hierarchy(struct hierarchy *h, char *cgname)
+@@ -1225,11 +1238,29 @@ static void remove_path_for_hierarchy(struct hierarchy *h, char *cgname)
h->fullcgpath = NULL;
}
{
int i;
size_t len;
-@@ -1241,10 +1272,17 @@ static inline bool cgfsng_create(struct cgroup_ops *ops,
+@@ -1238,10 +1269,17 @@ static inline bool cgfsng_create(struct cgroup_ops *ops,
struct lxc_conf *conf = handler->conf;
if (ops->container_cgroup) {
if (!conf)
return false;
-@@ -1285,7 +1323,7 @@ again:
+@@ -1282,7 +1320,7 @@ again:
}
for (i = 0; ops->hierarchies[i]; i++) {
int j;
ERROR("Failed to create cgroup \"%s\"", ops->hierarchies[i]->fullcgpath);
free(ops->hierarchies[i]->fullcgpath);
-@@ -1307,7 +1345,7 @@ out_free:
+@@ -1304,7 +1342,7 @@ out_free:
return false;
}
{
int i, len;
char pidstr[25];
-@@ -1320,8 +1358,13 @@ static bool cgfsng_enter(struct cgroup_ops *ops, pid_t pid)
+@@ -1317,8 +1355,13 @@ static bool cgfsng_enter(struct cgroup_ops *ops, pid_t pid)
int ret;
char *fullpath;
ret = lxc_write_to_file(fullpath, pidstr, len, false, 0666);
if (ret != 0) {
SYSERROR("Failed to enter cgroup \"%s\"", fullpath);
-@@ -1395,9 +1438,15 @@ static int chown_cgroup_wrapper(void *data)
+@@ -1392,9 +1435,15 @@ static int chown_cgroup_wrapper(void *data)
char *fullpath;
char *path = arg->hierarchies[i]->fullcgpath;
/* Failures to chown() these are inconvenient but not
* detrimental We leave these owned by the container launcher,
-@@ -1416,8 +1465,11 @@ static int chown_cgroup_wrapper(void *data)
+@@ -1413,8 +1462,11 @@ static int chown_cgroup_wrapper(void *data)
(void)chowmod(fullpath, destuid, nsgid, 0664);
free(fullpath);
fullpath = must_make_path(path, "cgroup.subtree_control", NULL);
(void)chowmod(fullpath, destuid, nsgid, 0664);
-@@ -1426,12 +1478,15 @@ static int chown_cgroup_wrapper(void *data)
+@@ -1423,12 +1475,15 @@ static int chown_cgroup_wrapper(void *data)
fullpath = must_make_path(path, "cgroup.threads", NULL);
(void)chowmod(fullpath, destuid, nsgid, 0664);
free(fullpath);
{
struct generic_userns_exec_data wrap;
-@@ -1442,6 +1497,7 @@ static bool cgfsng_chown(struct cgroup_ops *ops, struct lxc_conf *conf)
+@@ -1439,6 +1494,7 @@ static bool cgfsng_chown(struct cgroup_ops *ops, struct lxc_conf *conf)
wrap.path = NULL;
wrap.hierarchies = ops->hierarchies;
wrap.conf = conf;
if (userns_exec_1(conf, chown_cgroup_wrapper, &wrap,
"chown_cgroup_wrapper") < 0) {
-@@ -1821,7 +1877,8 @@ static bool cgfsng_unfreeze(struct cgroup_ops *ops)
+@@ -1818,7 +1874,8 @@ static bool cgfsng_unfreeze(struct cgroup_ops *ops)
}
static const char *cgfsng_get_cgroup(struct cgroup_ops *ops,
{
struct hierarchy *h;
-@@ -1832,6 +1889,9 @@ static const char *cgfsng_get_cgroup(struct cgroup_ops *ops,
+@@ -1829,6 +1886,9 @@ static const char *cgfsng_get_cgroup(struct cgroup_ops *ops,
return NULL;
}
return h->fullcgpath ? h->fullcgpath + strlen(h->mountpoint) : NULL;
}
-@@ -1863,7 +1923,7 @@ static int __cg_unified_attach(const struct hierarchy *h, const char *name,
+@@ -1860,7 +1920,7 @@ static int __cg_unified_attach(const struct hierarchy *h, const char *name,
int fret = -1, idx = 0;
char *base_path = NULL, *container_cgroup = NULL, *full_path = NULL;
/* not running */
if (!container_cgroup)
return 0;
-@@ -1943,7 +2003,7 @@ static bool cgfsng_attach(struct cgroup_ops *ops, const char *name,
+@@ -1940,7 +2000,7 @@ static bool cgfsng_attach(struct cgroup_ops *ops, const char *name,
continue;
}
extern char *lxc_cmd_get_config_item(const char *name, const char *item, const char *lxcpath);
extern char *lxc_cmd_get_name(const char *hashed_sock);
diff --git a/src/lxc/criu.c b/src/lxc/criu.c
-index c3642162..456d19cf 100644
+index dc567d34..398e8e94 100644
--- a/src/lxc/criu.c
+++ b/src/lxc/criu.c
@@ -328,7 +328,7 @@ static void exec_criu(struct cgroup_ops *cgroup_ops, struct criu_opts *opts)
goto out_fini_handler;
}
diff --git a/src/lxc/start.c b/src/lxc/start.c
-index 739866d8..6944b310 100644
+index 23b7de06..cf053d20 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
-@@ -1597,7 +1597,7 @@ static int lxc_spawn(struct lxc_handler *handler)
+@@ -1599,7 +1599,7 @@ static int lxc_spawn(struct lxc_handler *handler)
}
}
ERROR("Failed creating cgroups");
goto out_delete_net;
}
-@@ -1691,10 +1691,10 @@ static int lxc_spawn(struct lxc_handler *handler)
+@@ -1693,10 +1693,10 @@ static int lxc_spawn(struct lxc_handler *handler)
goto out_delete_net;
}
goto out_delete_net;
/* Now we're ready to preserve the network namespace */
-@@ -1755,16 +1755,30 @@ static int lxc_spawn(struct lxc_handler *handler)
+@@ -1765,16 +1765,30 @@ static int lxc_spawn(struct lxc_handler *handler)
}
}
3 files changed, 24 insertions(+), 19 deletions(-)
diff --git a/src/lxc/initutils.c b/src/lxc/initutils.c
-index 6ab39a7d..2c142bd4 100644
+index cadd8275..8cc18932 100644
--- a/src/lxc/initutils.c
+++ b/src/lxc/initutils.c
-@@ -57,14 +57,15 @@ static char *copy_global_config_value(char *p)
+@@ -58,14 +58,15 @@ static char *copy_global_config_value(char *p)
const char *lxc_global_config_value(const char *option_name)
{
static const char * const options[][2] = {
#ifndef PR_SET_MM
#define PR_SET_MM 35
diff --git a/src/lxc/start.c b/src/lxc/start.c
-index 6944b310..bccd5807 100644
+index cf053d20..827a9ee9 100644
--- a/src/lxc/start.c
+++ b/src/lxc/start.c
-@@ -1762,17 +1762,20 @@ static int lxc_spawn(struct lxc_handler *handler)
+@@ -1772,17 +1772,20 @@ static int lxc_spawn(struct lxc_handler *handler)
TRACE("Set up legacy device cgroup controller limits");
if (cgns_supported()) {
+[Install]
+WantedBy=multi-user.target
diff --git a/configure.ac b/configure.ac
-index 59a0d410..ea312bf3 100644
+index 1acc4615..bcf7ab64 100644
--- a/configure.ac
+++ b/configure.ac
-@@ -674,6 +674,7 @@ AC_CONFIG_FILES([
+@@ -671,6 +671,7 @@ AC_CONFIG_FILES([
config/init/systemd/lxc.service
config/init/systemd/lxc@.service
config/init/systemd/lxc-net.service
%{_sysconfdir}/rc.d/init.d/lxc
%{_sysconfdir}/rc.d/init.d/lxc-net
diff --git a/src/lxc/cmd/lxc_monitord.c b/src/lxc/cmd/lxc_monitord.c
-index 38eee0a9..ffa6deca 100644
+index 0b9d7fd0..fdfdd283 100644
--- a/src/lxc/cmd/lxc_monitord.c
+++ b/src/lxc/cmd/lxc_monitord.c
-@@ -354,17 +354,44 @@ static void lxc_monitord_sig_handler(int sig)
+@@ -355,17 +355,44 @@ static void lxc_monitord_sig_handler(int sig)
int main(int argc, char *argv[])
{
"NOTE: lxc-monitord is intended for use by lxc internally\n"
" and does not need to be run by hand\n\n");
exit(EXIT_FAILURE);
-@@ -387,9 +414,6 @@ int main(int argc, char *argv[])
+@@ -388,9 +415,6 @@ int main(int argc, char *argv[])
INFO("Failed to open log file %s, log will be lost", lxcpath);
lxc_log_options_no_override();
if (sigfillset(&mask) ||
sigdelset(&mask, SIGILL) ||
sigdelset(&mask, SIGSEGV) ||
-@@ -422,15 +446,17 @@ int main(int argc, char *argv[])
+@@ -423,15 +447,17 @@ int main(int argc, char *argv[])
goto on_error;
monitord_created = true;
- * if-empty-statement construct is to quiet the
- * warn-unused-result warning.
- */
-- if (write(pipefd, "S", 1))
+- if (lxc_write_nointr(pipefd, "S", 1))
- ;
- close(pipefd);
+ if (pipefd != -1) {
+ * if-empty-statement construct is to quiet the
+ * warn-unused-result warning.
+ */
-+ if (write(pipefd, "S", 1))
++ if (lxc_write_nointr(pipefd, "S", 1))
+ ;
+ close(pipefd);
+ }
if (lxc_monitord_mainloop_add(&mon)) {
ERROR("Failed to add mainloop handlers");
-@@ -441,7 +467,7 @@ int main(int argc, char *argv[])
+@@ -442,7 +468,7 @@ int main(int argc, char *argv[])
lxc_raw_getpid(), mon.lxcpath);
for (;;) {
extra/0001-confile-add-lxc.monitor.signal.pdeath.patch
extra/0002-tests-add-lxc.monitor.signal.pdeath.patch
extra/0003-doc-Translate-lxc.monitor.signal.pdeath-into-Japanes.patch
-extra/0004-tests-lxc-test-apparmor-mount-show-a-log-on-error.patch
-extra/0005-tests-lxc-test-apparmor-mount-check-environment-earl.patch
-extra/0006-lsm-fixup-lsm_process_label_set_at-return-values.patch
-extra/0007-apparmor-use-fopen_cloexec.patch
-extra/0008-utils-add-must_concat-helper.patch
-extra/0009-apparmor-update-current-profiles.patch
-extra/0010-apparmor-profile-generation.patch
-extra/0011-apparmor-allow-start-container-to-change-to-lxc.patch
-extra/0012-tests-add-test-for-generated-apparmor-profiles.patch
-extra/0013-CVE-2018-6556-verify-netns-fd-in-lxc-user-nic.patch
+extra/0004-apparmor-profile-generation.patch
+extra/0005-tests-add-test-for-generated-apparmor-profiles.patch
+extra/0006-conf-fix-path-lxcpath-mixups-in-tty-setup.patch
-Subproject commit 4d879838422a8a4bf79ff540fa0dbcce4a82d872
+Subproject commit 56fb4efa7a2f2e45b46177785e1fa62978e3ff34