From: Wolfgang Bumiller Date: Mon, 20 Aug 2018 09:51:46 +0000 (+0200) Subject: bump version to 3.0.2+pve1-1 X-Git-Url: https://git.proxmox.com/?p=lxc.git;a=commitdiff_plain;h=f46e8fbf9b90a692c4ef7a868db897c4d8715e81 bump version to 3.0.2+pve1-1 Signed-off-by: Wolfgang Bumiller --- diff --git a/Makefile b/Makefile index 5139576..f5f4127 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ PACKAGE=lxc-pve -LXCVER=3.0.1+pve2 +LXCVER=3.0.2+pve1 DEBREL=1 SRCDIR=lxc diff --git a/debian/changelog b/debian/changelog index dc7c0aa..b4c17a9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +lxc (3.0.2+pve1-1) unstable; urgency=medium + + * update to lxc-3.0.2 + + -- Proxmox Support Team Mon, 20 Aug 2018 11:51:27 +0200 + lxc (3.0.1+pve2-1) unstable; urgency=medium * fix CVE-2018-6556 diff --git a/debian/patches/extra/0001-confile-add-lxc.monitor.signal.pdeath.patch b/debian/patches/extra/0001-confile-add-lxc.monitor.signal.pdeath.patch index 22ede6e..8a6c407 100644 --- a/debian/patches/extra/0001-confile-add-lxc.monitor.signal.pdeath.patch +++ b/debian/patches/extra/0001-confile-add-lxc.monitor.signal.pdeath.patch @@ -19,10 +19,10 @@ Signed-off-by: Christian Brauner 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 @@ -45,10 +45,10 @@ index 4ac26058..3e9e4e03 100644 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; @@ -57,10 +57,10 @@ index c5d6f5b1..d36987c8 100644 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; @@ -69,7 +69,7 @@ index ea3a71df..f7a879c3 100644 /* 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); @@ -117,7 +117,7 @@ index 4f46d7bf..8a7505da 100644 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); } @@ -131,7 +131,7 @@ index 4f46d7bf..8a7505da 100644 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; } @@ -146,10 +146,10 @@ index 4f46d7bf..8a7505da 100644 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; } diff --git a/debian/patches/extra/0003-doc-Translate-lxc.monitor.signal.pdeath-into-Japanes.patch b/debian/patches/extra/0003-doc-Translate-lxc.monitor.signal.pdeath-into-Japanes.patch index d513f4c..68adff6 100644 --- a/debian/patches/extra/0003-doc-Translate-lxc.monitor.signal.pdeath-into-Japanes.patch +++ b/debian/patches/extra/0003-doc-Translate-lxc.monitor.signal.pdeath-into-Japanes.patch @@ -11,10 +11,10 @@ Signed-off-by: KATOH Yasufumi 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 +@@ -3161,6 +3161,25 @@ by KATOH Yasufumi diff --git a/debian/patches/extra/0004-apparmor-profile-generation.patch b/debian/patches/extra/0004-apparmor-profile-generation.patch new file mode 100644 index 0000000..8f833dc --- /dev/null +++ b/debian/patches/extra/0004-apparmor-profile-generation.patch @@ -0,0 +1,1605 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Wolfgang Bumiller +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 +(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 \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 + diff --git a/debian/patches/extra/0004-tests-lxc-test-apparmor-mount-show-a-log-on-error.patch b/debian/patches/extra/0004-tests-lxc-test-apparmor-mount-show-a-log-on-error.patch deleted file mode 100644 index 35b24c3..0000000 --- a/debian/patches/extra/0004-tests-lxc-test-apparmor-mount-show-a-log-on-error.patch +++ /dev/null @@ -1,117 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -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 -(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 - diff --git a/debian/patches/extra/0005-tests-add-test-for-generated-apparmor-profiles.patch b/debian/patches/extra/0005-tests-add-test-for-generated-apparmor-profiles.patch new file mode 100644 index 0000000..aa22978 --- /dev/null +++ b/debian/patches/extra/0005-tests-add-test-for-generated-apparmor-profiles.patch @@ -0,0 +1,126 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Wolfgang Bumiller +Date: Tue, 24 Jul 2018 13:59:04 +0200 +Subject: [PATCH] tests: add test for generated apparmor profiles + +Signed-off-by: Wolfgang Bumiller +(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}_//&: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 + diff --git a/debian/patches/extra/0005-tests-lxc-test-apparmor-mount-check-environment-earl.patch b/debian/patches/extra/0005-tests-lxc-test-apparmor-mount-check-environment-earl.patch deleted file mode 100644 index afa0eda..0000000 --- a/debian/patches/extra/0005-tests-lxc-test-apparmor-mount-check-environment-earl.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -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 -(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 - diff --git a/debian/patches/extra/0006-conf-fix-path-lxcpath-mixups-in-tty-setup.patch b/debian/patches/extra/0006-conf-fix-path-lxcpath-mixups-in-tty-setup.patch new file mode 100644 index 0000000..b88bda7 --- /dev/null +++ b/debian/patches/extra/0006-conf-fix-path-lxcpath-mixups-in-tty-setup.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Wolfgang Bumiller +Date: Mon, 20 Aug 2018 10:02:35 +0200 +Subject: [PATCH] conf: fix path/lxcpath mixups in tty setup + +Signed-off-by: Wolfgang Bumiller +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 + diff --git a/debian/patches/extra/0006-lsm-fixup-lsm_process_label_set_at-return-values.patch b/debian/patches/extra/0006-lsm-fixup-lsm_process_label_set_at-return-values.patch deleted file mode 100644 index 162dfdb..0000000 --- a/debian/patches/extra/0006-lsm-fixup-lsm_process_label_set_at-return-values.patch +++ /dev/null @@ -1,74 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -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 -(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 - diff --git a/debian/patches/extra/0007-apparmor-use-fopen_cloexec.patch b/debian/patches/extra/0007-apparmor-use-fopen_cloexec.patch deleted file mode 100644 index 274fdc5..0000000 --- a/debian/patches/extra/0007-apparmor-use-fopen_cloexec.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -Date: Wed, 25 Jul 2018 12:06:16 +0200 -Subject: [PATCH] apparmor: use fopen_cloexec - -Signed-off-by: Wolfgang Bumiller -(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 - diff --git a/debian/patches/extra/0008-utils-add-must_concat-helper.patch b/debian/patches/extra/0008-utils-add-must_concat-helper.patch deleted file mode 100644 index f30146c..0000000 --- a/debian/patches/extra/0008-utils-add-must_concat-helper.patch +++ /dev/null @@ -1,62 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -Date: Wed, 18 Jul 2018 12:43:37 +0200 -Subject: [PATCH] utils: add must_concat helper - -Signed-off-by: Wolfgang Bumiller -(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 - diff --git a/debian/patches/extra/0009-apparmor-update-current-profiles.patch b/debian/patches/extra/0009-apparmor-update-current-profiles.patch deleted file mode 100644 index 5c0e7fd..0000000 --- a/debian/patches/extra/0009-apparmor-update-current-profiles.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -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 -(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 - diff --git a/debian/patches/extra/0010-apparmor-profile-generation.patch b/debian/patches/extra/0010-apparmor-profile-generation.patch deleted file mode 100644 index 7b0294f..0000000 --- a/debian/patches/extra/0010-apparmor-profile-generation.patch +++ /dev/null @@ -1,1605 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -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 -(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 \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 - diff --git a/debian/patches/extra/0011-apparmor-allow-start-container-to-change-to-lxc.patch b/debian/patches/extra/0011-apparmor-allow-start-container-to-change-to-lxc.patch deleted file mode 100644 index 6bffc13..0000000 --- a/debian/patches/extra/0011-apparmor-allow-start-container-to-change-to-lxc.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -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 -(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 - diff --git a/debian/patches/extra/0012-tests-add-test-for-generated-apparmor-profiles.patch b/debian/patches/extra/0012-tests-add-test-for-generated-apparmor-profiles.patch deleted file mode 100644 index aa22978..0000000 --- a/debian/patches/extra/0012-tests-add-test-for-generated-apparmor-profiles.patch +++ /dev/null @@ -1,126 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Wolfgang Bumiller -Date: Tue, 24 Jul 2018 13:59:04 +0200 -Subject: [PATCH] tests: add test for generated apparmor profiles - -Signed-off-by: Wolfgang Bumiller -(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}_//&: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 - diff --git a/debian/patches/extra/0013-CVE-2018-6556-verify-netns-fd-in-lxc-user-nic.patch b/debian/patches/extra/0013-CVE-2018-6556-verify-netns-fd-in-lxc-user-nic.patch deleted file mode 100644 index 33698b6..0000000 --- a/debian/patches/extra/0013-CVE-2018-6556-verify-netns-fd-in-lxc-user-nic.patch +++ /dev/null @@ -1,115 +0,0 @@ -From 590b215c4f5225cfb7fb58d9c56fabd5197bfdac Mon Sep 17 00:00:00 2001 -From: Christian Brauner -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 -(cherry picked from commit c1cf54ebf251fdbad1e971679614e81649f1c032) -Signed-off-by: Fabian Grünbichler ---- - 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 - diff --git a/debian/patches/pve/0002-PVE-Down-run-lxcnetaddbr-when-instantiating-veths.patch b/debian/patches/pve/0002-PVE-Down-run-lxcnetaddbr-when-instantiating-veths.patch index 2cb20d9..d5f3b9d 100644 --- a/debian/patches/pve/0002-PVE-Down-run-lxcnetaddbr-when-instantiating-veths.patch +++ b/debian/patches/pve/0002-PVE-Down-run-lxcnetaddbr-when-instantiating-veths.patch @@ -11,10 +11,10 @@ Signed-off-by: Wolfgang Bumiller 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; diff --git a/debian/patches/pve/0003-PVE-Config-deny-rw-mounting-of-sys-and-proc.patch b/debian/patches/pve/0003-PVE-Config-deny-rw-mounting-of-sys-and-proc.patch index 0fec1ba..902fe4e 100644 --- a/debian/patches/pve/0003-PVE-Config-deny-rw-mounting-of-sys-and-proc.patch +++ b/debian/patches/pve/0003-PVE-Config-deny-rw-mounting-of-sys-and-proc.patch @@ -38,7 +38,7 @@ index a5e6c35f..4c3a4ba8 100644 # 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 @@ @@ -48,8 +48,8 @@ index 16529bbf..54f9ddf0 100644 - 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, diff --git a/debian/patches/pve/0004-PVE-Up-separate-the-limiting-from-the-namespaced-cgr.patch b/debian/patches/pve/0004-PVE-Up-separate-the-limiting-from-the-namespaced-cgr.patch index 59bea81..239e7c9 100644 --- a/debian/patches/pve/0004-PVE-Up-separate-the-limiting-from-the-namespaced-cgr.patch +++ b/debian/patches/pve/0004-PVE-Up-separate-the-limiting-from-the-namespaced-cgr.patch @@ -24,10 +24,10 @@ Signed-off-by: Wolfgang Bumiller 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; @@ -35,7 +35,7 @@ index 935b868b..9281cee0 100644 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; @@ -45,7 +45,7 @@ index 935b868b..9281cee0 100644 } 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; @@ -53,7 +53,7 @@ index 935b868b..9281cee0 100644 }; 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; @@ -61,7 +61,7 @@ index 935b868b..9281cee0 100644 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; } @@ -96,7 +96,7 @@ index 935b868b..9281cee0 100644 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; } @@ -127,7 +127,7 @@ index 935b868b..9281cee0 100644 { 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) { @@ -145,7 +145,7 @@ index 935b868b..9281cee0 100644 if (!conf) return false; -@@ -1285,7 +1323,7 @@ again: +@@ -1282,7 +1320,7 @@ again: } for (i = 0; ops->hierarchies[i]; i++) { @@ -154,7 +154,7 @@ index 935b868b..9281cee0 100644 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; } @@ -163,7 +163,7 @@ index 935b868b..9281cee0 100644 { 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; @@ -179,7 +179,7 @@ index 935b868b..9281cee0 100644 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; @@ -196,7 +196,7 @@ index 935b868b..9281cee0 100644 /* 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); @@ -209,7 +209,7 @@ index 935b868b..9281cee0 100644 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); @@ -226,7 +226,7 @@ index 935b868b..9281cee0 100644 { 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; @@ -234,7 +234,7 @@ index 935b868b..9281cee0 100644 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, @@ -244,7 +244,7 @@ index 935b868b..9281cee0 100644 { 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; } @@ -254,7 +254,7 @@ index 935b868b..9281cee0 100644 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; @@ -263,7 +263,7 @@ index 935b868b..9281cee0 100644 /* 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; } @@ -460,7 +460,7 @@ index 816cd748..e16c0d79 100644 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) @@ -482,10 +482,10 @@ index c3642162..456d19cf 100644 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) } } @@ -494,7 +494,7 @@ index 739866d8..6944b310 100644 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; } @@ -507,7 +507,7 @@ index 739866d8..6944b310 100644 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) } } diff --git a/debian/patches/pve/0005-PVE-Up-start-initutils-make-cgroupns-separation-leve.patch b/debian/patches/pve/0005-PVE-Up-start-initutils-make-cgroupns-separation-leve.patch index 5881202..53848b0 100644 --- a/debian/patches/pve/0005-PVE-Up-start-initutils-make-cgroupns-separation-leve.patch +++ b/debian/patches/pve/0005-PVE-Up-start-initutils-make-cgroupns-separation-leve.patch @@ -17,10 +17,10 @@ Signed-off-by: Wolfgang Bumiller 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] = { @@ -57,10 +57,10 @@ index b815cd19..4d005679 100644 #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()) { diff --git a/debian/patches/pve/0007-PVE-Up-possibility-to-run-lxc-monitord-as-a-regular-.patch b/debian/patches/pve/0007-PVE-Up-possibility-to-run-lxc-monitord-as-a-regular-.patch index edd62a2..4d2aee0 100644 --- a/debian/patches/pve/0007-PVE-Up-possibility-to-run-lxc-monitord-as-a-regular-.patch +++ b/debian/patches/pve/0007-PVE-Up-possibility-to-run-lxc-monitord-as-a-regular-.patch @@ -80,10 +80,10 @@ index 00000000..40635168 +[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 @@ -104,10 +104,10 @@ index 87978feb..fcf307fa 100644 %{_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[]) { @@ -156,7 +156,7 @@ index 38eee0a9..ffa6deca 100644 "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(); @@ -166,7 +166,7 @@ index 38eee0a9..ffa6deca 100644 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; @@ -176,7 +176,7 @@ index 38eee0a9..ffa6deca 100644 - * 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) { @@ -186,14 +186,14 @@ index 38eee0a9..ffa6deca 100644 + * 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 (;;) { diff --git a/debian/patches/series b/debian/patches/series index 9446a34..1abfe9e 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -9,13 +9,6 @@ pve/0008-PVE-Deprecated-Make-lxc-.service-forking.patch 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 diff --git a/lxc b/lxc index 4d87983..56fb4ef 160000 --- a/lxc +++ b/lxc @@ -1 +1 @@ -Subproject commit 4d879838422a8a4bf79ff540fa0dbcce4a82d872 +Subproject commit 56fb4efa7a2f2e45b46177785e1fa62978e3ff34