diff --git a/kernel-patches/for-mainline/apparmor-lsm.diff b/kernel-patches/for-mainline/apparmor-lsm.diff index 6060fc7da..63af28c4f 100644 --- a/kernel-patches/for-mainline/apparmor-lsm.diff +++ b/kernel-patches/for-mainline/apparmor-lsm.diff @@ -7,12 +7,12 @@ Signed-off-by: John Johansen Signed-off-by: Andreas Gruenbacher --- - security/apparmor/lsm.c | 879 ++++++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 879 insertions(+) + security/apparmor/lsm.c | 889 ++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 889 insertions(+) --- /dev/null +++ b/security/apparmor/lsm.c -@@ -0,0 +1,879 @@ +@@ -0,0 +1,889 @@ +/* + * Copyright (C) 1998-2007 Novell/SUSE + * @@ -645,6 +645,16 @@ Signed-off-by: Andreas Gruenbacher + unsigned long prot, unsigned long flags, + unsigned long addr, unsigned long addr_only) +{ ++ if ((addr < mmap_min_addr) && !capable(CAP_SYS_RAWIO)) { ++ struct aa_profile *profile = aa_get_profile(current); ++ if (profile) ++ /* future control check here */ ++ return -EACCES; ++ else ++ return -EACCES; ++ aa_put_profile(profile); ++ } ++ + return aa_mmap(file, "file_mmap", prot, flags); +} + diff --git a/kernel-patches/for-mainline/apparmor-main.diff b/kernel-patches/for-mainline/apparmor-main.diff index 4e64e6b40..3c5f17d0d 100644 --- a/kernel-patches/for-mainline/apparmor-main.diff +++ b/kernel-patches/for-mainline/apparmor-main.diff @@ -7,12 +7,12 @@ Signed-off-by: John Johansen Signed-off-by: Andreas Gruenbacher --- - security/apparmor/main.c | 1421 +++++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 1421 insertions(+) + security/apparmor/main.c | 1479 +++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 1479 insertions(+) --- /dev/null +++ b/security/apparmor/main.c -@@ -0,0 +1,1421 @@ +@@ -0,0 +1,1479 @@ +/* + * Copyright (C) 2002-2007 Novell/SUSE + * @@ -84,6 +84,8 @@ Signed-off-by: Andreas Gruenbacher +static void aa_audit_file_sub_mask(struct audit_buffer *ab, char *buffer, + int mask) +{ ++ const char unsafex[] = "upcn"; ++ const char safex[] = "UPCN"; + char *m = buffer; + + if (mask & AA_EXEC_MMAP) @@ -95,37 +97,18 @@ Signed-off-by: Andreas Gruenbacher + else if (mask & MAY_APPEND) + *m++ = 'a'; + if (mask & MAY_EXEC) { -+ if (mask & AA_EXEC_UNSAFE) { -+ switch(mask & AA_EXEC_MODIFIERS) { -+ case AA_EXEC_UNCONFINED: -+ *m++ = 'u'; -+ break; -+ case AA_EXEC_PIX: -+ *m++ = 'p'; -+ /* fall through */ -+ case AA_EXEC_INHERIT: -+ *m++ = 'i'; -+ break; -+ case AA_EXEC_PROFILE: -+ *m++ = 'p'; -+ break; -+ } -+ } else { -+ switch(mask & AA_EXEC_MODIFIERS) { -+ case AA_EXEC_UNCONFINED: -+ *m++ = 'U'; -+ break; -+ case AA_EXEC_PIX: -+ *m++ = 'P'; -+ /* fall through */ -+ case AA_EXEC_INHERIT: -+ *m++ = 'I'; -+ break; -+ case AA_EXEC_PROFILE: -+ *m++ = 'P'; -+ break; -+ } ++ int index = AA_EXEC_INDEX(mask); ++ /* all indexes > 4 are also named transitions */ ++ if (index > 4) ++ index = 4; ++ if (index > 0) { ++ if (mask & AA_EXEC_UNSAFE) ++ *m++ = unsafex[index - 1]; ++ else ++ *m++ = safex[index - 1]; + } ++ if (mask & AA_EXEC_INHERIT) ++ *m++ = 'i'; + *m++ = 'x'; + } + if (mask & AA_MAY_LINK) @@ -369,7 +352,7 @@ Signed-off-by: Andreas Gruenbacher + int *request_mask, int *audit_mask) +{ + unsigned int state; -+ int l_mode, t_mode, denied_mask = 0; ++ int l_mode, t_mode, l_x, t_x, denied_mask = 0; + int link_mask = AA_MAY_LINK << target_mode; + + *request_mask = link_mask; @@ -393,7 +376,8 @@ Signed-off-by: Andreas Gruenbacher + return denied_mask; + } + -+ /* Do link perm subset test ++ /* Do link perm subset test requiring permission on link are a ++ * subset of the permissions on target. + * If a subset test is required a permission subset test of the + * perms for the link are done against the user::other of the + * target's 'r', 'w', 'x', 'a', 'k', and 'm' permissions. @@ -405,6 +389,9 @@ Signed-off-by: Andreas Gruenbacher + + t_mode = aa_match(profile->file_rules, target, NULL); + ++ l_x = l_mode & (ALL_AA_EXEC_TYPE | AA_EXEC_BITS); ++ t_x = t_mode & (ALL_AA_EXEC_TYPE | AA_EXEC_BITS); ++ + /* For actual subset test ignore valid-profile-transition flags, + * and link bits + */ @@ -414,12 +401,20 @@ Signed-off-by: Andreas Gruenbacher + *request_mask = l_mode | link_mask; + + if (l_mode) { ++ int x = l_x | (t_x & ALL_AA_EXEC_UNSAFE); + denied_mask |= l_mode & ~t_mode; -+ if ((l_mode & AA_EXEC_BITS) && -+ (l_mode & ALL_AA_EXEC_TYPE) != -+ (t_mode & ALL_AA_EXEC_TYPE)) -+ denied_mask = (denied_mask & ~ALL_AA_EXEC_TYPE) | -+ (l_mode & (ALL_AA_EXEC_TYPE | AA_EXEC_BITS)); ++ /* mask off x modes not used by link */ ++ ++ /* handle exec subset ++ * - link safe exec issubset of unsafe exec ++ * - no link x perm is subset of target having x perm ++ */ ++ if ((l_mode & AA_USER_EXEC) && ++ (x & AA_USER_EXEC_TYPE) != (t_x & AA_USER_EXEC_TYPE)) ++ denied_mask = AA_USER_EXEC | (l_x & AA_USER_EXEC_TYPE); ++ if ((l_mode & AA_OTHER_EXEC) && ++ (x & AA_OTHER_EXEC_TYPE) != (t_x & AA_OTHER_EXEC_TYPE)) ++ denied_mask = AA_OTHER_EXEC | (l_x & AA_OTHER_EXEC_TYPE); + } + + return denied_mask; @@ -484,6 +479,13 @@ Signed-off-by: Andreas Gruenbacher + } +} + ++static char *new_compound_name(const char *n1, const char *n2) ++{ ++ char *name = kmalloc(strlen(n1) + strlen(n2) + 3, GFP_KERNEL); ++ if (name) ++ sprintf(name, "%s//%s", n1, n2); ++ return name; ++} +static inline void aa_put_name_buffer(char *buffer) +{ + kfree(buffer); @@ -840,16 +842,37 @@ Signed-off-by: Andreas Gruenbacher +} + +static struct aa_profile * -+aa_register_find(struct aa_profile *profile, const char *name, int mandatory, -+ int complain, struct aa_audit *sa) ++aa_register_find(struct aa_profile *profile, const char* ns_name, ++ const char *name, int mandatory, int complain, ++ struct aa_audit *sa) +{ ++ struct aa_namespace *ns; + struct aa_profile *new_profile; ++ int ns_ref = 0; ++ ++ if (profile) ++ ns = profile->ns; ++ else ++ ns = default_namespace; ++ ++ if (ns_name) { ++ /* locate the profile namespace */ ++ ns = aa_find_namespace(ns_name); ++ if (!ns) { ++ if (mandatory) { ++ sa->info = "profile namespace not found"; ++ sa->denied_mask = sa->request_mask; ++ sa->error_code = -ENOENT; ++ return ERR_PTR(-ENOENT); ++ } else { ++ return NULL; ++ } ++ } ++ ns_ref++; ++ } + + /* Locate new profile */ -+ if (profile) -+ new_profile = aa_find_profile(profile->ns, name); -+ else -+ new_profile = aa_find_profile(default_namespace, name); ++ new_profile = aa_find_profile(ns, name); + + if (new_profile) { + AA_DEBUG("%s: setting profile %s\n", @@ -863,16 +886,82 @@ Signed-off-by: Andreas Gruenbacher + aa_dup_profile(profile->ns->null_complain_profile); + } else { + sa->error_code = -EACCES; -+ return ERR_PTR(aa_audit_file(profile, sa)); ++ if (ns_ref) ++ aa_put_namespace(ns); ++ return ERR_PTR(-EACCES); + } + } else { + /* Only way we can get into this code is if task -+ * is unconfined, or pix. ++ * is unconfined, pix, nix. + */ + AA_DEBUG("%s: No profile found for exec image '%s'\n", + __FUNCTION__, + name); + } ++ if (ns_ref) ++ aa_put_namespace(ns); ++ return new_profile; ++} ++ ++static struct aa_profile * ++aa_x_to_profile(struct aa_profile *profile, const char *filename, int xmode, ++ struct aa_audit *sa, char **child) ++{ ++ struct aa_profile *new_profile = NULL; ++ int ix = xmode & AA_EXEC_INHERIT; ++ int complain = PROFILE_COMPLAIN(profile); ++ int index; ++ ++ *child = NULL; ++ switch (xmode & AA_EXEC_MODIFIERS) { ++ case 0: ++ /* only valid with ix flag */ ++ ix = 1; ++ break; ++ case AA_EXEC_UNCONFINED: ++ /* only valid without ix flag */ ++ ix = 0; ++ break; ++ case AA_EXEC_PROFILE: ++ new_profile = aa_register_find(profile, NULL, filename, !ix, ++ complain, sa); ++ break; ++ case AA_EXEC_CHILD: ++ *child = new_compound_name(profile->name, filename); ++ sa->name2 = *child; ++ if (!*child) { ++ sa->info = "Failed name resolution - exec failed"; ++ sa->error_code = -ENOMEM; ++ new_profile = ERR_PTR(-ENOMEM); ++ } else { ++ new_profile = aa_register_find(profile, NULL, *child, ++ !ix, complain, sa); ++ } ++ break; ++ default: ++ /* all other indexes are named transitions */ ++ index = AA_EXEC_INDEX(xmode); ++ if (index - 4 > profile->exec_table_size) { ++ sa->info = "invalid named transition - exec failed"; ++ sa->error_code = -EACCES; ++ new_profile = ERR_PTR(-EACCES); ++ } else { ++ char *ns_name = NULL; ++ char *name = profile->exec_table[index - 4]; ++ if (*name == ':') { ++ ns_name = name + 1; ++ name = ns_name + strlen(ns_name) + 1; ++ } ++ sa->name2 = name; ++ sa->name3 = ns_name; ++ new_profile = ++ aa_register_find(profile, ns_name, name, ++ !ix, complain, sa); ++ } ++ } ++ if (IS_ERR(new_profile)) ++ /* all these failures must be audited - no quieting */ ++ return ERR_PTR(aa_audit_reject(profile, sa)); + return new_profile; +} + @@ -886,7 +975,7 @@ Signed-off-by: Andreas Gruenbacher +int aa_register(struct linux_binprm *bprm) +{ + const char *filename; -+ char *buffer = NULL; ++ char *buffer = NULL, *child = NULL; + struct file *filp = bprm->file; + struct aa_profile *profile, *old_profile, *new_profile = NULL; + int exec_mode, complain = 0, shift; @@ -907,7 +996,7 @@ Signed-off-by: Andreas Gruenbacher + if (profile) { + sa.info = "Failed name resolution - exec failed"; + sa.error_code = PTR_ERR(filename); -+ aa_audit_reject(profile, &sa); ++ aa_audit_file(profile, &sa); + return sa.error_code; + } else + return 0; @@ -926,46 +1015,16 @@ Signed-off-by: Andreas Gruenbacher + exec_mode = aa_match(profile->file_rules, filename, + &sa.audit_mask); + ++ + if (exec_mode & sa.request_mask) { -+ switch ((exec_mode >> shift) & AA_EXEC_MODIFIERS) { -+ case AA_EXEC_INHERIT: -+ AA_DEBUG("%s: INHERIT %s\n", -+ __FUNCTION__, -+ filename); -+ /* nothing to be done here */ ++ int xm = exec_mode >> shift; ++ new_profile = aa_x_to_profile(profile, filename, ++ xm, &sa, &child); ++ ++ if (!new_profile && (xm & AA_EXEC_INHERIT)) ++ /* (p|c|n|)ix - don't change profile */ + goto cleanup; -+ -+ case AA_EXEC_UNCONFINED: -+ AA_DEBUG("%s: UNCONFINED %s\n", -+ __FUNCTION__, -+ filename); -+ -+ /* detach current profile */ -+ new_profile = NULL; -+ break; -+ -+ case AA_EXEC_PROFILE: -+ AA_DEBUG("%s: PROFILE %s\n", -+ __FUNCTION__, -+ filename); -+ new_profile = aa_register_find(profile, -+ filename, -+ 1, complain, -+ &sa); -+ break; -+ case AA_EXEC_PIX: -+ AA_DEBUG("%s: PROFILE %s\n", -+ __FUNCTION__, -+ filename); -+ new_profile = aa_register_find(profile, -+ filename, -+ 0, complain, -+ &sa); -+ if (!new_profile) -+ /* inherit - nothing to be done here */ -+ goto cleanup; -+ break; -+ } ++ /* error case caught below */ + + } else if (sa.request_mask & AUDIT_QUIET_MASK(sa.audit_mask)) { + /* quiet failed exit */ @@ -985,7 +1044,7 @@ Signed-off-by: Andreas Gruenbacher + } + } else { + /* Unconfined task, load profile if it exists */ -+ new_profile = aa_register_find(NULL, filename, 0, 0, &sa); ++ new_profile = aa_register_find(NULL, NULL, filename, 0, 0, &sa); + if (new_profile == NULL) + goto cleanup; + } @@ -1039,6 +1098,7 @@ Signed-off-by: Andreas Gruenbacher + } + +cleanup: ++ aa_put_name_buffer(child); + aa_put_name_buffer(buffer); + if (IS_ERR(new_profile)) + return PTR_ERR(new_profile); @@ -1180,7 +1240,7 @@ Signed-off-by: Andreas Gruenbacher +int aa_change_profile(const char *ns_name, const char *name) +{ + struct aa_task_context *cxt; -+ struct aa_profile *profile; ++ struct aa_profile *profile = NULL; + struct aa_namespace *ns = NULL; + struct aa_audit sa; + unsigned int state; @@ -1196,23 +1256,23 @@ Signed-off-by: Andreas Gruenbacher +repeat: + task_lock(current); + cxt = aa_task_context(current); -+ if (!cxt) { -+ task_unlock(current); -+ return -EPERM; -+ } -+ profile = aa_dup_profile(cxt->profile); ++ if (cxt) ++ profile = aa_dup_profile(cxt->profile); + task_unlock(current); + + if (ns_name) + ns = aa_find_namespace(ns_name); -+ else ++ else if (profile) + ns = aa_get_namespace(profile->ns); ++ else ++ ns = aa_get_namespace(default_namespace); ++ + if (!ns) { + aa_put_profile(profile); + return -ENOENT; + } + -+ if (PROFILE_COMPLAIN(profile) || ++ if (!profile || PROFILE_COMPLAIN(profile) || + (ns == profile->ns && + (aa_match(profile->file_rules, name, NULL) & AA_CHANGE_PROFILE))) + error = do_change_profile(profile, ns, name, 0, 0, &sa); @@ -1287,16 +1347,14 @@ Signed-off-by: Andreas Gruenbacher + else + profile_name = profile->name; + -+ name = kmalloc(strlen(hat_name) + 3 + strlen(profile_name), -+ GFP_KERNEL); ++ name = new_compound_name(profile_name, hat_name); + if (!name) { + error = -ENOMEM; + goto out; + } -+ sprintf(name, "%s//%s", profile_name, hat_name); + error = do_change_profile(profile, profile->ns, name, cookie, + 0, &sa); -+ kfree(name); ++ aa_put_name_buffer(name); + } else if (previous_profile) + error = do_change_profile(profile, profile->ns, + previous_profile->name, cookie, 1, diff --git a/kernel-patches/for-mainline/apparmor-misc.diff b/kernel-patches/for-mainline/apparmor-misc.diff index 0195310c0..34c963b08 100644 --- a/kernel-patches/for-mainline/apparmor-misc.diff +++ b/kernel-patches/for-mainline/apparmor-misc.diff @@ -13,13 +13,13 @@ Signed-off-by: Andreas Gruenbacher --- security/apparmor/Kconfig | 42 ++++ security/apparmor/Makefile | 13 + - security/apparmor/apparmor.h | 358 +++++++++++++++++++++++++++++++++++++++++ - security/apparmor/apparmorfs.c | 280 ++++++++++++++++++++++++++++++++ - security/apparmor/inline.h | 250 ++++++++++++++++++++++++++++ + security/apparmor/apparmor.h | 367 +++++++++++++++++++++++++++++++++++++++++ + security/apparmor/apparmorfs.c | 280 +++++++++++++++++++++++++++++++ + security/apparmor/inline.h | 250 +++++++++++++++++++++++++++ security/apparmor/list.c | 156 +++++++++++++++++ security/apparmor/locking.txt | 68 +++++++ - security/apparmor/procattr.c | 195 ++++++++++++++++++++++ - 8 files changed, 1362 insertions(+) + security/apparmor/procattr.c | 195 +++++++++++++++++++++ + 8 files changed, 1371 insertions(+) --- /dev/null +++ b/security/apparmor/Kconfig @@ -84,7 +84,7 @@ Signed-off-by: Andreas Gruenbacher + $(call cmd,make-caps) --- /dev/null +++ b/security/apparmor/apparmor.h -@@ -0,0 +1,358 @@ +@@ -0,0 +1,367 @@ +/* + * Copyright (C) 1998-2007 Novell/SUSE + * @@ -113,30 +113,31 @@ Signed-off-by: Andreas Gruenbacher +#define AA_EXEC_MMAP 0x0040 +#define AA_MAY_MOUNT 0x0080 /* no direct audit mapping */ +#define AA_EXEC_UNSAFE 0x0100 -+#define AA_EXEC_MOD_0 0x0200 -+#define AA_EXEC_MOD_1 0x0400 -+#define AA_EXEC_MOD_2 0x0800 -+#define AA_EXEC_MOD_3 0x1000 -+#define AA_EXEC_MOD_4 0x2000 ++#define AA_EXEC_INHERIT 0x0200 ++#define AA_EXEC_MOD_0 0x0400 ++#define AA_EXEC_MOD_1 0x0800 ++#define AA_EXEC_MOD_2 0x1000 ++#define AA_EXEC_MOD_3 0x2000 + +#define AA_BASE_PERMS (MAY_READ | MAY_WRITE | MAY_EXEC | \ + MAY_APPEND | AA_MAY_LINK | \ + AA_MAY_LOCK | AA_EXEC_MMAP | \ + AA_MAY_MOUNT | AA_EXEC_UNSAFE | \ -+ AA_EXEC_MOD_0 | AA_EXEC_MOD_1 | \ -+ AA_EXEC_MOD_2 | AA_EXEC_MOD_3 | \ -+ AA_EXEC_MOD_4) ++ AA_EXEC_INHERIT | AA_EXEC_MOD_0 | \ ++ AA_EXEC_MOD_1 | AA_EXEC_MOD_2 | \ ++ AA_EXEC_MOD_3) + +#define AA_EXEC_MODIFIERS (AA_EXEC_MOD_0 | AA_EXEC_MOD_1 | \ -+ AA_EXEC_MOD_2 | AA_EXEC_MOD_3 | \ -+ AA_EXEC_MOD_4) ++ AA_EXEC_MOD_2 | AA_EXEC_MOD_3) + -+#define AA_EXEC_TYPE (AA_EXEC_UNSAFE | AA_EXEC_MODIFIERS) ++#define AA_EXEC_TYPE (AA_EXEC_UNSAFE | AA_EXEC_INHERIT | \ ++ AA_EXEC_MODIFIERS) + +#define AA_EXEC_UNCONFINED AA_EXEC_MOD_0 -+#define AA_EXEC_INHERIT AA_EXEC_MOD_1 -+#define AA_EXEC_PROFILE (AA_EXEC_MOD_0 | AA_EXEC_MOD_1) -+#define AA_EXEC_PIX AA_EXEC_MOD_2 ++#define AA_EXEC_PROFILE AA_EXEC_MOD_1 ++#define AA_EXEC_CHILD (AA_EXEC_MOD_0 | AA_EXEC_MOD_1) ++/* remaining exec modes are index into profile name table */ ++#define AA_EXEC_INDEX(mode) ((mode & AA_EXEC_MODIFIERS) >> 10) + +#define AA_USER_SHIFT 0 +#define AA_OTHER_SHIFT 14 @@ -157,20 +158,26 @@ Signed-off-by: Andreas Gruenbacher + +#define AA_EXEC_BITS (AA_USER_EXEC | AA_OTHER_EXEC) + ++#define ALL_AA_EXEC_UNSAFE ((AA_EXEC_UNSAFE << AA_USER_SHIFT) | \ ++ (AA_EXEC_UNSAFE << AA_OTHER_SHIFT)) ++ +#define ALL_AA_EXEC_TYPE (AA_USER_EXEC_TYPE | AA_OTHER_EXEC_TYPE) + +/* overloaded permissions for link pairs */ +#define AA_LINK_SUBSET_TEST 0x0020 + ++#define AA_USER_PTRACE 0x10000000 ++#define AA_OTHER_PTRACE 0x20000000 ++#define AA_PTRACE_PERMS (AA_USER_PTRACE | AA_OTHER_PTRACE) ++ +/* shared permissions that are not duplicated in user::other */ -+#define AA_AUDIT_FIELD 0x10000000 -+#define AA_CHANGE_HAT 0x20000000 -+#define AA_CHANGE_PROFILE 0x40000000 ++#define AA_CHANGE_HAT 0x40000000 ++#define AA_CHANGE_PROFILE 0x80000000 + -+#define AA_SHARED_PERMS (AA_CHANGE_HAT | AA_CHANGE_PROFILE | \ -+ AA_AUDIT_FIELD) ++#define AA_SHARED_PERMS (AA_CHANGE_HAT | AA_CHANGE_PROFILE) + -+#define AA_VALID_PERM_MASK (AA_FILE_PERMS | AA_SHARED_PERMS) ++#define AA_VALID_PERM_MASK (AA_FILE_PERMS | AA_PTRACE_PERMS | \ ++ AA_SHARED_PERMS) + +/* audit bits for the second accept field */ +#define AUDIT_FILE_MASK 0x1fc07f @@ -270,6 +277,8 @@ Signed-off-by: Andreas Gruenbacher + struct list_head list; + struct aa_namespace *ns; + ++ int exec_table_size; ++ char **exec_table; + struct aa_dfa *file_rules; + struct { + int complain; diff --git a/kernel-patches/for-mainline/apparmor-module_interface.diff b/kernel-patches/for-mainline/apparmor-module_interface.diff index fb744c0eb..105689193 100644 --- a/kernel-patches/for-mainline/apparmor-module_interface.diff +++ b/kernel-patches/for-mainline/apparmor-module_interface.diff @@ -8,10 +8,10 @@ Signed-off-by: John Johansen Signed-off-by: Andreas Gruenbacher --- - security/apparmor/match.c | 364 +++++++++++++++ + security/apparmor/match.c | 364 ++++++++++++++ security/apparmor/match.h | 87 +++ - security/apparmor/module_interface.c | 815 +++++++++++++++++++++++++++++++++++ - 3 files changed, 1266 insertions(+) + security/apparmor/module_interface.c | 874 +++++++++++++++++++++++++++++++++++ + 3 files changed, 1325 insertions(+) --- /dev/null +++ b/security/apparmor/match.c @@ -472,7 +472,7 @@ Signed-off-by: Andreas Gruenbacher +#endif /* __MATCH_H */ --- /dev/null +++ b/security/apparmor/module_interface.c -@@ -0,0 +1,815 @@ +@@ -0,0 +1,874 @@ +/* + * Copyright (C) 1998-2007 Novell/SUSE + * @@ -716,7 +716,7 @@ Signed-off-by: Andreas Gruenbacher + * + * returns dfa or ERR_PTR + */ -+struct aa_dfa *aa_unpack_dfa(struct aa_ext *e) ++static struct aa_dfa *aa_unpack_dfa(struct aa_ext *e) +{ + char *blob = NULL; + size_t size, error = 0; @@ -748,16 +748,53 @@ Signed-off-by: Andreas Gruenbacher + return dfa; +} + ++static int aa_unpack_exec_table(struct aa_ext *e, struct aa_profile *profile) ++{ ++ void *pos = e->pos; ++ ++ /* exec table is optional */ ++ if (aa_is_nameX(e, AA_STRUCT, "xtable")) { ++ int i, size; ++ ++ size = aa_is_array(e, NULL); ++ /* currently 4 exec bits and entries 0-3 are reserved iupcx */ ++ if (size > 16 - 4) ++ goto fail; ++ profile->exec_table = kzalloc(sizeof(char *) * size, ++ GFP_KERNEL); ++ if (!profile->exec_table) ++ goto fail; ++ ++ for (i = 0; i < size; i++) { ++ char *tmp; ++ if (!aa_is_dynstring(e, &tmp, NULL)) ++ goto fail; ++ /* note: strings beginning with a : have an embedded ++ \0 seperating the profile ns name from the profile ++ name */ ++ profile->exec_table[i] = tmp; ++ } ++ if (!aa_is_nameX(e, AA_ARRAYEND, NULL)) ++ goto fail; ++ if (!aa_is_nameX(e, AA_STRUCTEND, NULL)) ++ goto fail; ++ } ++ return 1; ++ ++fail: ++ e->pos = pos; ++ return 0; ++} ++ +/** + * aa_unpack_profile - unpack a serialized profile + * @e: serialized data extent information -+ * @operation: operation profile is being unpacked for ++ * @sa: audit struct for the operation + */ +static struct aa_profile *aa_unpack_profile(struct aa_ext *e, -+ const char *operation) ++ struct aa_audit *sa) +{ + struct aa_profile *profile = NULL; -+ struct aa_audit sa; + + int error = -EPROTO; + @@ -798,25 +835,21 @@ Signed-off-by: Andreas Gruenbacher + error = PTR_ERR(profile->file_rules); + profile->file_rules = NULL; + goto fail; -+ if (!aa_is_u16(e, &profile->audit_network[i], NULL)) -+ goto fail; -+ if (!aa_is_u16(e, &profile->quiet_network[i], NULL)) -+ goto fail; + } + ++ if (!aa_unpack_exec_table(e, profile)) ++ goto fail; ++ + if (!aa_is_nameX(e, AA_STRUCTEND, NULL)) + goto fail; + + return profile; + +fail: -+ memset(&sa, 0, sizeof(sa)); -+ sa.operation = operation; -+ sa.gfp_mask = GFP_KERNEL; -+ sa.name = profile && profile->name ? profile->name : "unknown"; -+ if (!sa.info) -+ sa.info = "failed to unpack profile"; -+ aa_audit_status(NULL, &sa); ++ sa->name = profile && profile->name ? profile->name : "unknown"; ++ if (!sa->info) ++ sa->info = "failed to unpack profile"; ++ aa_audit_status(NULL, sa); + + if (profile) + free_aa_profile(profile); @@ -831,27 +864,19 @@ Signed-off-by: Andreas Gruenbacher + * + * returns error or 0 if header is good + */ -+static int aa_verify_header(struct aa_ext *e, const char *operation) ++static int aa_verify_header(struct aa_ext *e, struct aa_audit *sa) +{ + /* get the interface version */ + if (!aa_is_u32(e, &e->version, "version")) { -+ struct aa_audit sa; -+ memset(&sa, 0, sizeof(sa)); -+ sa.operation = operation; -+ sa.gfp_mask = GFP_KERNEL; -+ sa.info = "invalid profile format"; -+ aa_audit_status(NULL, &sa); ++ sa->info = "invalid profile format"; ++ aa_audit_status(NULL, sa); + return -EPROTONOSUPPORT; + } + + /* check that the interface version is currently supported */ -+ if (e->version != 4) { -+ struct aa_audit sa; -+ memset(&sa, 0, sizeof(sa)); -+ sa.operation = operation; -+ sa.gfp_mask = GFP_KERNEL; -+ sa.info = "unsupported interface version"; -+ aa_audit_status(NULL, &sa); ++ if (e->version != 5) { ++ sa->info = "unsupported interface version"; ++ aa_audit_status(NULL, sa); + return -EPROTONOSUPPORT; + } + @@ -878,11 +903,17 @@ Signed-off-by: Andreas Gruenbacher + .pos = data, + .ns_name = NULL + }; -+ ssize_t error = aa_verify_header(&e, "profile_load"); ++ ssize_t error; ++ struct aa_audit sa; ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "profile_load"; ++ sa.gfp_mask = GFP_KERNEL; ++ ++ error = aa_verify_header(&e, &sa); + if (error) + return error; + -+ profile = aa_unpack_profile(&e, "profile_load"); ++ profile = aa_unpack_profile(&e, &sa); + if (IS_ERR(profile)) + return PTR_ERR(profile); + @@ -914,6 +945,10 @@ Signed-off-by: Andreas Gruenbacher + /* A profile with this name exists already. */ + write_unlock(&ns->lock); + write_unlock(&profile_ns_list_lock); ++ sa.name = profile->name; ++ sa.name2 = ns->name; ++ sa.info = "failed: profile already loaded"; ++ aa_audit_status(NULL, &sa); + mutex_unlock(&aa_interface_lock); + aa_put_profile(profile); + return -EEXIST; @@ -923,8 +958,11 @@ Signed-off-by: Andreas Gruenbacher + list_add(&profile->list, &ns->profiles); + write_unlock(&ns->lock); + write_unlock(&profile_ns_list_lock); -+ mutex_unlock(&aa_interface_lock); + ++ sa.name = profile->name; ++ sa.name2 = ns->name; ++ aa_audit_status(NULL, &sa); ++ mutex_unlock(&aa_interface_lock); + return size; +} + @@ -970,12 +1008,17 @@ Signed-off-by: Andreas Gruenbacher + .pos = udata, + .ns_name = NULL + }; ++ ssize_t error; ++ struct aa_audit sa; ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "profile_replace"; ++ sa.gfp_mask = GFP_KERNEL; + -+ ssize_t error = aa_verify_header(&e, "profile_replace"); ++ error = aa_verify_header(&e, &sa); + if (error) + return error; + -+ new_profile = aa_unpack_profile(&e, "profile_replace"); ++ new_profile = aa_unpack_profile(&e, &sa); + if (IS_ERR(new_profile)) + return PTR_ERR(new_profile); + @@ -1013,13 +1056,16 @@ Signed-off-by: Andreas Gruenbacher + } + new_profile->ns = aa_get_namespace(ns); + ns->profile_count++; ++ /* not don't need an extra ref count to keep new_profile as ++ * it is protect by the interface mutex */ + list_add(&new_profile->list, &ns->profiles); + write_unlock(&ns->lock); + write_unlock(&profile_ns_list_lock); + -+ if (!old_profile) ++ if (!old_profile) { ++ sa.operation = "profile_load"; + goto out; -+ ++ } + /* + * Replacement needs to allocate a new aa_task_context for each + * task confined by old_profile. To do this the profile locks @@ -1048,6 +1094,9 @@ Signed-off-by: Andreas Gruenbacher + aa_put_profile(old_profile); + +out: ++ sa.name = new_profile->name; ++ sa.name2 = ns->name; ++ aa_audit_status(NULL, &sa); + mutex_unlock(&aa_interface_lock); + return size; +} @@ -1064,29 +1113,35 @@ Signed-off-by: Andreas Gruenbacher +{ + struct aa_namespace *ns; + struct aa_profile *profile; ++ struct aa_audit sa; ++ memset(&sa, 0, sizeof(sa)); ++ sa.operation = "profile_remove"; ++ sa.gfp_mask = GFP_KERNEL; + + mutex_lock(&aa_interface_lock); + write_lock(&profile_ns_list_lock); + -+ if (name[0] == '/') { -+ ns = default_namespace; -+ } else { -+ char *split = strchr(name, ':'); ++ if (name[0] == ':') { ++ char *split = strchr(name + 1, ':'); + if (!split) + goto noent; + *split = 0; -+ ns = __aa_find_namespace(name, &profile_ns_list); ++ ns = __aa_find_namespace(name + 1, &profile_ns_list); + name = split + 1; ++ } else { ++ ns = default_namespace; + } + + if (!ns) + goto noent; ++ sa.name2 = ns->name; + write_lock(&ns->lock); + profile = __aa_find_profile(name, &ns->profiles); + if (!profile) { + write_unlock(&ns->lock); + goto noent; + } ++ sa.name = profile->name; + + /* Remove the profile from each task context it is on. */ + lock_profile(profile); @@ -1103,6 +1158,8 @@ Signed-off-by: Andreas Gruenbacher + aa_put_namespace(ns); + } + write_unlock(&profile_ns_list_lock); ++ ++ aa_audit_status(NULL, &sa); + mutex_unlock(&aa_interface_lock); + aa_put_profile(profile); + @@ -1110,6 +1167,8 @@ Signed-off-by: Andreas Gruenbacher + +noent: + write_unlock(&profile_ns_list_lock); ++ sa.info = "failed: profile does not exist"; ++ aa_audit_status(NULL, &sa); + mutex_unlock(&aa_interface_lock); + return -ENOENT; +} diff --git a/kernel-patches/for-mainline/apparmor-network.diff b/kernel-patches/for-mainline/apparmor-network.diff index 1649b2fd3..72c1901e0 100644 --- a/kernel-patches/for-mainline/apparmor-network.diff +++ b/kernel-patches/for-mainline/apparmor-network.diff @@ -2,9 +2,9 @@ security/apparmor/Makefile | 7 + security/apparmor/apparmor.h | 9 ++ security/apparmor/lsm.c | 129 ++++++++++++++++++++++++++++++++++- - security/apparmor/main.c | 106 ++++++++++++++++++++++++++++ - security/apparmor/module_interface.c | 20 +++++ - 5 files changed, 267 insertions(+), 4 deletions(-) + security/apparmor/main.c | 107 ++++++++++++++++++++++++++++- + security/apparmor/module_interface.c | 26 ++++++- + 5 files changed, 271 insertions(+), 7 deletions(-) --- a/security/apparmor/Makefile +++ b/security/apparmor/Makefile @@ -32,7 +32,7 @@ /* * We use MAY_READ, MAY_WRITE, MAY_EXEC, MAY_APPEND and the following flags -@@ -199,6 +201,9 @@ struct aa_profile { +@@ -208,6 +210,9 @@ struct aa_profile { struct list_head task_contexts; spinlock_t lock; unsigned long int_flags; @@ -42,7 +42,7 @@ }; extern struct list_head profile_ns_list; -@@ -245,6 +250,7 @@ struct aa_audit { +@@ -254,6 +259,7 @@ struct aa_audit { int request_mask, denied_mask, audit_mask; struct iattr *iattr; pid_t task, parent; @@ -50,7 +50,7 @@ int error_code; }; -@@ -306,6 +312,9 @@ extern void aa_change_task_context(struc +@@ -315,6 +321,9 @@ extern void aa_change_task_context(struc struct aa_profile *previous_profile); extern int aa_may_ptrace(struct aa_task_context *cxt, struct aa_profile *tracee); @@ -70,7 +70,7 @@ #include "apparmor.h" #include "inline.h" -@@ -653,6 +654,117 @@ static void apparmor_task_free_security( +@@ -663,6 +664,117 @@ static void apparmor_task_free_security( aa_release(task); } @@ -188,7 +188,7 @@ static int apparmor_getprocattr(struct task_struct *task, char *name, char **value) { -@@ -753,9 +865,6 @@ struct security_operations apparmor_ops +@@ -763,9 +875,6 @@ struct security_operations apparmor_ops .capable = apparmor_capable, .syslog = cap_syslog, @@ -198,7 +198,7 @@ .bprm_apply_creds = cap_bprm_apply_creds, .bprm_set_security = apparmor_bprm_set_security, .bprm_secureexec = apparmor_bprm_secureexec, -@@ -791,6 +900,20 @@ struct security_operations apparmor_ops +@@ -801,6 +910,20 @@ struct security_operations apparmor_ops .getprocattr = apparmor_getprocattr, .setprocattr = apparmor_setprocattr, @@ -231,7 +231,7 @@ #include "apparmor.h" -@@ -133,6 +136,24 @@ static void aa_audit_file_mask(struct au +@@ -116,6 +119,24 @@ static void aa_audit_file_mask(struct au audit_log_format(ab, " %s=\"%s::%s\"", name, user, other); } @@ -256,10 +256,11 @@ /** * aa_audit - Log an audit event to the audit subsystem * @profile: profile to check against -@@ -204,6 +225,25 @@ static int aa_audit_base(struct aa_profi +@@ -187,7 +208,25 @@ static int aa_audit_base(struct aa_profi audit_log_untrustedstring(ab, sa->name2); } +- audit_log_format(ab, " pid=%d", current->pid); + if (sa->family || sa->type) { + if (address_families[sa->family]) + audit_log_format(ab, " family=\"%s\"", @@ -279,10 +280,10 @@ + } + + audit_log_format(ab, " pid=%d", current->pid); - audit_log_format(ab, " pid=%d", current->pid); if (profile) { -@@ -766,6 +806,72 @@ int aa_link(struct aa_profile *profile, + audit_log_format(ab, " profile="); +@@ -768,6 +807,72 @@ int aa_link(struct aa_profile *profile, return error; } @@ -357,16 +358,18 @@ *******************************/ --- a/security/apparmor/module_interface.c +++ b/security/apparmor/module_interface.c -@@ -283,6 +283,8 @@ static struct aa_profile *aa_unpack_prof +@@ -320,8 +320,8 @@ static struct aa_profile *aa_unpack_prof + struct aa_audit *sa) { struct aa_profile *profile = NULL; - struct aa_audit sa; +- +- int error = -EPROTO; + size_t size = 0; -+ int i; ++ int i, error = -EPROTO; - int error = -EPROTO; - -@@ -317,6 +319,24 @@ static struct aa_profile *aa_unpack_prof + profile = alloc_aa_profile(); + if (!profile) +@@ -354,6 +354,28 @@ static struct aa_profile *aa_unpack_prof if (!aa_is_u32(e, &(profile->set_caps), NULL)) goto fail; @@ -378,6 +381,10 @@ + for (i = 0; i < size; i++) { + if (!aa_is_u16(e, &profile->network_families[i], NULL)) + goto fail; ++ if (!aa_is_u16(e, &profile->audit_network[i], NULL)) ++ goto fail; ++ if (!aa_is_u16(e, &profile->quiet_network[i], NULL)) ++ goto fail; + } + if (!aa_is_nameX(e, AA_ARRAYEND, NULL)) + goto fail; diff --git a/kernel-patches/for-mainline/apparmor-rlimits.diff b/kernel-patches/for-mainline/apparmor-rlimits.diff index 60d54c8ce..5895479c4 100644 --- a/kernel-patches/for-mainline/apparmor-rlimits.diff +++ b/kernel-patches/for-mainline/apparmor-rlimits.diff @@ -1,3 +1,23 @@ +From: John Johansen +Subject: AppArmor: per profile controls for system rlimits + +Provide contol of rlimits on a per profile basis. Each profile provides +a per limit contol and corresponding hard limit value, such that when a +profile becomes attached to a task it sets the tasks limits to be <= to +the profiles specified limits. Note: the profile limit value will not +raise a tasks limit if it is already less than the profile mandates. + +In addition to setting a tasks limits, the ability to set limits on +a confined task are controlled. AppArmor only controls the raising +of a tasks limits Tasks with CAP_SYS_RESOURCE can have their hard limits +raised up to the value specified by the profile. AppArmor does not +prevent a task for lowering its hard limits, nor does it provide +additional control on soft limits. + +AppArmor only controls the limits specified in a profile so that +any limit not specified is free to be modified subject to standard +linux limitations. + --- security/apparmor/apparmor.h | 23 ++++++ security/apparmor/apparmorfs.c | 2 @@ -16,7 +36,7 @@ #include #include -@@ -129,6 +130,18 @@ extern unsigned int apparmor_path_max; +@@ -136,6 +137,18 @@ extern unsigned int apparmor_path_max; #define AA_ERROR(fmt, args...) printk(KERN_ERR "AppArmor: " fmt, ##args) @@ -35,7 +55,7 @@ struct aa_profile; /* struct aa_namespace - namespace for a set of profiles -@@ -163,6 +176,8 @@ struct aa_namespace { +@@ -170,6 +183,8 @@ struct aa_namespace { * @audit_caps: caps that are to be audited * @quiet_caps: caps that should not be audited * @capabilities: capabilities granted by the process @@ -44,7 +64,7 @@ * @count: reference count of the profile * @task_contexts: list of tasks confined by profile * @lock: lock for the task_contexts list -@@ -197,6 +212,9 @@ struct aa_profile { +@@ -206,6 +221,9 @@ struct aa_profile { kernel_cap_t audit_caps; kernel_cap_t quiet_caps; @@ -54,7 +74,7 @@ struct kref count; struct list_head task_contexts; spinlock_t lock; -@@ -248,6 +266,7 @@ struct aa_audit { +@@ -257,6 +275,7 @@ struct aa_audit { const char *name2; const char *name3; int request_mask, denied_mask, audit_mask; @@ -62,7 +82,7 @@ struct iattr *iattr; pid_t task, parent; int family, type, protocol; -@@ -315,6 +334,10 @@ extern int aa_may_ptrace(struct aa_task_ +@@ -324,6 +343,10 @@ extern int aa_may_ptrace(struct aa_task_ extern int aa_net_perm(struct aa_profile *profile, char *operation, int family, int type, int protocol); extern int aa_revalidate_sk(struct sock *sk, char *operation); @@ -86,7 +106,7 @@ strlen(features)); --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c -@@ -856,6 +856,21 @@ static int apparmor_setprocattr(struct t +@@ -866,6 +866,21 @@ static int apparmor_setprocattr(struct t return error; } @@ -108,7 +128,7 @@ struct security_operations apparmor_ops = { .ptrace = apparmor_ptrace, .capget = cap_capget, -@@ -897,6 +912,7 @@ struct security_operations apparmor_ops +@@ -907,6 +922,7 @@ struct security_operations apparmor_ops .task_free_security = apparmor_task_free_security, .task_post_setuid = cap_task_post_setuid, .task_reparent_to_init = cap_task_reparent_to_init, @@ -118,7 +138,7 @@ .setprocattr = apparmor_setprocattr, --- a/security/apparmor/main.c +++ b/security/apparmor/main.c -@@ -176,6 +176,9 @@ static int aa_audit_base(struct aa_profi +@@ -177,6 +177,9 @@ static int aa_audit_base(struct aa_profi if (sa->request_mask) audit_log_format(ab, " fsuid=%d", current->fsuid); @@ -128,7 +148,7 @@ if (sa->iattr) { struct iattr *iattr = sa->iattr; -@@ -871,6 +874,79 @@ int aa_revalidate_sk(struct sock *sk, ch +@@ -872,6 +875,79 @@ int aa_revalidate_sk(struct sock *sk, ch return error; } @@ -208,7 +228,7 @@ /******************************* * Global task related functions -@@ -884,6 +960,7 @@ int aa_revalidate_sk(struct sock *sk, ch +@@ -885,6 +961,7 @@ int aa_revalidate_sk(struct sock *sk, ch */ int aa_clone(struct task_struct *child) { @@ -216,7 +236,7 @@ struct aa_task_context *cxt, *child_cxt; struct aa_profile *profile; -@@ -893,6 +970,11 @@ int aa_clone(struct task_struct *child) +@@ -894,6 +971,11 @@ int aa_clone(struct task_struct *child) if (!child_cxt) return -ENOMEM; @@ -228,7 +248,7 @@ repeat: profile = aa_get_profile(current); if (profile) { -@@ -909,18 +991,22 @@ repeat: +@@ -910,18 +992,22 @@ repeat: goto repeat; } @@ -256,7 +276,7 @@ aa_audit_hint(profile, &sa); } aa_put_profile(profile); -@@ -1098,6 +1184,10 @@ repeat: +@@ -1156,6 +1242,10 @@ repeat: sa.task = current->parent->pid; aa_audit_reject(profile, &sa); } @@ -267,7 +287,7 @@ new_profile = old_profile; goto cleanup; } -@@ -1237,6 +1327,12 @@ static int do_change_profile(struct aa_p +@@ -1296,6 +1386,12 @@ static int do_change_profile(struct aa_p goto out; } @@ -280,7 +300,7 @@ if (new_profile == ns->null_complain_profile) aa_audit_hint(cxt->profile, sa); -@@ -1425,17 +1521,18 @@ struct aa_profile *__aa_replace_profile( +@@ -1482,17 +1578,18 @@ struct aa_profile *__aa_replace_profile( cxt = lock_task_and_profiles(task, profile); if (unlikely(profile && profile->isstale)) { @@ -307,7 +327,7 @@ } if (cxt) -@@ -1443,8 +1540,15 @@ struct aa_profile *__aa_replace_profile( +@@ -1500,8 +1597,15 @@ struct aa_profile *__aa_replace_profile( aa_change_task_context(task, new_cxt, profile, 0, NULL); task_unlock(task); @@ -323,7 +343,7 @@ } /** -@@ -1509,6 +1613,7 @@ void aa_change_task_context(struct task_ +@@ -1566,6 +1670,7 @@ void aa_change_task_context(struct task_ if (old_cxt) { list_del_init(&old_cxt->list); @@ -331,7 +351,7 @@ call_rcu(&old_cxt->rcu, free_aa_task_context_rcu_callback); } if (new_cxt) { -@@ -1520,6 +1625,7 @@ void aa_change_task_context(struct task_ +@@ -1577,6 +1682,7 @@ void aa_change_task_context(struct task_ new_cxt->cookie = cookie; new_cxt->task = task; new_cxt->profile = aa_dup_profile(profile); @@ -364,8 +384,8 @@ static size_t aa_is_array(struct aa_ext *e, const char *name) { void *pos = e->pos; -@@ -273,6 +289,39 @@ struct aa_dfa *aa_unpack_dfa(struct aa_e - return dfa; +@@ -311,6 +327,39 @@ fail: + return 0; } +int aa_unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) @@ -404,7 +424,7 @@ /** * aa_unpack_profile - unpack a serialized profile * @e: serialized data extent information -@@ -319,6 +368,9 @@ static struct aa_profile *aa_unpack_prof +@@ -354,6 +403,9 @@ static struct aa_profile *aa_unpack_prof if (!aa_is_u32(e, &(profile->set_caps), NULL)) goto fail; @@ -414,16 +434,16 @@ size = aa_is_array(e, "net_allowed_af"); if (size) { if (size > AF_MAX) -@@ -565,6 +617,8 @@ ssize_t aa_replace_profile(void *udata, - if (!old_profile) +@@ -613,6 +665,8 @@ ssize_t aa_replace_profile(void *udata, + sa.operation = "profile_load"; goto out; - + } + /* do not fail replacement based off of profile's NPROC rlimit */ + /* * Replacement needs to allocate a new aa_task_context for each * task confined by old_profile. To do this the profile locks -@@ -585,6 +639,7 @@ ssize_t aa_replace_profile(void *udata, +@@ -633,6 +687,7 @@ ssize_t aa_replace_profile(void *udata, task_lock(task); task_replace(task, new_cxt, new_profile); task_unlock(task); @@ -431,7 +451,7 @@ new_cxt = NULL; } unlock_both_profiles(old_profile, new_profile); -@@ -604,6 +659,7 @@ out: +@@ -655,6 +710,7 @@ out: * * remove a profile from the profile list and all aa_task_context references * to said profile. diff --git a/kernel-patches/for-mainline/audit-log-type-in-syslog.diff b/kernel-patches/for-mainline/audit-log-type-in-syslog.diff index 8f5b4e12e..518ca74c3 100644 --- a/kernel-patches/for-mainline/audit-log-type-in-syslog.diff +++ b/kernel-patches/for-mainline/audit-log-type-in-syslog.diff @@ -4,7 +4,7 @@ --- a/security/apparmor/main.c +++ b/security/apparmor/main.c -@@ -176,8 +176,10 @@ static int aa_audit_base(struct aa_profi +@@ -159,8 +159,10 @@ static int aa_audit_base(struct aa_profi return type == AUDIT_APPARMOR_ALLOWED ? 0 : -ENOMEM; } diff --git a/kernel-patches/for-mainline/series b/kernel-patches/for-mainline/series index d5459720e..9733382ab 100644 --- a/kernel-patches/for-mainline/series +++ b/kernel-patches/for-mainline/series @@ -110,6 +110,7 @@ apparmor-intree.diff #FS2.2.2_fix-unionfs-with-AppArmor.patch #FS2.1.3_fix-unionfs-with-AppArmor.patch +#named-transitions.diff apparmor-network.diff #fix-net.diff apparmor-rlimits.diff