From 71181796e39f68ffea858d10e3ac09925685aa35 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 29 Jul 2016 01:29:02 -0700 Subject: [PATCH] Add patches for 4.6 kernel Signed-off-by: John Johansen --- ...s-validate-buffer-size-in-apparmor_s.patch | 118 ++ ...-refcount-bug-in-profile-replacement.patch | 33 + ...lacement-bug-that-adds-new-child-to-.patch | 38 + ...r-fix-uninitialized-lsm_audit-member.patch | 87 ++ ...ould-not-be-returning-ENOENT-when-it.patch | 32 + ...ate-the-mtime-of-the-profile-file-on.patch | 28 + ...-disconnected-bind-mnts-reconnection.patch | 36 + ...l-paths-should-be-treated-as-disconn.patch | 114 ++ ...-parent-ref-after-updating-the-activ.patch | 29 + ...g-failures-for-all-profiles-in-a-set.patch | 87 ++ ...it-full-profile-hname-on-successful-.patch | 33 + ...the-target-profile-name-is-always-au.patch | 112 ++ ...that-xindex-is-in-trans_table-bounds.patch | 27 + ...-count-leak-when-profile-sha1-hash-i.patch | 27 + ...count-race-when-finding-a-child-prof.patch | 37 + ...t_next_entry-instead-of-list_entry_n.patch | 59 + ...YS_CAP_RESOURCE-to-be-sufficient-to-.patch | 50 + ...sing-id-bounds-check-on-dfa-verifica.patch | 40 + ...heck-for-vmalloc_addr-if-kvzalloc-fa.patch | 38 + ...s-in-profile_unpack-when-policy_db-i.patch | 33 + ...ule-parameters-can-be-changed-after-.patch | 162 +++ ...-apparmor-do-not-expose-kernel-stack.patch | 31 + ..._size-computation-for-when-setprocat.patch | 27 + ...AUCE-AppArmor-basic-networking-rules.patch | 603 ++++++++++ ...eting-of-audit-messages-for-network-.patch | 38 + ...armor-Add-the-ability-to-mediate-mou.patch | 1010 +++++++++++++++++ ...pArmor-fix-boolreturn.cocci-warnings.patch | 180 +++ 27 files changed, 3109 insertions(+) create mode 100644 kernel-patches/4.6/0001-apparmor-fix-oops-validate-buffer-size-in-apparmor_s.patch create mode 100644 kernel-patches/4.6/0002-apparmor-fix-refcount-bug-in-profile-replacement.patch create mode 100644 kernel-patches/4.6/0003-apparmor-fix-replacement-bug-that-adds-new-child-to-.patch create mode 100644 kernel-patches/4.6/0004-apparmor-fix-uninitialized-lsm_audit-member.patch create mode 100644 kernel-patches/4.6/0005-apparmor-exec-should-not-be-returning-ENOENT-when-it.patch create mode 100644 kernel-patches/4.6/0006-apparmor-fix-update-the-mtime-of-the-profile-file-on.patch create mode 100644 kernel-patches/4.6/0007-apparmor-fix-disconnected-bind-mnts-reconnection.patch create mode 100644 kernel-patches/4.6/0008-apparmor-internal-paths-should-be-treated-as-disconn.patch create mode 100644 kernel-patches/4.6/0009-apparmor-fix-put-parent-ref-after-updating-the-activ.patch create mode 100644 kernel-patches/4.6/0010-apparmor-fix-log-failures-for-all-profiles-in-a-set.patch create mode 100644 kernel-patches/4.6/0011-apparmor-fix-audit-full-profile-hname-on-successful-.patch create mode 100644 kernel-patches/4.6/0012-apparmor-ensure-the-target-profile-name-is-always-au.patch create mode 100644 kernel-patches/4.6/0013-apparmor-check-that-xindex-is-in-trans_table-bounds.patch create mode 100644 kernel-patches/4.6/0014-apparmor-fix-ref-count-leak-when-profile-sha1-hash-i.patch create mode 100644 kernel-patches/4.6/0015-apparmor-fix-refcount-race-when-finding-a-child-prof.patch create mode 100644 kernel-patches/4.6/0016-apparmor-use-list_next_entry-instead-of-list_entry_n.patch create mode 100644 kernel-patches/4.6/0017-apparmor-allow-SYS_CAP_RESOURCE-to-be-sufficient-to-.patch create mode 100644 kernel-patches/4.6/0018-apparmor-add-missing-id-bounds-check-on-dfa-verifica.patch create mode 100644 kernel-patches/4.6/0019-apparmor-don-t-check-for-vmalloc_addr-if-kvzalloc-fa.patch create mode 100644 kernel-patches/4.6/0020-apparmor-fix-oops-in-profile_unpack-when-policy_db-i.patch create mode 100644 kernel-patches/4.6/0021-apparmor-fix-module-parameters-can-be-changed-after-.patch create mode 100644 kernel-patches/4.6/0022-apparmor-do-not-expose-kernel-stack.patch create mode 100644 kernel-patches/4.6/0023-apparmor-fix-arg_size-computation-for-when-setprocat.patch create mode 100644 kernel-patches/4.6/0024-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch create mode 100644 kernel-patches/4.6/0025-apparmor-Fix-quieting-of-audit-messages-for-network-.patch create mode 100644 kernel-patches/4.6/0026-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch create mode 100644 kernel-patches/4.6/0027-UBUNTU-SAUCE-AppArmor-fix-boolreturn.cocci-warnings.patch diff --git a/kernel-patches/4.6/0001-apparmor-fix-oops-validate-buffer-size-in-apparmor_s.patch b/kernel-patches/4.6/0001-apparmor-fix-oops-validate-buffer-size-in-apparmor_s.patch new file mode 100644 index 000000000..f818189e1 --- /dev/null +++ b/kernel-patches/4.6/0001-apparmor-fix-oops-validate-buffer-size-in-apparmor_s.patch @@ -0,0 +1,118 @@ +From 24b6ac149a57c2d3d5a9920e64d914e8ff00d346 Mon Sep 17 00:00:00 2001 +From: Vegard Nossum +Date: Thu, 7 Jul 2016 13:41:11 -0700 +Subject: [PATCH 01/27] apparmor: fix oops, validate buffer size in + apparmor_setprocattr() + +When proc_pid_attr_write() was changed to use memdup_user apparmor's +(interface violating) assumption that the setprocattr buffer was always +a single page was violated. + +The size test is not strictly speaking needed as proc_pid_attr_write() +will reject anything larger, but for the sake of robustness we can keep +it in. + +SMACK and SELinux look safe to me, but somebody else should probably +have a look just in case. + +Based on original patch from Vegard Nossum +modified for the case that apparmor provides null termination. + +Fixes: bb646cdb12e75d82258c2f2e7746d5952d3e321a +Reported-by: Vegard Nossum +Cc: Al Viro +Cc: John Johansen +Cc: Paul Moore +Cc: Stephen Smalley +Cc: Eric Paris +Cc: Casey Schaufler +Cc: stable@kernel.org +Signed-off-by: John Johansen +Reviewed-by: Tyler Hicks +Signed-off-by: James Morris +--- + security/apparmor/lsm.c | 36 +++++++++++++++++++----------------- + 1 file changed, 19 insertions(+), 17 deletions(-) + +diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c +index dec607c..5ee8201 100644 +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -523,34 +523,34 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, + { + struct common_audit_data sa; + struct apparmor_audit_data aad = {0,}; +- char *command, *args = value; ++ char *command, *largs = NULL, *args = value; + size_t arg_size; + int error; + + if (size == 0) + return -EINVAL; +- /* args points to a PAGE_SIZE buffer, AppArmor requires that +- * the buffer must be null terminated or have size <= PAGE_SIZE -1 +- * so that AppArmor can null terminate them +- */ +- if (args[size - 1] != '\0') { +- if (size == PAGE_SIZE) +- return -EINVAL; +- args[size] = '\0'; +- } +- + /* task can only write its own attributes */ + if (current != task) + return -EACCES; + +- args = value; ++ /* AppArmor requires that the buffer must be null terminated atm */ ++ if (args[size - 1] != '\0') { ++ /* null terminate */ ++ largs = args = kmalloc(size + 1, GFP_KERNEL); ++ if (!args) ++ return -ENOMEM; ++ memcpy(args, value, size); ++ args[size] = '\0'; ++ } ++ ++ error = -EINVAL; + args = strim(args); + command = strsep(&args, " "); + if (!args) +- return -EINVAL; ++ goto out; + args = skip_spaces(args); + if (!*args) +- return -EINVAL; ++ goto out; + + arg_size = size - (args - (char *) value); + if (strcmp(name, "current") == 0) { +@@ -576,10 +576,12 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, + goto fail; + } else + /* only support the "current" and "exec" process attributes */ +- return -EINVAL; ++ goto fail; + + if (!error) + error = size; ++out: ++ kfree(largs); + return error; + + fail: +@@ -588,9 +590,9 @@ fail: + aad.profile = aa_current_profile(); + aad.op = OP_SETPROCATTR; + aad.info = name; +- aad.error = -EINVAL; ++ aad.error = error = -EINVAL; + aa_audit_msg(AUDIT_APPARMOR_DENIED, &sa, NULL); +- return -EINVAL; ++ goto out; + } + + static int apparmor_task_setrlimit(struct task_struct *task, +-- +2.7.4 + diff --git a/kernel-patches/4.6/0002-apparmor-fix-refcount-bug-in-profile-replacement.patch b/kernel-patches/4.6/0002-apparmor-fix-refcount-bug-in-profile-replacement.patch new file mode 100644 index 000000000..82b1906c9 --- /dev/null +++ b/kernel-patches/4.6/0002-apparmor-fix-refcount-bug-in-profile-replacement.patch @@ -0,0 +1,33 @@ +From 444bc4f95ec283cd0fb9777f4890bd9bc307809d Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Mon, 11 Apr 2016 16:55:10 -0700 +Subject: [PATCH 02/27] apparmor: fix refcount bug in profile replacement + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +--- + security/apparmor/policy.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c +index 705c287..222052f 100644 +--- a/security/apparmor/policy.c ++++ b/security/apparmor/policy.c +@@ -1189,12 +1189,12 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) + aa_get_profile(newest); + aa_put_profile(parent); + rcu_assign_pointer(ent->new->parent, newest); +- } else +- aa_put_profile(newest); ++ } + /* aafs interface uses replacedby */ + rcu_assign_pointer(ent->new->replacedby->profile, + aa_get_profile(ent->new)); + __list_add_profile(&parent->base.profiles, ent->new); ++ aa_put_profile(newest); + } else { + /* aafs interface uses replacedby */ + rcu_assign_pointer(ent->new->replacedby->profile, +-- +2.7.4 + diff --git a/kernel-patches/4.6/0003-apparmor-fix-replacement-bug-that-adds-new-child-to-.patch b/kernel-patches/4.6/0003-apparmor-fix-replacement-bug-that-adds-new-child-to-.patch new file mode 100644 index 000000000..9dcf618d3 --- /dev/null +++ b/kernel-patches/4.6/0003-apparmor-fix-replacement-bug-that-adds-new-child-to-.patch @@ -0,0 +1,38 @@ +From 1224a06778b89dcbf0ca85bd961c2fcdd8765a69 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Mon, 11 Apr 2016 16:57:19 -0700 +Subject: [PATCH 03/27] apparmor: fix replacement bug that adds new child to + old parent + +When set atomic replacement is used and the parent is updated before the +child, and the child did not exist in the old parent so there is no +direct replacement then the new child is incorrectly added to the old +parent. This results in the new parent not having the child(ren) that +it should and the old parent when being destroyed asserting the +following error. + +AppArmor: policy_destroy: internal error, policy '' still +contains profiles + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +--- + security/apparmor/policy.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c +index 222052f..c92a9f6 100644 +--- a/security/apparmor/policy.c ++++ b/security/apparmor/policy.c +@@ -1193,7 +1193,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) + /* aafs interface uses replacedby */ + rcu_assign_pointer(ent->new->replacedby->profile, + aa_get_profile(ent->new)); +- __list_add_profile(&parent->base.profiles, ent->new); ++ __list_add_profile(&newest->base.profiles, ent->new); + aa_put_profile(newest); + } else { + /* aafs interface uses replacedby */ +-- +2.7.4 + diff --git a/kernel-patches/4.6/0004-apparmor-fix-uninitialized-lsm_audit-member.patch b/kernel-patches/4.6/0004-apparmor-fix-uninitialized-lsm_audit-member.patch new file mode 100644 index 000000000..8ec263c8c --- /dev/null +++ b/kernel-patches/4.6/0004-apparmor-fix-uninitialized-lsm_audit-member.patch @@ -0,0 +1,87 @@ +From 15d921647676fdc2c3ee1cf9aa8f578b1012ecff Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Sun, 8 Jun 2014 11:20:54 -0700 +Subject: [PATCH 04/27] apparmor: fix uninitialized lsm_audit member + +BugLink: http://bugs.launchpad.net/bugs/1268727 + +The task field in the lsm_audit struct needs to be initialized if +a change_hat fails, otherwise the following oops will occur + +BUG: unable to handle kernel paging request at 0000002fbead7d08 +IP: [] _raw_spin_lock+0xe/0x50 +PGD 1e3f35067 PUD 0 +Oops: 0002 [#1] SMP +Modules linked in: pppox crc_ccitt p8023 p8022 psnap llc ax25 btrfs raid6_pq xor xfs libcrc32c dm_multipath scsi_dh kvm_amd dcdbas kvm microcode amd64_edac_mod joydev edac_core psmouse edac_mce_amd serio_raw k10temp sp5100_tco i2c_piix4 ipmi_si ipmi_msghandler acpi_power_meter mac_hid lp parport hid_generic usbhid hid pata_acpi mpt2sas ahci raid_class pata_atiixp bnx2 libahci scsi_transport_sas [last unloaded: tipc] +CPU: 2 PID: 699 Comm: changehat_twice Tainted: GF O 3.13.0-7-generic #25-Ubuntu +Hardware name: Dell Inc. PowerEdge R415/08WNM9, BIOS 1.8.6 12/06/2011 +task: ffff8802135c6000 ti: ffff880212986000 task.ti: ffff880212986000 +RIP: 0010:[] [] _raw_spin_lock+0xe/0x50 +RSP: 0018:ffff880212987b68 EFLAGS: 00010006 +RAX: 0000000000020000 RBX: 0000002fbead7500 RCX: 0000000000000000 +RDX: 0000000000000292 RSI: ffff880212987ba8 RDI: 0000002fbead7d08 +RBP: ffff880212987b68 R08: 0000000000000246 R09: ffff880216e572a0 +R10: ffffffff815fd677 R11: ffffea0008469580 R12: ffffffff8130966f +R13: ffff880212987ba8 R14: 0000002fbead7d08 R15: ffff8800d8c6b830 +FS: 00002b5e6c84e7c0(0000) GS:ffff880216e40000(0000) knlGS:0000000055731700 +CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 +CR2: 0000002fbead7d08 CR3: 000000021270f000 CR4: 00000000000006e0 +Stack: + ffff880212987b98 ffffffff81075f17 ffffffff8130966f 0000000000000009 + 0000000000000000 0000000000000000 ffff880212987bd0 ffffffff81075f7c + 0000000000000292 ffff880212987c08 ffff8800d8c6b800 0000000000000026 +Call Trace: + [] __lock_task_sighand+0x47/0x80 + [] ? apparmor_cred_prepare+0x2f/0x50 + [] do_send_sig_info+0x2c/0x80 + [] send_sig_info+0x1e/0x30 + [] aa_audit+0x13d/0x190 + [] aa_audit_file+0xbc/0x130 + [] ? apparmor_cred_prepare+0x2f/0x50 + [] aa_change_hat+0x202/0x530 + [] aa_setprocattr_changehat+0x116/0x1d0 + [] apparmor_setprocattr+0x25d/0x300 + [] security_setprocattr+0x16/0x20 + [] proc_pid_attr_write+0x107/0x130 + [] vfs_write+0xb4/0x1f0 + [] SyS_write+0x49/0xa0 + [] tracesys+0xe1/0xe6 + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +--- + security/apparmor/audit.c | 3 ++- + security/apparmor/file.c | 3 ++- + 2 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c +index 89c7865..3a7f1da 100644 +--- a/security/apparmor/audit.c ++++ b/security/apparmor/audit.c +@@ -200,7 +200,8 @@ int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, + + if (sa->aad->type == AUDIT_APPARMOR_KILL) + (void)send_sig_info(SIGKILL, NULL, +- sa->u.tsk ? sa->u.tsk : current); ++ sa->type == LSM_AUDIT_DATA_TASK && sa->u.tsk ? ++ sa->u.tsk : current); + + if (sa->aad->type == AUDIT_APPARMOR_ALLOWED) + return complain_error(sa->aad->error); +diff --git a/security/apparmor/file.c b/security/apparmor/file.c +index 913f377..43d6ae7 100644 +--- a/security/apparmor/file.c ++++ b/security/apparmor/file.c +@@ -110,7 +110,8 @@ int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, + int type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + struct apparmor_audit_data aad = {0,}; +- sa.type = LSM_AUDIT_DATA_NONE; ++ sa.type = LSM_AUDIT_DATA_TASK; ++ sa.u.tsk = NULL; + sa.aad = &aad; + aad.op = op, + aad.fs.request = request; +-- +2.7.4 + diff --git a/kernel-patches/4.6/0005-apparmor-exec-should-not-be-returning-ENOENT-when-it.patch b/kernel-patches/4.6/0005-apparmor-exec-should-not-be-returning-ENOENT-when-it.patch new file mode 100644 index 000000000..9c79b4168 --- /dev/null +++ b/kernel-patches/4.6/0005-apparmor-exec-should-not-be-returning-ENOENT-when-it.patch @@ -0,0 +1,32 @@ +From c1216728b7d644443eef31e4bd9d01b4a0a51d61 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Fri, 25 Jul 2014 04:02:03 -0700 +Subject: [PATCH 05/27] apparmor: exec should not be returning ENOENT when it + denies + +The current behavior is confusing as it causes exec failures to report +the executable is missing instead of identifying that apparmor +caused the failure. + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +--- + security/apparmor/domain.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c +index dc0027b..67a7418 100644 +--- a/security/apparmor/domain.c ++++ b/security/apparmor/domain.c +@@ -433,7 +433,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) + new_profile = aa_get_newest_profile(ns->unconfined); + info = "ux fallback"; + } else { +- error = -ENOENT; ++ error = -EACCES; + info = "profile not found"; + /* remove MAY_EXEC to audit as failure */ + perms.allow &= ~MAY_EXEC; +-- +2.7.4 + diff --git a/kernel-patches/4.6/0006-apparmor-fix-update-the-mtime-of-the-profile-file-on.patch b/kernel-patches/4.6/0006-apparmor-fix-update-the-mtime-of-the-profile-file-on.patch new file mode 100644 index 000000000..b55a59618 --- /dev/null +++ b/kernel-patches/4.6/0006-apparmor-fix-update-the-mtime-of-the-profile-file-on.patch @@ -0,0 +1,28 @@ +From 2d3389de6c8ab6b3ad2cef4ea460c8fce2a226b9 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Fri, 25 Jul 2014 04:01:56 -0700 +Subject: [PATCH 06/27] apparmor: fix update the mtime of the profile file on + replacement + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +--- + security/apparmor/apparmorfs.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c +index ad4fa49..45a6199 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -379,6 +379,8 @@ void __aa_fs_profile_migrate_dents(struct aa_profile *old, + + for (i = 0; i < AAFS_PROF_SIZEOF; i++) { + new->dents[i] = old->dents[i]; ++ if (new->dents[i]) ++ new->dents[i]->d_inode->i_mtime = CURRENT_TIME; + old->dents[i] = NULL; + } + } +-- +2.7.4 + diff --git a/kernel-patches/4.6/0007-apparmor-fix-disconnected-bind-mnts-reconnection.patch b/kernel-patches/4.6/0007-apparmor-fix-disconnected-bind-mnts-reconnection.patch new file mode 100644 index 000000000..b7638aead --- /dev/null +++ b/kernel-patches/4.6/0007-apparmor-fix-disconnected-bind-mnts-reconnection.patch @@ -0,0 +1,36 @@ +From 9caa96e30a1b2bb191a29af872285c8d0b078c10 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Fri, 25 Jul 2014 04:02:08 -0700 +Subject: [PATCH 07/27] apparmor: fix disconnected bind mnts reconnection + +Bind mounts can fail to be properly reconnected when PATH_CONNECT is +specified. Ensure that when PATH_CONNECT is specified the path has +a root. + +BugLink: http://bugs.launchpad.net/bugs/1319984 + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +--- + security/apparmor/path.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/security/apparmor/path.c b/security/apparmor/path.c +index 71e0e3a..bb2f2c6 100644 +--- a/security/apparmor/path.c ++++ b/security/apparmor/path.c +@@ -141,7 +141,10 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, + error = -EACCES; + if (*res == '/') + *name = res + 1; +- } ++ } else if (*res != '/') ++ /* CONNECT_PATH with missing root */ ++ error = prepend(name, *name - buf, "/", 1); ++ + } + + out: +-- +2.7.4 + diff --git a/kernel-patches/4.6/0008-apparmor-internal-paths-should-be-treated-as-disconn.patch b/kernel-patches/4.6/0008-apparmor-internal-paths-should-be-treated-as-disconn.patch new file mode 100644 index 000000000..01a0eca74 --- /dev/null +++ b/kernel-patches/4.6/0008-apparmor-internal-paths-should-be-treated-as-disconn.patch @@ -0,0 +1,114 @@ +From 11702a732e149380e05e2ab8ae1b743ac89f892f Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Fri, 25 Jul 2014 04:02:10 -0700 +Subject: [PATCH 08/27] apparmor: internal paths should be treated as + disconnected + +Internal mounts are not mounted anywhere and as such should be treated +as disconnected paths. + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +--- + security/apparmor/path.c | 64 +++++++++++++++++++++++++++--------------------- + 1 file changed, 36 insertions(+), 28 deletions(-) + +diff --git a/security/apparmor/path.c b/security/apparmor/path.c +index bb2f2c6..596f799 100644 +--- a/security/apparmor/path.c ++++ b/security/apparmor/path.c +@@ -25,7 +25,6 @@ + #include "include/path.h" + #include "include/policy.h" + +- + /* modified from dcache.c */ + static int prepend(char **buffer, int buflen, const char *str, int namelen) + { +@@ -39,6 +38,38 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen) + + #define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT) + ++/* If the path is not connected to the expected root, ++ * check if it is a sysctl and handle specially else remove any ++ * leading / that __d_path may have returned. ++ * Unless ++ * specifically directed to connect the path, ++ * OR ++ * if in a chroot and doing chroot relative paths and the path ++ * resolves to the namespace root (would be connected outside ++ * of chroot) and specifically directed to connect paths to ++ * namespace root. ++ */ ++static int disconnect(const struct path *path, char *buf, char **name, ++ int flags) ++{ ++ int error = 0; ++ ++ if (!(flags & PATH_CONNECT_PATH) && ++ !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && ++ our_mnt(path->mnt))) { ++ /* disconnected path, don't return pathname starting ++ * with '/' ++ */ ++ error = -EACCES; ++ if (**name == '/') ++ *name = *name + 1; ++ } else if (**name != '/') ++ /* CONNECT_PATH with missing root */ ++ error = prepend(name, *name - buf, "/", 1); ++ ++ return error; ++} ++ + /** + * d_namespace_path - lookup a name associated with a given path + * @path: path to lookup (NOT NULL) +@@ -74,7 +105,8 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, + * control instead of hard coded /proc + */ + return prepend(name, *name - buf, "/proc", 5); +- } ++ } else ++ return disconnect(path, buf, name, flags); + return 0; + } + +@@ -120,32 +152,8 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, + goto out; + } + +- /* If the path is not connected to the expected root, +- * check if it is a sysctl and handle specially else remove any +- * leading / that __d_path may have returned. +- * Unless +- * specifically directed to connect the path, +- * OR +- * if in a chroot and doing chroot relative paths and the path +- * resolves to the namespace root (would be connected outside +- * of chroot) and specifically directed to connect paths to +- * namespace root. +- */ +- if (!connected) { +- if (!(flags & PATH_CONNECT_PATH) && +- !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && +- our_mnt(path->mnt))) { +- /* disconnected path, don't return pathname starting +- * with '/' +- */ +- error = -EACCES; +- if (*res == '/') +- *name = res + 1; +- } else if (*res != '/') +- /* CONNECT_PATH with missing root */ +- error = prepend(name, *name - buf, "/", 1); +- +- } ++ if (!connected) ++ error = disconnect(path, buf, name, flags); + + out: + return error; +-- +2.7.4 + diff --git a/kernel-patches/4.6/0009-apparmor-fix-put-parent-ref-after-updating-the-activ.patch b/kernel-patches/4.6/0009-apparmor-fix-put-parent-ref-after-updating-the-activ.patch new file mode 100644 index 000000000..4bd1245de --- /dev/null +++ b/kernel-patches/4.6/0009-apparmor-fix-put-parent-ref-after-updating-the-activ.patch @@ -0,0 +1,29 @@ +From c70811d9e6234c96d0ef405cd8ad78b70efb8637 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Sat, 16 Apr 2016 13:59:02 -0700 +Subject: [PATCH 09/27] apparmor: fix put() parent ref after updating the + active ref + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +--- + security/apparmor/policy.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c +index c92a9f6..455c9f8 100644 +--- a/security/apparmor/policy.c ++++ b/security/apparmor/policy.c +@@ -1187,8 +1187,8 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) + /* parent replaced in this atomic set? */ + if (newest != parent) { + aa_get_profile(newest); +- aa_put_profile(parent); + rcu_assign_pointer(ent->new->parent, newest); ++ aa_put_profile(parent); + } + /* aafs interface uses replacedby */ + rcu_assign_pointer(ent->new->replacedby->profile, +-- +2.7.4 + diff --git a/kernel-patches/4.6/0010-apparmor-fix-log-failures-for-all-profiles-in-a-set.patch b/kernel-patches/4.6/0010-apparmor-fix-log-failures-for-all-profiles-in-a-set.patch new file mode 100644 index 000000000..6139b3150 --- /dev/null +++ b/kernel-patches/4.6/0010-apparmor-fix-log-failures-for-all-profiles-in-a-set.patch @@ -0,0 +1,87 @@ +From f671b902943f83f0fbc8c8b7bf8bbfb817d124f1 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Sat, 16 Apr 2016 14:16:50 -0700 +Subject: [PATCH 10/27] apparmor: fix log failures for all profiles in a set + +currently only the profile that is causing the failure is logged. This +makes it more confusing than necessary about which profiles loaded +and which didn't. So make sure to log success and failure messages for +all profiles in the set being loaded. + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +--- + security/apparmor/policy.c | 29 +++++++++++++++++++---------- + 1 file changed, 19 insertions(+), 10 deletions(-) + +diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c +index 455c9f8..db31bc5 100644 +--- a/security/apparmor/policy.c ++++ b/security/apparmor/policy.c +@@ -1067,7 +1067,7 @@ static int __lookup_replace(struct aa_namespace *ns, const char *hname, + */ + ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) + { +- const char *ns_name, *name = NULL, *info = NULL; ++ const char *ns_name, *info = NULL; + struct aa_namespace *ns = NULL; + struct aa_load_ent *ent, *tmp; + int op = OP_PROF_REPL; +@@ -1082,18 +1082,15 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) + /* released below */ + ns = aa_prepare_namespace(ns_name); + if (!ns) { +- info = "failed to prepare namespace"; +- error = -ENOMEM; +- name = ns_name; +- goto fail; ++ error = audit_policy(op, GFP_KERNEL, ns_name, ++ "failed to prepare namespace", -ENOMEM); ++ goto free; + } + + mutex_lock(&ns->lock); + /* setup parent and ns info */ + list_for_each_entry(ent, &lh, list) { + struct aa_policy *policy; +- +- name = ent->new->base.hname; + error = __lookup_replace(ns, ent->new->base.hname, noreplace, + &ent->old, &info); + if (error) +@@ -1121,7 +1118,6 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) + if (!p) { + error = -ENOENT; + info = "parent does not exist"; +- name = ent->new->base.hname; + goto fail_lock; + } + rcu_assign_pointer(ent->new->parent, aa_get_profile(p)); +@@ -1214,9 +1210,22 @@ out: + + fail_lock: + mutex_unlock(&ns->lock); +-fail: +- error = audit_policy(op, GFP_KERNEL, name, info, error); + ++ /* audit cause of failure */ ++ op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; ++ audit_policy(op, GFP_KERNEL, ent->new->base.hname, info, error); ++ /* audit status that rest of profiles in the atomic set failed too */ ++ info = "valid profile in failed atomic policy load"; ++ list_for_each_entry(tmp, &lh, list) { ++ if (tmp == ent) { ++ info = "unchecked profile in failed atomic policy load"; ++ /* skip entry that caused failure */ ++ continue; ++ } ++ op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; ++ audit_policy(op, GFP_KERNEL, tmp->new->base.hname, info, error); ++ } ++free: + list_for_each_entry_safe(ent, tmp, &lh, list) { + list_del_init(&ent->list); + aa_load_ent_free(ent); +-- +2.7.4 + diff --git a/kernel-patches/4.6/0011-apparmor-fix-audit-full-profile-hname-on-successful-.patch b/kernel-patches/4.6/0011-apparmor-fix-audit-full-profile-hname-on-successful-.patch new file mode 100644 index 000000000..096b02965 --- /dev/null +++ b/kernel-patches/4.6/0011-apparmor-fix-audit-full-profile-hname-on-successful-.patch @@ -0,0 +1,33 @@ +From bc3c7d342bf53afdfdf46bc92dac5c624c89fb91 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Sat, 16 Apr 2016 14:19:38 -0700 +Subject: [PATCH 11/27] apparmor: fix audit full profile hname on successful + load + +Currently logging of a successful profile load only logs the basename +of the profile. This can result in confusion when a child profile has +the same name as the another profile in the set. Logging the hname +will ensure there is no confusion. + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +--- + security/apparmor/policy.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c +index db31bc5..ca402d0 100644 +--- a/security/apparmor/policy.c ++++ b/security/apparmor/policy.c +@@ -1159,7 +1159,7 @@ ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) + list_del_init(&ent->list); + op = (!ent->old && !ent->rename) ? OP_PROF_LOAD : OP_PROF_REPL; + +- audit_policy(op, GFP_ATOMIC, ent->new->base.name, NULL, error); ++ audit_policy(op, GFP_ATOMIC, ent->new->base.hname, NULL, error); + + if (ent->old) { + __replace_profile(ent->old, ent->new, 1); +-- +2.7.4 + diff --git a/kernel-patches/4.6/0012-apparmor-ensure-the-target-profile-name-is-always-au.patch b/kernel-patches/4.6/0012-apparmor-ensure-the-target-profile-name-is-always-au.patch new file mode 100644 index 000000000..1997ad543 --- /dev/null +++ b/kernel-patches/4.6/0012-apparmor-ensure-the-target-profile-name-is-always-au.patch @@ -0,0 +1,112 @@ +From 848da0479e5b9da3dc2ae4c64e0cca77a0abf02a Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Wed, 20 Apr 2016 14:18:18 -0700 +Subject: [PATCH 12/27] apparmor: ensure the target profile name is always + audited + +The target profile name was not being correctly audited in a few +cases because the target variable was not being set and gotos +passed the code to set it at apply: + +Since it is always based on new_profile just drop the target var +and conditionally report based on new_profile. + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +--- + security/apparmor/domain.c | 20 +++++++++----------- + 1 file changed, 9 insertions(+), 11 deletions(-) + +diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c +index 67a7418..fc3036b 100644 +--- a/security/apparmor/domain.c ++++ b/security/apparmor/domain.c +@@ -346,7 +346,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) + file_inode(bprm->file)->i_uid, + file_inode(bprm->file)->i_mode + }; +- const char *name = NULL, *target = NULL, *info = NULL; ++ const char *name = NULL, *info = NULL; + int error = 0; + + if (bprm->cred_prepared) +@@ -399,6 +399,7 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) + if (cxt->onexec) { + struct file_perms cp; + info = "change_profile onexec"; ++ new_profile = aa_get_newest_profile(cxt->onexec); + if (!(perms.allow & AA_MAY_ONEXEC)) + goto audit; + +@@ -413,7 +414,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) + + if (!(cp.allow & AA_MAY_ONEXEC)) + goto audit; +- new_profile = aa_get_newest_profile(cxt->onexec); + goto apply; + } + +@@ -445,10 +445,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) + if (!new_profile) { + error = -ENOMEM; + info = "could not create null profile"; +- } else { ++ } else + error = -EACCES; +- target = new_profile->base.hname; +- } + perms.xindex |= AA_X_UNSAFE; + } else + /* fail exec */ +@@ -459,7 +457,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) + * fail the exec. + */ + if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) { +- aa_put_profile(new_profile); + error = -EPERM; + goto cleanup; + } +@@ -474,10 +471,8 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) + + if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { + error = may_change_ptraced_domain(new_profile); +- if (error) { +- aa_put_profile(new_profile); ++ if (error) + goto audit; +- } + } + + /* Determine if secure exec is needed. +@@ -498,7 +493,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm) + bprm->unsafe |= AA_SECURE_X_NEEDED; + } + apply: +- target = new_profile->base.hname; + /* when transitioning profiles clear unsafe personality bits */ + bprm->per_clear |= PER_CLEAR_ON_SETID; + +@@ -506,15 +500,19 @@ x_clear: + aa_put_profile(cxt->profile); + /* transfer new profile reference will be released when cxt is freed */ + cxt->profile = new_profile; ++ new_profile = NULL; + + /* clear out all temporary/transitional state from the context */ + aa_clear_task_cxt_trans(cxt); + + audit: + error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, +- name, target, cond.uid, info, error); ++ name, ++ new_profile ? new_profile->base.hname : NULL, ++ cond.uid, info, error); + + cleanup: ++ aa_put_profile(new_profile); + aa_put_profile(profile); + kfree(buffer); + +-- +2.7.4 + diff --git a/kernel-patches/4.6/0013-apparmor-check-that-xindex-is-in-trans_table-bounds.patch b/kernel-patches/4.6/0013-apparmor-check-that-xindex-is-in-trans_table-bounds.patch new file mode 100644 index 000000000..476204570 --- /dev/null +++ b/kernel-patches/4.6/0013-apparmor-check-that-xindex-is-in-trans_table-bounds.patch @@ -0,0 +1,27 @@ +From 706473f3ead5cdffe5ad159adfbc090e0fda81d6 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Thu, 17 Mar 2016 12:02:54 -0700 +Subject: [PATCH 13/27] apparmor: check that xindex is in trans_table bounds + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +--- + security/apparmor/policy_unpack.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c +index a689f10..c841b12 100644 +--- a/security/apparmor/policy_unpack.c ++++ b/security/apparmor/policy_unpack.c +@@ -676,7 +676,7 @@ static bool verify_xindex(int xindex, int table_size) + int index, xtype; + xtype = xindex & AA_X_TYPE_MASK; + index = xindex & AA_X_INDEX_MASK; +- if (xtype == AA_X_TABLE && index > table_size) ++ if (xtype == AA_X_TABLE && index >= table_size) + return 0; + return 1; + } +-- +2.7.4 + diff --git a/kernel-patches/4.6/0014-apparmor-fix-ref-count-leak-when-profile-sha1-hash-i.patch b/kernel-patches/4.6/0014-apparmor-fix-ref-count-leak-when-profile-sha1-hash-i.patch new file mode 100644 index 000000000..d98cc68e9 --- /dev/null +++ b/kernel-patches/4.6/0014-apparmor-fix-ref-count-leak-when-profile-sha1-hash-i.patch @@ -0,0 +1,27 @@ +From 05a64c434466029b298ee1e78a988cd6a7f80c0e Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Wed, 18 Nov 2015 11:41:05 -0800 +Subject: [PATCH 14/27] apparmor: fix ref count leak when profile sha1 hash is + read + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +--- + security/apparmor/apparmorfs.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c +index 45a6199..0d8dd71 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -331,6 +331,7 @@ static int aa_fs_seq_hash_show(struct seq_file *seq, void *v) + seq_printf(seq, "%.2x", profile->hash[i]); + seq_puts(seq, "\n"); + } ++ aa_put_profile(profile); + + return 0; + } +-- +2.7.4 + diff --git a/kernel-patches/4.6/0015-apparmor-fix-refcount-race-when-finding-a-child-prof.patch b/kernel-patches/4.6/0015-apparmor-fix-refcount-race-when-finding-a-child-prof.patch new file mode 100644 index 000000000..fab201db9 --- /dev/null +++ b/kernel-patches/4.6/0015-apparmor-fix-refcount-race-when-finding-a-child-prof.patch @@ -0,0 +1,37 @@ +From 6b0b8b91f454bd021e27abe0e611a6764e4806c1 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Wed, 16 Dec 2015 18:09:10 -0800 +Subject: [PATCH 15/27] apparmor: fix refcount race when finding a child + profile + +When finding a child profile via an rcu critical section, the profile +may be put and scheduled for deletion after the child is found but +before its refcount is incremented. + +Protect against this by repeating the lookup if the profiles refcount +is 0 and is one its way to deletion. + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +--- + security/apparmor/policy.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c +index ca402d0..7807125 100644 +--- a/security/apparmor/policy.c ++++ b/security/apparmor/policy.c +@@ -766,7 +766,9 @@ struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) + struct aa_profile *profile; + + rcu_read_lock(); +- profile = aa_get_profile(__find_child(&parent->base.profiles, name)); ++ do { ++ profile = __find_child(&parent->base.profiles, name); ++ } while (profile && !aa_get_profile_not0(profile)); + rcu_read_unlock(); + + /* refcount released by caller */ +-- +2.7.4 + diff --git a/kernel-patches/4.6/0016-apparmor-use-list_next_entry-instead-of-list_entry_n.patch b/kernel-patches/4.6/0016-apparmor-use-list_next_entry-instead-of-list_entry_n.patch new file mode 100644 index 000000000..97c5b675f --- /dev/null +++ b/kernel-patches/4.6/0016-apparmor-use-list_next_entry-instead-of-list_entry_n.patch @@ -0,0 +1,59 @@ +From 84acc6aa6976e62756e14d3a00c5634724cbaa59 Mon Sep 17 00:00:00 2001 +From: Geliang Tang +Date: Mon, 16 Nov 2015 21:46:33 +0800 +Subject: [PATCH 16/27] apparmor: use list_next_entry instead of + list_entry_next + +list_next_entry has been defined in list.h, so I replace list_entry_next +with it. + +Signed-off-by: Geliang Tang +Acked-by: Serge Hallyn +Signed-off-by: John Johansen +--- + security/apparmor/apparmorfs.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c +index 0d8dd71..729e595 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -553,8 +553,6 @@ fail2: + } + + +-#define list_entry_next(pos, member) \ +- list_entry(pos->member.next, typeof(*pos), member) + #define list_entry_is_head(pos, head, member) (&pos->member == (head)) + + /** +@@ -585,7 +583,7 @@ static struct aa_namespace *__next_namespace(struct aa_namespace *root, + parent = ns->parent; + while (ns != root) { + mutex_unlock(&ns->lock); +- next = list_entry_next(ns, base.list); ++ next = list_next_entry(ns, base.list); + if (!list_entry_is_head(next, &parent->sub_ns, base.list)) { + mutex_lock(&next->lock); + return next; +@@ -639,7 +637,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p) + parent = rcu_dereference_protected(p->parent, + mutex_is_locked(&p->ns->lock)); + while (parent) { +- p = list_entry_next(p, base.list); ++ p = list_next_entry(p, base.list); + if (!list_entry_is_head(p, &parent->base.profiles, base.list)) + return p; + p = parent; +@@ -648,7 +646,7 @@ static struct aa_profile *__next_profile(struct aa_profile *p) + } + + /* is next another profile in the namespace */ +- p = list_entry_next(p, base.list); ++ p = list_next_entry(p, base.list); + if (!list_entry_is_head(p, &ns->base.profiles, base.list)) + return p; + +-- +2.7.4 + diff --git a/kernel-patches/4.6/0017-apparmor-allow-SYS_CAP_RESOURCE-to-be-sufficient-to-.patch b/kernel-patches/4.6/0017-apparmor-allow-SYS_CAP_RESOURCE-to-be-sufficient-to-.patch new file mode 100644 index 000000000..b6688e159 --- /dev/null +++ b/kernel-patches/4.6/0017-apparmor-allow-SYS_CAP_RESOURCE-to-be-sufficient-to-.patch @@ -0,0 +1,50 @@ +From a3896605318b86d8cf288c122e03604e349d5dd7 Mon Sep 17 00:00:00 2001 +From: Jeff Mahoney +Date: Fri, 6 Nov 2015 15:17:30 -0500 +Subject: [PATCH 17/27] apparmor: allow SYS_CAP_RESOURCE to be sufficient to + prlimit another task + +While using AppArmor, SYS_CAP_RESOURCE is insufficient to call prlimit +on another task. The only other example of a AppArmor mediating access to +another, already running, task (ignoring fork+exec) is ptrace. + +The AppArmor model for ptrace is that one of the following must be true: +1) The tracer is unconfined +2) The tracer is in complain mode +3) The tracer and tracee are confined by the same profile +4) The tracer is confined but has SYS_CAP_PTRACE + +1), 2, and 3) are already true for setrlimit. + +We can match the ptrace model just by allowing CAP_SYS_RESOURCE. + +We still test the values of the rlimit since it can always be overridden +using a value that means unlimited for a particular resource. + +Signed-off-by: Jeff Mahoney +Signed-off-by: John Johansen +--- + security/apparmor/resource.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c +index 748bf0c..67a6072 100644 +--- a/security/apparmor/resource.c ++++ b/security/apparmor/resource.c +@@ -101,9 +101,11 @@ int aa_task_setrlimit(struct aa_profile *profile, struct task_struct *task, + /* TODO: extend resource control to handle other (non current) + * profiles. AppArmor rules currently have the implicit assumption + * that the task is setting the resource of a task confined with +- * the same profile. ++ * the same profile or that the task setting the resource of another ++ * task has CAP_SYS_RESOURCE. + */ +- if (profile != task_profile || ++ if ((profile != task_profile && ++ aa_capable(profile, CAP_SYS_RESOURCE, 1)) || + (profile->rlimits.mask & (1 << resource) && + new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max)) + error = -EACCES; +-- +2.7.4 + diff --git a/kernel-patches/4.6/0018-apparmor-add-missing-id-bounds-check-on-dfa-verifica.patch b/kernel-patches/4.6/0018-apparmor-add-missing-id-bounds-check-on-dfa-verifica.patch new file mode 100644 index 000000000..72f43855e --- /dev/null +++ b/kernel-patches/4.6/0018-apparmor-add-missing-id-bounds-check-on-dfa-verifica.patch @@ -0,0 +1,40 @@ +From 6fdcc3cfecd4d89457036627d59ebe5154d094c5 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Thu, 2 Jun 2016 02:37:02 -0700 +Subject: [PATCH 18/27] apparmor: add missing id bounds check on dfa + verification + +Signed-off-by: John Johansen +--- + security/apparmor/include/match.h | 1 + + security/apparmor/match.c | 2 ++ + 2 files changed, 3 insertions(+) + +diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h +index 001c43a..a1c04fe 100644 +--- a/security/apparmor/include/match.h ++++ b/security/apparmor/include/match.h +@@ -62,6 +62,7 @@ struct table_set_header { + #define YYTD_ID_ACCEPT2 6 + #define YYTD_ID_NXT 7 + #define YYTD_ID_TSIZE 8 ++#define YYTD_ID_MAX 8 + + #define YYTD_DATA8 1 + #define YYTD_DATA16 2 +diff --git a/security/apparmor/match.c b/security/apparmor/match.c +index 727eb42..f9f57c6 100644 +--- a/security/apparmor/match.c ++++ b/security/apparmor/match.c +@@ -47,6 +47,8 @@ static struct table_header *unpack_table(char *blob, size_t bsize) + * it every time we use td_id as an index + */ + th.td_id = be16_to_cpu(*(u16 *) (blob)) - 1; ++ if (th.td_id > YYTD_ID_MAX) ++ goto out; + th.td_flags = be16_to_cpu(*(u16 *) (blob + 2)); + th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8)); + blob += sizeof(struct table_header); +-- +2.7.4 + diff --git a/kernel-patches/4.6/0019-apparmor-don-t-check-for-vmalloc_addr-if-kvzalloc-fa.patch b/kernel-patches/4.6/0019-apparmor-don-t-check-for-vmalloc_addr-if-kvzalloc-fa.patch new file mode 100644 index 000000000..f4d0e589c --- /dev/null +++ b/kernel-patches/4.6/0019-apparmor-don-t-check-for-vmalloc_addr-if-kvzalloc-fa.patch @@ -0,0 +1,38 @@ +From 95d203cfb59627a86483a279ba82f1aa75297e07 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Wed, 15 Jun 2016 09:57:55 +0300 +Subject: [PATCH 19/27] apparmor: don't check for vmalloc_addr if kvzalloc() + failed + +Signed-off-by: John Johansen +--- + security/apparmor/match.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/security/apparmor/match.c b/security/apparmor/match.c +index f9f57c6..32b72eb 100644 +--- a/security/apparmor/match.c ++++ b/security/apparmor/match.c +@@ -75,14 +75,14 @@ static struct table_header *unpack_table(char *blob, size_t bsize) + u32, be32_to_cpu); + else + goto fail; ++ /* if table was vmalloced make sure the page tables are synced ++ * before it is used, as it goes live to all cpus. ++ */ ++ if (is_vmalloc_addr(table)) ++ vm_unmap_aliases(); + } + + out: +- /* if table was vmalloced make sure the page tables are synced +- * before it is used, as it goes live to all cpus. +- */ +- if (is_vmalloc_addr(table)) +- vm_unmap_aliases(); + return table; + fail: + kvfree(table); +-- +2.7.4 + diff --git a/kernel-patches/4.6/0020-apparmor-fix-oops-in-profile_unpack-when-policy_db-i.patch b/kernel-patches/4.6/0020-apparmor-fix-oops-in-profile_unpack-when-policy_db-i.patch new file mode 100644 index 000000000..afa3a8dc3 --- /dev/null +++ b/kernel-patches/4.6/0020-apparmor-fix-oops-in-profile_unpack-when-policy_db-i.patch @@ -0,0 +1,33 @@ +From e925f976c7a9c85455f67c360671254bac2d9a91 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Wed, 15 Jun 2016 10:00:55 +0300 +Subject: [PATCH 20/27] apparmor: fix oops in profile_unpack() when policy_db + is not present + +BugLink: http://bugs.launchpad.net/bugs/1592547 + +If unpack_dfa() returns NULL due to the dfa not being present, +profile_unpack() is not checking if the dfa is not present (NULL). + +Signed-off-by: John Johansen +--- + security/apparmor/policy_unpack.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c +index c841b12..dac2121 100644 +--- a/security/apparmor/policy_unpack.c ++++ b/security/apparmor/policy_unpack.c +@@ -583,6 +583,9 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) + error = PTR_ERR(profile->policy.dfa); + profile->policy.dfa = NULL; + goto fail; ++ } else if (!profile->policy.dfa) { ++ error = -EPROTO; ++ goto fail; + } + if (!unpack_u32(e, &profile->policy.start[0], "start")) + /* default start state */ +-- +2.7.4 + diff --git a/kernel-patches/4.6/0021-apparmor-fix-module-parameters-can-be-changed-after-.patch b/kernel-patches/4.6/0021-apparmor-fix-module-parameters-can-be-changed-after-.patch new file mode 100644 index 000000000..e1dce3168 --- /dev/null +++ b/kernel-patches/4.6/0021-apparmor-fix-module-parameters-can-be-changed-after-.patch @@ -0,0 +1,162 @@ +From 45774028820fe2ffbbc94667165f04749821d529 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Wed, 22 Jun 2016 18:01:08 -0700 +Subject: [PATCH 21/27] apparmor: fix module parameters can be changed after + policy is locked + +the policy_lock parameter is a one way switch that prevents policy +from being further modified. Unfortunately some of the module parameters +can effectively modify policy by turning off enforcement. + +split policy_admin_capable into a view check and a full admin check, +and update the admin check to test the policy_lock parameter. + +Signed-off-by: John Johansen +--- + security/apparmor/include/policy.h | 2 ++ + security/apparmor/lsm.c | 22 ++++++++++------------ + security/apparmor/policy.c | 18 +++++++++++++++++- + 3 files changed, 29 insertions(+), 13 deletions(-) + +diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h +index c28b0f2..52275f0 100644 +--- a/security/apparmor/include/policy.h ++++ b/security/apparmor/include/policy.h +@@ -403,6 +403,8 @@ static inline int AUDIT_MODE(struct aa_profile *profile) + return profile->audit; + } + ++bool policy_view_capable(void); ++bool policy_admin_capable(void); + bool aa_may_manage_policy(int op); + + #endif /* __AA_POLICY_H */ +diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c +index 5ee8201..bd40b12 100644 +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -751,51 +751,49 @@ __setup("apparmor=", apparmor_enabled_setup); + /* set global flag turning off the ability to load policy */ + static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp) + { +- if (!capable(CAP_MAC_ADMIN)) ++ if (!policy_admin_capable()) + return -EPERM; +- if (aa_g_lock_policy) +- return -EACCES; + return param_set_bool(val, kp); + } + + static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp) + { +- if (!capable(CAP_MAC_ADMIN)) ++ if (!policy_view_capable()) + return -EPERM; + return param_get_bool(buffer, kp); + } + + static int param_set_aabool(const char *val, const struct kernel_param *kp) + { +- if (!capable(CAP_MAC_ADMIN)) ++ if (!policy_admin_capable()) + return -EPERM; + return param_set_bool(val, kp); + } + + static int param_get_aabool(char *buffer, const struct kernel_param *kp) + { +- if (!capable(CAP_MAC_ADMIN)) ++ if (!policy_view_capable()) + return -EPERM; + return param_get_bool(buffer, kp); + } + + static int param_set_aauint(const char *val, const struct kernel_param *kp) + { +- if (!capable(CAP_MAC_ADMIN)) ++ if (!policy_admin_capable()) + return -EPERM; + return param_set_uint(val, kp); + } + + static int param_get_aauint(char *buffer, const struct kernel_param *kp) + { +- if (!capable(CAP_MAC_ADMIN)) ++ if (!policy_view_capable()) + return -EPERM; + return param_get_uint(buffer, kp); + } + + static int param_get_audit(char *buffer, struct kernel_param *kp) + { +- if (!capable(CAP_MAC_ADMIN)) ++ if (!policy_view_capable()) + return -EPERM; + + if (!apparmor_enabled) +@@ -807,7 +805,7 @@ static int param_get_audit(char *buffer, struct kernel_param *kp) + static int param_set_audit(const char *val, struct kernel_param *kp) + { + int i; +- if (!capable(CAP_MAC_ADMIN)) ++ if (!policy_admin_capable()) + return -EPERM; + + if (!apparmor_enabled) +@@ -828,7 +826,7 @@ static int param_set_audit(const char *val, struct kernel_param *kp) + + static int param_get_mode(char *buffer, struct kernel_param *kp) + { +- if (!capable(CAP_MAC_ADMIN)) ++ if (!policy_admin_capable()) + return -EPERM; + + if (!apparmor_enabled) +@@ -840,7 +838,7 @@ static int param_get_mode(char *buffer, struct kernel_param *kp) + static int param_set_mode(const char *val, struct kernel_param *kp) + { + int i; +- if (!capable(CAP_MAC_ADMIN)) ++ if (!policy_admin_capable()) + return -EPERM; + + if (!apparmor_enabled) +diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c +index 7807125..179e68d 100644 +--- a/security/apparmor/policy.c ++++ b/security/apparmor/policy.c +@@ -918,6 +918,22 @@ static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, + &sa, NULL); + } + ++bool policy_view_capable(void) ++{ ++ struct user_namespace *user_ns = current_user_ns(); ++ bool response = false; ++ ++ if (ns_capable(user_ns, CAP_MAC_ADMIN)) ++ response = true; ++ ++ return response; ++} ++ ++bool policy_admin_capable(void) ++{ ++ return policy_view_capable() && !aa_g_lock_policy; ++} ++ + /** + * aa_may_manage_policy - can the current task manage policy + * @op: the policy manipulation operation being done +@@ -932,7 +948,7 @@ bool aa_may_manage_policy(int op) + return 0; + } + +- if (!capable(CAP_MAC_ADMIN)) { ++ if (!policy_admin_capable()) { + audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES); + return 0; + } +-- +2.7.4 + diff --git a/kernel-patches/4.6/0022-apparmor-do-not-expose-kernel-stack.patch b/kernel-patches/4.6/0022-apparmor-do-not-expose-kernel-stack.patch new file mode 100644 index 000000000..e36d4dcce --- /dev/null +++ b/kernel-patches/4.6/0022-apparmor-do-not-expose-kernel-stack.patch @@ -0,0 +1,31 @@ +From 7fcfc22cd04261ac35a579c99bcc804db7eb3e83 Mon Sep 17 00:00:00 2001 +From: Heinrich Schuchardt +Date: Fri, 10 Jun 2016 23:34:26 +0200 +Subject: [PATCH 22/27] apparmor: do not expose kernel stack + +Do not copy uninitalized fields th.td_hilen, th.td_data. + +Signed-off-by: Heinrich Schuchardt +Signed-off-by: John Johansen +--- + security/apparmor/match.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/security/apparmor/match.c b/security/apparmor/match.c +index 32b72eb..3f900fc 100644 +--- a/security/apparmor/match.c ++++ b/security/apparmor/match.c +@@ -63,7 +63,9 @@ static struct table_header *unpack_table(char *blob, size_t bsize) + + table = kvzalloc(tsize); + if (table) { +- *table = th; ++ table->td_id = th.td_id; ++ table->td_flags = th.td_flags; ++ table->td_lolen = th.td_lolen; + if (th.td_flags == YYTD_DATA8) + UNPACK_ARRAY(table->td_data, blob, th.td_lolen, + u8, byte_to_byte); +-- +2.7.4 + diff --git a/kernel-patches/4.6/0023-apparmor-fix-arg_size-computation-for-when-setprocat.patch b/kernel-patches/4.6/0023-apparmor-fix-arg_size-computation-for-when-setprocat.patch new file mode 100644 index 000000000..ecedfc4be --- /dev/null +++ b/kernel-patches/4.6/0023-apparmor-fix-arg_size-computation-for-when-setprocat.patch @@ -0,0 +1,27 @@ +From 1b98560066c26fecb0a61aeb9249e141af2e63f9 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Sat, 9 Jul 2016 23:46:33 -0700 +Subject: [PATCH 23/27] apparmor: fix arg_size computation for when setprocattr + is null terminated + +Signed-off-by: John Johansen +--- + security/apparmor/lsm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c +index bd40b12..1bf6c53 100644 +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -552,7 +552,7 @@ static int apparmor_setprocattr(struct task_struct *task, char *name, + if (!*args) + goto out; + +- arg_size = size - (args - (char *) value); ++ arg_size = size - (args - (largs ? largs : (char *) value)); + if (strcmp(name, "current") == 0) { + if (strcmp(command, "changehat") == 0) { + error = aa_setprocattr_changehat(args, arg_size, +-- +2.7.4 + diff --git a/kernel-patches/4.6/0024-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch b/kernel-patches/4.6/0024-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch new file mode 100644 index 000000000..8a1156146 --- /dev/null +++ b/kernel-patches/4.6/0024-UBUNTU-SAUCE-AppArmor-basic-networking-rules.patch @@ -0,0 +1,603 @@ +From 8d7c032e7798fa1c46449728874b64fff882368b Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Mon, 4 Oct 2010 15:03:36 -0700 +Subject: [PATCH 24/27] UBUNTU: SAUCE: AppArmor: basic networking rules + +Base support for network mediation. + +Signed-off-by: John Johansen +--- + security/apparmor/.gitignore | 1 + + security/apparmor/Makefile | 42 +++++++++- + security/apparmor/apparmorfs.c | 1 + + security/apparmor/include/audit.h | 4 + + security/apparmor/include/net.h | 44 ++++++++++ + security/apparmor/include/policy.h | 3 + + security/apparmor/lsm.c | 112 +++++++++++++++++++++++++ + security/apparmor/net.c | 162 +++++++++++++++++++++++++++++++++++++ + security/apparmor/policy.c | 1 + + security/apparmor/policy_unpack.c | 46 +++++++++++ + 10 files changed, 414 insertions(+), 2 deletions(-) + create mode 100644 security/apparmor/include/net.h + create mode 100644 security/apparmor/net.c + +diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore +index 9cdec70..d5b291e 100644 +--- a/security/apparmor/.gitignore ++++ b/security/apparmor/.gitignore +@@ -1,5 +1,6 @@ + # + # Generated include files + # ++net_names.h + capability_names.h + rlim_names.h +diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile +index d693df8..5dbb72f 100644 +--- a/security/apparmor/Makefile ++++ b/security/apparmor/Makefile +@@ -4,10 +4,10 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o + + apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ + path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ +- resource.o sid.o file.o ++ resource.o sid.o file.o net.o + apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o + +-clean-files := capability_names.h rlim_names.h ++clean-files := capability_names.h rlim_names.h net_names.h + + + # Build a lower case string table of capability names +@@ -25,6 +25,38 @@ cmd_make-caps = echo "static const char *const capability_names[] = {" > $@ ;\ + -e 's/^\#define[ \t]+CAP_([A-Z0-9_]+)[ \t]+([0-9]+)/\L\1/p' | \ + tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + ++# Build a lower case string table of address family names ++# Transform lines from ++# define AF_LOCAL 1 /* POSIX name for AF_UNIX */ ++# #define AF_INET 2 /* Internet IP Protocol */ ++# to ++# [1] = "local", ++# [2] = "inet", ++# ++# and build the securityfs entries for the mapping. ++# Transforms lines from ++# #define AF_INET 2 /* Internet IP Protocol */ ++# to ++# #define AA_FS_AF_MASK "local inet" ++quiet_cmd_make-af = GEN $@ ++cmd_make-af = echo "static const char *address_family_names[] = {" > $@ ;\ ++ sed $< >>$@ -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e \ ++ 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ ++ echo "};" >> $@ ;\ ++ echo -n '\#define AA_FS_AF_MASK "' >> $@ ;\ ++ sed -r -n 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/\L\1/p'\ ++ $< | tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ ++ ++# Build a lower case string table of sock type names ++# Transform lines from ++# SOCK_STREAM = 1, ++# to ++# [1] = "stream", ++quiet_cmd_make-sock = GEN $@ ++cmd_make-sock = echo "static const char *sock_type_names[] = {" >> $@ ;\ ++ sed $^ >>$@ -r -n \ ++ -e 's/^\tSOCK_([A-Z0-9_]+)[\t]+=[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ ++ echo "};" >> $@ + + # Build a lower case string table of rlimit names. + # Transforms lines from +@@ -61,6 +93,7 @@ cmd_make-rlim = echo "static const char *const rlim_names[RLIM_NLIMITS] = {" \ + tr '\n' ' ' | sed -e 's/ $$/"\n/' >> $@ + + $(obj)/capability.o : $(obj)/capability_names.h ++$(obj)/net.o : $(obj)/net_names.h + $(obj)/resource.o : $(obj)/rlim_names.h + $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ + $(src)/Makefile +@@ -68,3 +101,8 @@ $(obj)/capability_names.h : $(srctree)/include/uapi/linux/capability.h \ + $(obj)/rlim_names.h : $(srctree)/include/uapi/asm-generic/resource.h \ + $(src)/Makefile + $(call cmd,make-rlim) ++$(obj)/net_names.h : $(srctree)/include/linux/socket.h \ ++ $(srctree)/include/linux/net.h \ ++ $(src)/Makefile ++ $(call cmd,make-af) ++ $(call cmd,make-sock) +diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c +index 729e595..181d961 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -807,6 +807,7 @@ static struct aa_fs_entry aa_fs_entry_features[] = { + AA_FS_DIR("policy", aa_fs_entry_policy), + AA_FS_DIR("domain", aa_fs_entry_domain), + AA_FS_DIR("file", aa_fs_entry_file), ++ AA_FS_DIR("network", aa_fs_entry_network), + AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), + AA_FS_DIR("rlimit", aa_fs_entry_rlimit), + AA_FS_DIR("caps", aa_fs_entry_caps), +diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h +index ba3dfd1..5d3c419 100644 +--- a/security/apparmor/include/audit.h ++++ b/security/apparmor/include/audit.h +@@ -125,6 +125,10 @@ struct apparmor_audit_data { + u32 denied; + kuid_t ouid; + } fs; ++ struct { ++ int type, protocol; ++ struct sock *sk; ++ } net; + }; + }; + +diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h +new file mode 100644 +index 0000000..cb8a121 +--- /dev/null ++++ b/security/apparmor/include/net.h +@@ -0,0 +1,44 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor network mediation definitions. ++ * ++ * Copyright (C) 1998-2008 Novell/SUSE ++ * Copyright 2009-2012 Canonical Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ */ ++ ++#ifndef __AA_NET_H ++#define __AA_NET_H ++ ++#include ++ ++#include "apparmorfs.h" ++ ++/* struct aa_net - network confinement data ++ * @allowed: basic network families permissions ++ * @audit_network: which network permissions to force audit ++ * @quiet_network: which network permissions to quiet rejects ++ */ ++struct aa_net { ++ u16 allow[AF_MAX]; ++ u16 audit[AF_MAX]; ++ u16 quiet[AF_MAX]; ++}; ++ ++extern struct aa_fs_entry aa_fs_entry_network[]; ++ ++extern int aa_net_perm(int op, struct aa_profile *profile, u16 family, ++ int type, int protocol, struct sock *sk); ++extern int aa_revalidate_sk(int op, struct sock *sk); ++ ++static inline void aa_free_net_rules(struct aa_net *new) ++{ ++ /* NOP */ ++} ++ ++#endif /* __AA_NET_H */ +diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h +index 52275f0..4fc4dac 100644 +--- a/security/apparmor/include/policy.h ++++ b/security/apparmor/include/policy.h +@@ -27,6 +27,7 @@ + #include "capability.h" + #include "domain.h" + #include "file.h" ++#include "net.h" + #include "resource.h" + + extern const char *const aa_profile_mode_names[]; +@@ -176,6 +177,7 @@ struct aa_replacedby { + * @policy: general match rules governing policy + * @file: The set of rules governing basic file access and domain transitions + * @caps: capabilities for the profile ++ * @net: network controls for the profile + * @rlimits: rlimits for the profile + * + * @dents: dentries for the profiles file entries in apparmorfs +@@ -217,6 +219,7 @@ struct aa_profile { + struct aa_policydb policy; + struct aa_file_rules file; + struct aa_caps caps; ++ struct aa_net net; + struct aa_rlimit rlimits; + + unsigned char *hash; +diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c +index 1bf6c53..284ddda 100644 +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -32,6 +32,7 @@ + #include "include/context.h" + #include "include/file.h" + #include "include/ipc.h" ++#include "include/net.h" + #include "include/path.h" + #include "include/policy.h" + #include "include/procattr.h" +@@ -607,6 +608,104 @@ static int apparmor_task_setrlimit(struct task_struct *task, + return error; + } + ++static int apparmor_socket_create(int family, int type, int protocol, int kern) ++{ ++ struct aa_profile *profile; ++ int error = 0; ++ ++ if (kern) ++ return 0; ++ ++ profile = __aa_current_profile(); ++ if (!unconfined(profile)) ++ error = aa_net_perm(OP_CREATE, profile, family, type, protocol, ++ NULL); ++ return error; ++} ++ ++static int apparmor_socket_bind(struct socket *sock, ++ struct sockaddr *address, int addrlen) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_BIND, sk); ++} ++ ++static int apparmor_socket_connect(struct socket *sock, ++ struct sockaddr *address, int addrlen) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_CONNECT, sk); ++} ++ ++static int apparmor_socket_listen(struct socket *sock, int backlog) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_LISTEN, sk); ++} ++ ++static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_ACCEPT, sk); ++} ++ ++static int apparmor_socket_sendmsg(struct socket *sock, ++ struct msghdr *msg, int size) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_SENDMSG, sk); ++} ++ ++static int apparmor_socket_recvmsg(struct socket *sock, ++ struct msghdr *msg, int size, int flags) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_RECVMSG, sk); ++} ++ ++static int apparmor_socket_getsockname(struct socket *sock) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_GETSOCKNAME, sk); ++} ++ ++static int apparmor_socket_getpeername(struct socket *sock) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_GETPEERNAME, sk); ++} ++ ++static int apparmor_socket_getsockopt(struct socket *sock, int level, ++ int optname) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_GETSOCKOPT, sk); ++} ++ ++static int apparmor_socket_setsockopt(struct socket *sock, int level, ++ int optname) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_SETSOCKOPT, sk); ++} ++ ++static int apparmor_socket_shutdown(struct socket *sock, int how) ++{ ++ struct sock *sk = sock->sk; ++ ++ return aa_revalidate_sk(OP_SOCK_SHUTDOWN, sk); ++} ++ + static struct security_hook_list apparmor_hooks[] = { + LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), + LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), +@@ -636,6 +735,19 @@ static struct security_hook_list apparmor_hooks[] = { + LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), + LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), + ++ LSM_HOOK_INIT(socket_create, apparmor_socket_create), ++ LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), ++ LSM_HOOK_INIT(socket_connect, apparmor_socket_connect), ++ LSM_HOOK_INIT(socket_listen, apparmor_socket_listen), ++ LSM_HOOK_INIT(socket_accept, apparmor_socket_accept), ++ LSM_HOOK_INIT(socket_sendmsg, apparmor_socket_sendmsg), ++ LSM_HOOK_INIT(socket_recvmsg, apparmor_socket_recvmsg), ++ LSM_HOOK_INIT(socket_getsockname, apparmor_socket_getsockname), ++ LSM_HOOK_INIT(socket_getpeername, apparmor_socket_getpeername), ++ LSM_HOOK_INIT(socket_getsockopt, apparmor_socket_getsockopt), ++ LSM_HOOK_INIT(socket_setsockopt, apparmor_socket_setsockopt), ++ LSM_HOOK_INIT(socket_shutdown, apparmor_socket_shutdown), ++ + LSM_HOOK_INIT(cred_alloc_blank, apparmor_cred_alloc_blank), + LSM_HOOK_INIT(cred_free, apparmor_cred_free), + LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare), +diff --git a/security/apparmor/net.c b/security/apparmor/net.c +new file mode 100644 +index 0000000..003dd18 +--- /dev/null ++++ b/security/apparmor/net.c +@@ -0,0 +1,162 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor network mediation ++ * ++ * Copyright (C) 1998-2008 Novell/SUSE ++ * Copyright 2009-2012 Canonical Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ */ ++ ++#include "include/apparmor.h" ++#include "include/audit.h" ++#include "include/context.h" ++#include "include/net.h" ++#include "include/policy.h" ++ ++#include "net_names.h" ++ ++struct aa_fs_entry aa_fs_entry_network[] = { ++ AA_FS_FILE_STRING("af_mask", AA_FS_AF_MASK), ++ { } ++}; ++ ++/* audit callback for net specific fields */ ++static void audit_cb(struct audit_buffer *ab, void *va) ++{ ++ struct common_audit_data *sa = va; ++ ++ audit_log_format(ab, " family="); ++ if (address_family_names[sa->u.net->family]) { ++ audit_log_string(ab, address_family_names[sa->u.net->family]); ++ } else { ++ audit_log_format(ab, "\"unknown(%d)\"", sa->u.net->family); ++ } ++ audit_log_format(ab, " sock_type="); ++ if (sock_type_names[sa->aad->net.type]) { ++ audit_log_string(ab, sock_type_names[sa->aad->net.type]); ++ } else { ++ audit_log_format(ab, "\"unknown(%d)\"", sa->aad->net.type); ++ } ++ audit_log_format(ab, " protocol=%d", sa->aad->net.protocol); ++} ++ ++/** ++ * audit_net - audit network access ++ * @profile: profile being enforced (NOT NULL) ++ * @op: operation being checked ++ * @family: network family ++ * @type: network type ++ * @protocol: network protocol ++ * @sk: socket auditing is being applied to ++ * @error: error code for failure else 0 ++ * ++ * Returns: %0 or sa->error else other errorcode on failure ++ */ ++static int audit_net(struct aa_profile *profile, int op, u16 family, int type, ++ int protocol, struct sock *sk, int error) ++{ ++ int audit_type = AUDIT_APPARMOR_AUTO; ++ struct common_audit_data sa; ++ struct apparmor_audit_data aad = { }; ++ struct lsm_network_audit net = { }; ++ if (sk) { ++ sa.type = LSM_AUDIT_DATA_NET; ++ } else { ++ sa.type = LSM_AUDIT_DATA_NONE; ++ } ++ /* todo fill in socket addr info */ ++ sa.aad = &aad; ++ sa.u.net = &net; ++ sa.aad->op = op, ++ sa.u.net->family = family; ++ sa.u.net->sk = sk; ++ sa.aad->net.type = type; ++ sa.aad->net.protocol = protocol; ++ sa.aad->error = error; ++ ++ if (likely(!sa.aad->error)) { ++ u16 audit_mask = profile->net.audit[sa.u.net->family]; ++ if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && ++ !(1 << sa.aad->net.type & audit_mask))) ++ return 0; ++ audit_type = AUDIT_APPARMOR_AUDIT; ++ } else { ++ u16 quiet_mask = profile->net.quiet[sa.u.net->family]; ++ u16 kill_mask = 0; ++ u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; ++ ++ if (denied & kill_mask) ++ audit_type = AUDIT_APPARMOR_KILL; ++ ++ if ((denied & quiet_mask) && ++ AUDIT_MODE(profile) != AUDIT_NOQUIET && ++ AUDIT_MODE(profile) != AUDIT_ALL) ++ return COMPLAIN_MODE(profile) ? 0 : sa.aad->error; ++ } ++ ++ return aa_audit(audit_type, profile, GFP_KERNEL, &sa, audit_cb); ++} ++ ++/** ++ * aa_net_perm - very course network access check ++ * @op: operation being checked ++ * @profile: profile being enforced (NOT NULL) ++ * @family: network family ++ * @type: network type ++ * @protocol: network protocol ++ * ++ * Returns: %0 else error if permission denied ++ */ ++int aa_net_perm(int op, struct aa_profile *profile, u16 family, int type, ++ int protocol, struct sock *sk) ++{ ++ u16 family_mask; ++ int error; ++ ++ if ((family < 0) || (family >= AF_MAX)) ++ return -EINVAL; ++ ++ if ((type < 0) || (type >= SOCK_MAX)) ++ return -EINVAL; ++ ++ /* unix domain and netlink sockets are handled by ipc */ ++ if (family == AF_UNIX || family == AF_NETLINK) ++ return 0; ++ ++ family_mask = profile->net.allow[family]; ++ ++ error = (family_mask & (1 << type)) ? 0 : -EACCES; ++ ++ return audit_net(profile, op, family, type, protocol, sk, error); ++} ++ ++/** ++ * aa_revalidate_sk - Revalidate access to a sock ++ * @op: operation being checked ++ * @sk: sock being revalidated (NOT NULL) ++ * ++ * Returns: %0 else error if permission denied ++ */ ++int aa_revalidate_sk(int op, struct sock *sk) ++{ ++ struct aa_profile *profile; ++ int error = 0; ++ ++ /* aa_revalidate_sk should not be called from interrupt context ++ * don't mediate these calls as they are not task related ++ */ ++ if (in_interrupt()) ++ return 0; ++ ++ profile = __aa_current_profile(); ++ if (!unconfined(profile)) ++ error = aa_net_perm(op, profile, sk->sk_family, sk->sk_type, ++ sk->sk_protocol, sk); ++ ++ return error; ++} +diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c +index 179e68d..f1a8541 100644 +--- a/security/apparmor/policy.c ++++ b/security/apparmor/policy.c +@@ -603,6 +603,7 @@ void aa_free_profile(struct aa_profile *profile) + + aa_free_file_rules(&profile->file); + aa_free_cap_rules(&profile->caps); ++ aa_free_net_rules(&profile->net); + aa_free_rlimit_rules(&profile->rlimits); + + kzfree(profile->dirname); +diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c +index dac2121..0107bc4 100644 +--- a/security/apparmor/policy_unpack.c ++++ b/security/apparmor/policy_unpack.c +@@ -193,6 +193,19 @@ fail: + return 0; + } + ++static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) ++{ ++ if (unpack_nameX(e, AA_U16, name)) { ++ if (!inbounds(e, sizeof(u16))) ++ return 0; ++ if (data) ++ *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); ++ e->pos += sizeof(u16); ++ return 1; ++ } ++ return 0; ++} ++ + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) + { + if (unpack_nameX(e, AA_U32, name)) { +@@ -476,6 +489,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) + { + struct aa_profile *profile = NULL; + const char *name = NULL; ++ size_t size = 0; + int i, error = -EPROTO; + kernel_cap_t tmpcap; + u32 tmp; +@@ -576,6 +590,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e) + if (!unpack_rlimits(e, profile)) + goto fail; + ++ size = unpack_array(e, "net_allowed_af"); ++ if (size) { ++ ++ for (i = 0; i < size; i++) { ++ /* discard extraneous rules that this kernel will ++ * never request ++ */ ++ if (i >= AF_MAX) { ++ u16 tmp; ++ if (!unpack_u16(e, &tmp, NULL) || ++ !unpack_u16(e, &tmp, NULL) || ++ !unpack_u16(e, &tmp, NULL)) ++ goto fail; ++ continue; ++ } ++ if (!unpack_u16(e, &profile->net.allow[i], NULL)) ++ goto fail; ++ if (!unpack_u16(e, &profile->net.audit[i], NULL)) ++ goto fail; ++ if (!unpack_u16(e, &profile->net.quiet[i], NULL)) ++ goto fail; ++ } ++ if (!unpack_nameX(e, AA_ARRAYEND, NULL)) ++ goto fail; ++ } ++ /* ++ * allow unix domain and netlink sockets they are handled ++ * by IPC ++ */ ++ profile->net.allow[AF_UNIX] = 0xffff; ++ profile->net.allow[AF_NETLINK] = 0xffff; ++ + if (unpack_nameX(e, AA_STRUCT, "policydb")) { + /* generic policy dfa - optional and may be NULL */ + profile->policy.dfa = unpack_dfa(e); +-- +2.7.4 + diff --git a/kernel-patches/4.6/0025-apparmor-Fix-quieting-of-audit-messages-for-network-.patch b/kernel-patches/4.6/0025-apparmor-Fix-quieting-of-audit-messages-for-network-.patch new file mode 100644 index 000000000..15992e7ce --- /dev/null +++ b/kernel-patches/4.6/0025-apparmor-Fix-quieting-of-audit-messages-for-network-.patch @@ -0,0 +1,38 @@ +From aa45ba104003404efb59e6f7178045ade756035d Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Fri, 29 Jun 2012 17:34:00 -0700 +Subject: [PATCH 25/27] apparmor: Fix quieting of audit messages for network + mediation + +If a profile specified a quieting of network denials for a given rule by +either the quiet or deny rule qualifiers, the resultant quiet mask for +denied requests was applied incorrectly, resulting in two potential bugs. +1. The misapplied quiet mask would prevent denials from being correctly + tested against the kill mask/mode. Thus network access requests that + should have resulted in the application being killed did not. + +2. The actual quieting of the denied network request was not being applied. + This would result in network rejections always being logged even when + they had been specifically marked as quieted. + +Signed-off-by: John Johansen +--- + security/apparmor/net.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/security/apparmor/net.c b/security/apparmor/net.c +index 003dd18..6e6e5c9 100644 +--- a/security/apparmor/net.c ++++ b/security/apparmor/net.c +@@ -88,7 +88,7 @@ static int audit_net(struct aa_profile *profile, int op, u16 family, int type, + } else { + u16 quiet_mask = profile->net.quiet[sa.u.net->family]; + u16 kill_mask = 0; +- u16 denied = (1 << sa.aad->net.type) & ~quiet_mask; ++ u16 denied = (1 << sa.aad->net.type); + + if (denied & kill_mask) + audit_type = AUDIT_APPARMOR_KILL; +-- +2.7.4 + diff --git a/kernel-patches/4.6/0026-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch b/kernel-patches/4.6/0026-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch new file mode 100644 index 000000000..6557970b9 --- /dev/null +++ b/kernel-patches/4.6/0026-UBUNTU-SAUCE-apparmor-Add-the-ability-to-mediate-mou.patch @@ -0,0 +1,1010 @@ +From da5b036d6235f44c4ccbeaaf8d46fb29ff22745f Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Wed, 16 May 2012 10:58:05 -0700 +Subject: [PATCH 26/27] UBUNTU: SAUCE: apparmor: Add the ability to mediate + mount + +Add the ability for apparmor to do mediation of mount operations. Mount +rules require an updated apparmor_parser (2.8 series) for policy compilation. + +The basic form of the rules are. + + [audit] [deny] mount [conds]* [device] [ -> [conds] path], + [audit] [deny] remount [conds]* [path], + [audit] [deny] umount [conds]* [path], + [audit] [deny] pivotroot [oldroot=] + + remount is just a short cut for mount options=remount + + where [conds] can be + fstype= + options= + +Example mount commands + mount, # allow all mounts, but not umount or pivotroot + + mount fstype=procfs, # allow mounting procfs anywhere + + mount options=(bind, ro) /foo -> /bar, # readonly bind mount + + mount /dev/sda -> /mnt, + + mount /dev/sd** -> /mnt/**, + + mount fstype=overlayfs options=(rw,upperdir=/tmp/upper/,lowerdir=/) -> /mnt/ + + umount, + + umount /m*, + +See the apparmor userspace for full documentation + +Signed-off-by: John Johansen +Acked-by: Kees Cook +--- + security/apparmor/Makefile | 2 +- + security/apparmor/apparmorfs.c | 15 +- + security/apparmor/audit.c | 4 + + security/apparmor/domain.c | 2 +- + security/apparmor/include/apparmor.h | 3 +- + security/apparmor/include/audit.h | 11 + + security/apparmor/include/domain.h | 2 + + security/apparmor/include/mount.h | 54 +++ + security/apparmor/include/path.h | 2 +- + security/apparmor/lsm.c | 59 ++++ + security/apparmor/mount.c | 620 +++++++++++++++++++++++++++++++++++ + security/apparmor/path.c | 8 +- + 12 files changed, 773 insertions(+), 9 deletions(-) + create mode 100644 security/apparmor/include/mount.h + create mode 100644 security/apparmor/mount.c + +diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile +index 5dbb72f..89b3445 100644 +--- a/security/apparmor/Makefile ++++ b/security/apparmor/Makefile +@@ -4,7 +4,7 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o + + apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ + path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ +- resource.o sid.o file.o net.o ++ resource.o sid.o file.o net.o mount.o + apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o + + clean-files := capability_names.h rlim_names.h net_names.h +diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c +index 181d961..5fb67f6 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -800,7 +800,18 @@ static struct aa_fs_entry aa_fs_entry_domain[] = { + + static struct aa_fs_entry aa_fs_entry_policy[] = { + AA_FS_FILE_BOOLEAN("set_load", 1), +- {} ++ { } ++}; ++ ++static struct aa_fs_entry aa_fs_entry_mount[] = { ++ AA_FS_FILE_STRING("mask", "mount umount"), ++ { } ++}; ++ ++static struct aa_fs_entry aa_fs_entry_namespaces[] = { ++ AA_FS_FILE_BOOLEAN("profile", 1), ++ AA_FS_FILE_BOOLEAN("pivot_root", 1), ++ { } + }; + + static struct aa_fs_entry aa_fs_entry_features[] = { +@@ -808,6 +819,8 @@ static struct aa_fs_entry aa_fs_entry_features[] = { + AA_FS_DIR("domain", aa_fs_entry_domain), + AA_FS_DIR("file", aa_fs_entry_file), + AA_FS_DIR("network", aa_fs_entry_network), ++ AA_FS_DIR("mount", aa_fs_entry_mount), ++ AA_FS_DIR("namespaces", aa_fs_entry_namespaces), + AA_FS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), + AA_FS_DIR("rlimit", aa_fs_entry_rlimit), + AA_FS_DIR("caps", aa_fs_entry_caps), +diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c +index 3a7f1da..c2a8b8a 100644 +--- a/security/apparmor/audit.c ++++ b/security/apparmor/audit.c +@@ -44,6 +44,10 @@ const char *const op_table[] = { + "file_mmap", + "file_mprotect", + ++ "pivotroot", ++ "mount", ++ "umount", ++ + "create", + "post_create", + "bind", +diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c +index fc3036b..f2a83b4 100644 +--- a/security/apparmor/domain.c ++++ b/security/apparmor/domain.c +@@ -236,7 +236,7 @@ static const char *next_name(int xtype, const char *name) + * + * Returns: refcounted profile, or NULL on failure (MAYBE NULL) + */ +-static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) ++struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) + { + struct aa_profile *new_profile = NULL; + struct aa_namespace *ns = profile->ns; +diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h +index e4ea626..ce6ff6a 100644 +--- a/security/apparmor/include/apparmor.h ++++ b/security/apparmor/include/apparmor.h +@@ -30,8 +30,9 @@ + #define AA_CLASS_NET 4 + #define AA_CLASS_RLIMITS 5 + #define AA_CLASS_DOMAIN 6 ++#define AA_CLASS_MOUNT 7 + +-#define AA_CLASS_LAST AA_CLASS_DOMAIN ++#define AA_CLASS_LAST AA_CLASS_MOUNT + + /* Control parameters settable through module/boot flags */ + extern enum audit_mode aa_g_audit; +diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h +index 5d3c419..b9f1d57 100644 +--- a/security/apparmor/include/audit.h ++++ b/security/apparmor/include/audit.h +@@ -72,6 +72,10 @@ enum aa_ops { + OP_FMMAP, + OP_FMPROT, + ++ OP_PIVOTROOT, ++ OP_MOUNT, ++ OP_UMOUNT, ++ + OP_CREATE, + OP_POST_CREATE, + OP_BIND, +@@ -120,6 +124,13 @@ struct apparmor_audit_data { + unsigned long max; + } rlim; + struct { ++ const char *src_name; ++ const char *type; ++ const char *trans; ++ const char *data; ++ unsigned long flags; ++ } mnt; ++ struct { + const char *target; + u32 request; + u32 denied; +diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h +index de04464..a3f70c5 100644 +--- a/security/apparmor/include/domain.h ++++ b/security/apparmor/include/domain.h +@@ -23,6 +23,8 @@ struct aa_domain { + char **table; + }; + ++struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex); ++ + int apparmor_bprm_set_creds(struct linux_binprm *bprm); + int apparmor_bprm_secureexec(struct linux_binprm *bprm); + void apparmor_bprm_committing_creds(struct linux_binprm *bprm); +diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h +new file mode 100644 +index 0000000..a43b1d6 +--- /dev/null ++++ b/security/apparmor/include/mount.h +@@ -0,0 +1,54 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor file mediation function definitions. ++ * ++ * Copyright 2012 Canonical Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ */ ++ ++#ifndef __AA_MOUNT_H ++#define __AA_MOUNT_H ++ ++#include ++#include ++ ++#include "domain.h" ++#include "policy.h" ++ ++/* mount perms */ ++#define AA_MAY_PIVOTROOT 0x01 ++#define AA_MAY_MOUNT 0x02 ++#define AA_MAY_UMOUNT 0x04 ++#define AA_AUDIT_DATA 0x40 ++#define AA_CONT_MATCH 0x40 ++ ++#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) ++ ++int aa_remount(struct aa_profile *profile, const struct path *path, ++ unsigned long flags, void *data); ++ ++int aa_bind_mount(struct aa_profile *profile, const struct path *path, ++ const char *old_name, unsigned long flags); ++ ++ ++int aa_mount_change_type(struct aa_profile *profile, const struct path *path, ++ unsigned long flags); ++ ++int aa_move_mount(struct aa_profile *profile, const struct path *path, ++ const char *old_name); ++ ++int aa_new_mount(struct aa_profile *profile, const char *dev_name, ++ const struct path *path, const char *type, unsigned long flags, ++ void *data); ++ ++int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags); ++ ++int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, ++ const struct path *new_path); ++ ++#endif /* __AA_MOUNT_H */ +diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h +index 286ac75..73560f2 100644 +--- a/security/apparmor/include/path.h ++++ b/security/apparmor/include/path.h +@@ -26,7 +26,7 @@ enum path_flags { + PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */ + }; + +-int aa_path_name(struct path *path, int flags, char **buffer, ++int aa_path_name(const struct path *path, int flags, char **buffer, + const char **name, const char **info); + + #endif /* __AA_PATH_H */ +diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c +index 284ddda..780fec9 100644 +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -36,6 +36,7 @@ + #include "include/path.h" + #include "include/policy.h" + #include "include/procattr.h" ++#include "include/mount.h" + + /* Flag indicating whether initialization completed */ + int apparmor_initialized __initdata; +@@ -492,6 +493,60 @@ static int apparmor_file_mprotect(struct vm_area_struct *vma, + !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); + } + ++static int apparmor_sb_mount(const char *dev_name, struct path *path, ++ const char *type, unsigned long flags, void *data) ++{ ++ struct aa_profile *profile; ++ int error = 0; ++ ++ /* Discard magic */ ++ if ((flags & MS_MGC_MSK) == MS_MGC_VAL) ++ flags &= ~MS_MGC_MSK; ++ ++ flags &= ~AA_MS_IGNORE_MASK; ++ ++ profile = __aa_current_profile(); ++ if (!unconfined(profile)) { ++ if (flags & MS_REMOUNT) ++ error = aa_remount(profile, path, flags, data); ++ else if (flags & MS_BIND) ++ error = aa_bind_mount(profile, path, dev_name, flags); ++ else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | ++ MS_UNBINDABLE)) ++ error = aa_mount_change_type(profile, path, flags); ++ else if (flags & MS_MOVE) ++ error = aa_move_mount(profile, path, dev_name); ++ else ++ error = aa_new_mount(profile, dev_name, path, type, ++ flags, data); ++ } ++ return error; ++} ++ ++static int apparmor_sb_umount(struct vfsmount *mnt, int flags) ++{ ++ struct aa_profile *profile; ++ int error = 0; ++ ++ profile = __aa_current_profile(); ++ if (!unconfined(profile)) ++ error = aa_umount(profile, mnt, flags); ++ ++ return error; ++} ++ ++static int apparmor_sb_pivotroot(struct path *old_path, struct path *new_path) ++{ ++ struct aa_profile *profile; ++ int error = 0; ++ ++ profile = __aa_current_profile(); ++ if (!unconfined(profile)) ++ error = aa_pivotroot(profile, old_path, new_path); ++ ++ return error; ++} ++ + static int apparmor_getprocattr(struct task_struct *task, char *name, + char **value) + { +@@ -712,6 +767,10 @@ static struct security_hook_list apparmor_hooks[] = { + LSM_HOOK_INIT(capget, apparmor_capget), + LSM_HOOK_INIT(capable, apparmor_capable), + ++ LSM_HOOK_INIT(sb_mount, apparmor_sb_mount), ++ LSM_HOOK_INIT(sb_umount, apparmor_sb_umount), ++ LSM_HOOK_INIT(sb_pivotroot, apparmor_sb_pivotroot), ++ + LSM_HOOK_INIT(path_link, apparmor_path_link), + LSM_HOOK_INIT(path_unlink, apparmor_path_unlink), + LSM_HOOK_INIT(path_symlink, apparmor_path_symlink), +diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c +new file mode 100644 +index 0000000..9cf9170 +--- /dev/null ++++ b/security/apparmor/mount.c +@@ -0,0 +1,620 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor mediation of files ++ * ++ * Copyright (C) 1998-2008 Novell/SUSE ++ * Copyright 2009-2012 Canonical Ltd. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License as ++ * published by the Free Software Foundation, version 2 of the ++ * License. ++ */ ++ ++#include ++#include ++#include ++ ++#include "include/apparmor.h" ++#include "include/audit.h" ++#include "include/context.h" ++#include "include/domain.h" ++#include "include/file.h" ++#include "include/match.h" ++#include "include/mount.h" ++#include "include/path.h" ++#include "include/policy.h" ++ ++ ++static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags) ++{ ++ if (flags & MS_RDONLY) ++ audit_log_format(ab, "ro"); ++ else ++ audit_log_format(ab, "rw"); ++ if (flags & MS_NOSUID) ++ audit_log_format(ab, ", nosuid"); ++ if (flags & MS_NODEV) ++ audit_log_format(ab, ", nodev"); ++ if (flags & MS_NOEXEC) ++ audit_log_format(ab, ", noexec"); ++ if (flags & MS_SYNCHRONOUS) ++ audit_log_format(ab, ", sync"); ++ if (flags & MS_REMOUNT) ++ audit_log_format(ab, ", remount"); ++ if (flags & MS_MANDLOCK) ++ audit_log_format(ab, ", mand"); ++ if (flags & MS_DIRSYNC) ++ audit_log_format(ab, ", dirsync"); ++ if (flags & MS_NOATIME) ++ audit_log_format(ab, ", noatime"); ++ if (flags & MS_NODIRATIME) ++ audit_log_format(ab, ", nodiratime"); ++ if (flags & MS_BIND) ++ audit_log_format(ab, flags & MS_REC ? ", rbind" : ", bind"); ++ if (flags & MS_MOVE) ++ audit_log_format(ab, ", move"); ++ if (flags & MS_SILENT) ++ audit_log_format(ab, ", silent"); ++ if (flags & MS_POSIXACL) ++ audit_log_format(ab, ", acl"); ++ if (flags & MS_UNBINDABLE) ++ audit_log_format(ab, flags & MS_REC ? ", runbindable" : ++ ", unbindable"); ++ if (flags & MS_PRIVATE) ++ audit_log_format(ab, flags & MS_REC ? ", rprivate" : ++ ", private"); ++ if (flags & MS_SLAVE) ++ audit_log_format(ab, flags & MS_REC ? ", rslave" : ++ ", slave"); ++ if (flags & MS_SHARED) ++ audit_log_format(ab, flags & MS_REC ? ", rshared" : ++ ", shared"); ++ if (flags & MS_RELATIME) ++ audit_log_format(ab, ", relatime"); ++ if (flags & MS_I_VERSION) ++ audit_log_format(ab, ", iversion"); ++ if (flags & MS_STRICTATIME) ++ audit_log_format(ab, ", strictatime"); ++ if (flags & MS_NOUSER) ++ audit_log_format(ab, ", nouser"); ++} ++ ++/** ++ * audit_cb - call back for mount specific audit fields ++ * @ab: audit_buffer (NOT NULL) ++ * @va: audit struct to audit values of (NOT NULL) ++ */ ++static void audit_cb(struct audit_buffer *ab, void *va) ++{ ++ struct common_audit_data *sa = va; ++ ++ if (sa->aad->mnt.type) { ++ audit_log_format(ab, " fstype="); ++ audit_log_untrustedstring(ab, sa->aad->mnt.type); ++ } ++ if (sa->aad->mnt.src_name) { ++ audit_log_format(ab, " srcname="); ++ audit_log_untrustedstring(ab, sa->aad->mnt.src_name); ++ } ++ if (sa->aad->mnt.trans) { ++ audit_log_format(ab, " trans="); ++ audit_log_untrustedstring(ab, sa->aad->mnt.trans); ++ } ++ if (sa->aad->mnt.flags || sa->aad->op == OP_MOUNT) { ++ audit_log_format(ab, " flags=\""); ++ audit_mnt_flags(ab, sa->aad->mnt.flags); ++ audit_log_format(ab, "\""); ++ } ++ if (sa->aad->mnt.data) { ++ audit_log_format(ab, " options="); ++ audit_log_untrustedstring(ab, sa->aad->mnt.data); ++ } ++} ++ ++/** ++ * audit_mount - handle the auditing of mount operations ++ * @profile: the profile being enforced (NOT NULL) ++ * @gfp: allocation flags ++ * @op: operation being mediated (NOT NULL) ++ * @name: name of object being mediated (MAYBE NULL) ++ * @src_name: src_name of object being mediated (MAYBE_NULL) ++ * @type: type of filesystem (MAYBE_NULL) ++ * @trans: name of trans (MAYBE NULL) ++ * @flags: filesystem idependent mount flags ++ * @data: filesystem mount flags ++ * @request: permissions requested ++ * @perms: the permissions computed for the request (NOT NULL) ++ * @info: extra information message (MAYBE NULL) ++ * @error: 0 if operation allowed else failure error code ++ * ++ * Returns: %0 or error on failure ++ */ ++static int audit_mount(struct aa_profile *profile, gfp_t gfp, int op, ++ const char *name, const char *src_name, ++ const char *type, const char *trans, ++ unsigned long flags, const void *data, u32 request, ++ struct file_perms *perms, const char *info, int error) ++{ ++ int audit_type = AUDIT_APPARMOR_AUTO; ++ struct common_audit_data sa = { }; ++ struct apparmor_audit_data aad = { }; ++ ++ if (likely(!error)) { ++ u32 mask = perms->audit; ++ ++ if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) ++ mask = 0xffff; ++ ++ /* mask off perms that are not being force audited */ ++ request &= mask; ++ ++ if (likely(!request)) ++ return 0; ++ audit_type = AUDIT_APPARMOR_AUDIT; ++ } else { ++ /* only report permissions that were denied */ ++ request = request & ~perms->allow; ++ ++ if (request & perms->kill) ++ audit_type = AUDIT_APPARMOR_KILL; ++ ++ /* quiet known rejects, assumes quiet and kill do not overlap */ ++ if ((request & perms->quiet) && ++ AUDIT_MODE(profile) != AUDIT_NOQUIET && ++ AUDIT_MODE(profile) != AUDIT_ALL) ++ request &= ~perms->quiet; ++ ++ if (!request) ++ return COMPLAIN_MODE(profile) ? ++ complain_error(error) : error; ++ } ++ ++ sa.type = LSM_AUDIT_DATA_NONE; ++ sa.aad = &aad; ++ sa.aad->op = op; ++ sa.aad->name = name; ++ sa.aad->mnt.src_name = src_name; ++ sa.aad->mnt.type = type; ++ sa.aad->mnt.trans = trans; ++ sa.aad->mnt.flags = flags; ++ if (data && (perms->audit & AA_AUDIT_DATA)) ++ sa.aad->mnt.data = data; ++ sa.aad->info = info; ++ sa.aad->error = error; ++ ++ return aa_audit(audit_type, profile, gfp, &sa, audit_cb); ++} ++ ++/** ++ * match_mnt_flags - Do an ordered match on mount flags ++ * @dfa: dfa to match against ++ * @state: state to start in ++ * @flags: mount flags to match against ++ * ++ * Mount flags are encoded as an ordered match. This is done instead of ++ * checking against a simple bitmask, to allow for logical operations ++ * on the flags. ++ * ++ * Returns: next state after flags match ++ */ ++static unsigned int match_mnt_flags(struct aa_dfa *dfa, unsigned int state, ++ unsigned long flags) ++{ ++ unsigned int i; ++ ++ for (i = 0; i <= 31 ; ++i) { ++ if ((1 << i) & flags) ++ state = aa_dfa_next(dfa, state, i + 1); ++ } ++ ++ return state; ++} ++ ++/** ++ * compute_mnt_perms - compute mount permission associated with @state ++ * @dfa: dfa to match against (NOT NULL) ++ * @state: state match finished in ++ * ++ * Returns: mount permissions ++ */ ++static struct file_perms compute_mnt_perms(struct aa_dfa *dfa, ++ unsigned int state) ++{ ++ struct file_perms perms; ++ ++ perms.kill = 0; ++ perms.allow = dfa_user_allow(dfa, state); ++ perms.audit = dfa_user_audit(dfa, state); ++ perms.quiet = dfa_user_quiet(dfa, state); ++ perms.xindex = dfa_user_xindex(dfa, state); ++ ++ return perms; ++} ++ ++static const char const *mnt_info_table[] = { ++ "match succeeded", ++ "failed mntpnt match", ++ "failed srcname match", ++ "failed type match", ++ "failed flags match", ++ "failed data match" ++}; ++ ++/* ++ * Returns 0 on success else element that match failed in, this is the ++ * index into the mnt_info_table above ++ */ ++static int do_match_mnt(struct aa_dfa *dfa, unsigned int start, ++ const char *mntpnt, const char *devname, ++ const char *type, unsigned long flags, ++ void *data, bool binary, struct file_perms *perms) ++{ ++ unsigned int state; ++ ++ state = aa_dfa_match(dfa, start, mntpnt); ++ state = aa_dfa_null_transition(dfa, state); ++ if (!state) ++ return 1; ++ ++ if (devname) ++ state = aa_dfa_match(dfa, state, devname); ++ state = aa_dfa_null_transition(dfa, state); ++ if (!state) ++ return 2; ++ ++ if (type) ++ state = aa_dfa_match(dfa, state, type); ++ state = aa_dfa_null_transition(dfa, state); ++ if (!state) ++ return 3; ++ ++ state = match_mnt_flags(dfa, state, flags); ++ if (!state) ++ return 4; ++ *perms = compute_mnt_perms(dfa, state); ++ if (perms->allow & AA_MAY_MOUNT) ++ return 0; ++ ++ /* only match data if not binary and the DFA flags data is expected */ ++ if (data && !binary && (perms->allow & AA_CONT_MATCH)) { ++ state = aa_dfa_null_transition(dfa, state); ++ if (!state) ++ return 4; ++ ++ state = aa_dfa_match(dfa, state, data); ++ if (!state) ++ return 5; ++ *perms = compute_mnt_perms(dfa, state); ++ if (perms->allow & AA_MAY_MOUNT) ++ return 0; ++ } ++ ++ /* failed at end of flags match */ ++ return 4; ++} ++ ++/** ++ * match_mnt - handle path matching for mount ++ * @profile: the confining profile ++ * @mntpnt: string for the mntpnt (NOT NULL) ++ * @devname: string for the devname/src_name (MAYBE NULL) ++ * @type: string for the dev type (MAYBE NULL) ++ * @flags: mount flags to match ++ * @data: fs mount data (MAYBE NULL) ++ * @binary: whether @data is binary ++ * @perms: Returns: permission found by the match ++ * @info: Returns: infomation string about the match for logging ++ * ++ * Returns: 0 on success else error ++ */ ++static int match_mnt(struct aa_profile *profile, const char *mntpnt, ++ const char *devname, const char *type, ++ unsigned long flags, void *data, bool binary, ++ struct file_perms *perms, const char **info) ++{ ++ int pos; ++ ++ if (!profile->policy.dfa) ++ return -EACCES; ++ ++ pos = do_match_mnt(profile->policy.dfa, ++ profile->policy.start[AA_CLASS_MOUNT], ++ mntpnt, devname, type, flags, data, binary, perms); ++ if (pos) { ++ *info = mnt_info_table[pos]; ++ return -EACCES; ++ } ++ ++ return 0; ++} ++ ++static int path_flags(struct aa_profile *profile, const struct path *path) ++{ ++ return profile->path_flags | ++ S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0; ++} ++ ++int aa_remount(struct aa_profile *profile, const struct path *path, ++ unsigned long flags, void *data) ++{ ++ struct file_perms perms = { }; ++ const char *name, *info = NULL; ++ char *buffer = NULL; ++ int binary, error; ++ ++ binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; ++ ++ error = aa_path_name(path, path_flags(profile, path), &buffer, &name, ++ &info); ++ if (error) ++ goto audit; ++ ++ error = match_mnt(profile, name, NULL, NULL, flags, data, binary, ++ &perms, &info); ++ ++audit: ++ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, ++ NULL, flags, data, AA_MAY_MOUNT, &perms, info, ++ error); ++ kfree(buffer); ++ ++ return error; ++} ++ ++int aa_bind_mount(struct aa_profile *profile, const struct path *path, ++ const char *dev_name, unsigned long flags) ++{ ++ struct file_perms perms = { }; ++ char *buffer = NULL, *old_buffer = NULL; ++ const char *name, *old_name = NULL, *info = NULL; ++ struct path old_path; ++ int error; ++ ++ if (!dev_name || !*dev_name) ++ return -EINVAL; ++ ++ flags &= MS_REC | MS_BIND; ++ ++ error = aa_path_name(path, path_flags(profile, path), &buffer, &name, ++ &info); ++ if (error) ++ goto audit; ++ ++ error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); ++ if (error) ++ goto audit; ++ ++ error = aa_path_name(&old_path, path_flags(profile, &old_path), ++ &old_buffer, &old_name, &info); ++ path_put(&old_path); ++ if (error) ++ goto audit; ++ ++ error = match_mnt(profile, name, old_name, NULL, flags, NULL, 0, ++ &perms, &info); ++ ++audit: ++ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, ++ NULL, NULL, flags, NULL, AA_MAY_MOUNT, &perms, ++ info, error); ++ kfree(buffer); ++ kfree(old_buffer); ++ ++ return error; ++} ++ ++int aa_mount_change_type(struct aa_profile *profile, const struct path *path, ++ unsigned long flags) ++{ ++ struct file_perms perms = { }; ++ char *buffer = NULL; ++ const char *name, *info = NULL; ++ int error; ++ ++ /* These are the flags allowed by do_change_type() */ ++ flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | ++ MS_UNBINDABLE); ++ ++ error = aa_path_name(path, path_flags(profile, path), &buffer, &name, ++ &info); ++ if (error) ++ goto audit; ++ ++ error = match_mnt(profile, name, NULL, NULL, flags, NULL, 0, &perms, ++ &info); ++ ++audit: ++ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, NULL, NULL, ++ NULL, flags, NULL, AA_MAY_MOUNT, &perms, info, ++ error); ++ kfree(buffer); ++ ++ return error; ++} ++ ++int aa_move_mount(struct aa_profile *profile, const struct path *path, ++ const char *orig_name) ++{ ++ struct file_perms perms = { }; ++ char *buffer = NULL, *old_buffer = NULL; ++ const char *name, *old_name = NULL, *info = NULL; ++ struct path old_path; ++ int error; ++ ++ if (!orig_name || !*orig_name) ++ return -EINVAL; ++ ++ error = aa_path_name(path, path_flags(profile, path), &buffer, &name, ++ &info); ++ if (error) ++ goto audit; ++ ++ error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); ++ if (error) ++ goto audit; ++ ++ error = aa_path_name(&old_path, path_flags(profile, &old_path), ++ &old_buffer, &old_name, &info); ++ path_put(&old_path); ++ if (error) ++ goto audit; ++ ++ error = match_mnt(profile, name, old_name, NULL, MS_MOVE, NULL, 0, ++ &perms, &info); ++ ++audit: ++ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, old_name, ++ NULL, NULL, MS_MOVE, NULL, AA_MAY_MOUNT, &perms, ++ info, error); ++ kfree(buffer); ++ kfree(old_buffer); ++ ++ return error; ++} ++ ++int aa_new_mount(struct aa_profile *profile, const char *orig_dev_name, ++ const struct path *path, const char *type, unsigned long flags, ++ void *data) ++{ ++ struct file_perms perms = { }; ++ char *buffer = NULL, *dev_buffer = NULL; ++ const char *name = NULL, *dev_name = NULL, *info = NULL; ++ int binary = 1; ++ int error; ++ ++ dev_name = orig_dev_name; ++ if (type) { ++ int requires_dev; ++ struct file_system_type *fstype = get_fs_type(type); ++ if (!fstype) ++ return -ENODEV; ++ ++ binary = fstype->fs_flags & FS_BINARY_MOUNTDATA; ++ requires_dev = fstype->fs_flags & FS_REQUIRES_DEV; ++ put_filesystem(fstype); ++ ++ if (requires_dev) { ++ struct path dev_path; ++ ++ if (!dev_name || !*dev_name) { ++ error = -ENOENT; ++ goto out; ++ } ++ ++ error = kern_path(dev_name, LOOKUP_FOLLOW, &dev_path); ++ if (error) ++ goto audit; ++ ++ error = aa_path_name(&dev_path, ++ path_flags(profile, &dev_path), ++ &dev_buffer, &dev_name, &info); ++ path_put(&dev_path); ++ if (error) ++ goto audit; ++ } ++ } ++ ++ error = aa_path_name(path, path_flags(profile, path), &buffer, &name, ++ &info); ++ if (error) ++ goto audit; ++ ++ error = match_mnt(profile, name, dev_name, type, flags, data, binary, ++ &perms, &info); ++ ++audit: ++ error = audit_mount(profile, GFP_KERNEL, OP_MOUNT, name, dev_name, ++ type, NULL, flags, data, AA_MAY_MOUNT, &perms, info, ++ error); ++ kfree(buffer); ++ kfree(dev_buffer); ++ ++out: ++ return error; ++ ++} ++ ++int aa_umount(struct aa_profile *profile, struct vfsmount *mnt, int flags) ++{ ++ struct file_perms perms = { }; ++ char *buffer = NULL; ++ const char *name, *info = NULL; ++ int error; ++ ++ struct path path = { mnt, mnt->mnt_root }; ++ error = aa_path_name(&path, path_flags(profile, &path), &buffer, &name, ++ &info); ++ if (error) ++ goto audit; ++ ++ if (!error && profile->policy.dfa) { ++ unsigned int state; ++ state = aa_dfa_match(profile->policy.dfa, ++ profile->policy.start[AA_CLASS_MOUNT], ++ name); ++ perms = compute_mnt_perms(profile->policy.dfa, state); ++ } ++ ++ if (AA_MAY_UMOUNT & ~perms.allow) ++ error = -EACCES; ++ ++audit: ++ error = audit_mount(profile, GFP_KERNEL, OP_UMOUNT, name, NULL, NULL, ++ NULL, 0, NULL, AA_MAY_UMOUNT, &perms, info, error); ++ kfree(buffer); ++ ++ return error; ++} ++ ++int aa_pivotroot(struct aa_profile *profile, const struct path *old_path, ++ const struct path *new_path) ++{ ++ struct file_perms perms = { }; ++ struct aa_profile *target = NULL; ++ char *old_buffer = NULL, *new_buffer = NULL; ++ const char *old_name, *new_name = NULL, *info = NULL; ++ int error; ++ ++ error = aa_path_name(old_path, path_flags(profile, old_path), ++ &old_buffer, &old_name, &info); ++ if (error) ++ goto audit; ++ ++ error = aa_path_name(new_path, path_flags(profile, new_path), ++ &new_buffer, &new_name, &info); ++ if (error) ++ goto audit; ++ ++ if (profile->policy.dfa) { ++ unsigned int state; ++ state = aa_dfa_match(profile->policy.dfa, ++ profile->policy.start[AA_CLASS_MOUNT], ++ new_name); ++ state = aa_dfa_null_transition(profile->policy.dfa, state); ++ state = aa_dfa_match(profile->policy.dfa, state, old_name); ++ perms = compute_mnt_perms(profile->policy.dfa, state); ++ } ++ ++ if (AA_MAY_PIVOTROOT & perms.allow) { ++ if ((perms.xindex & AA_X_TYPE_MASK) == AA_X_TABLE) { ++ target = x_table_lookup(profile, perms.xindex); ++ if (!target) ++ error = -ENOENT; ++ else ++ error = aa_replace_current_profile(target); ++ } ++ } else ++ error = -EACCES; ++ ++audit: ++ error = audit_mount(profile, GFP_KERNEL, OP_PIVOTROOT, new_name, ++ old_name, NULL, target ? target->base.name : NULL, ++ 0, NULL, AA_MAY_PIVOTROOT, &perms, info, error); ++ aa_put_profile(target); ++ kfree(old_buffer); ++ kfree(new_buffer); ++ ++ return error; ++} +diff --git a/security/apparmor/path.c b/security/apparmor/path.c +index 596f799..a8fc7d0 100644 +--- a/security/apparmor/path.c ++++ b/security/apparmor/path.c +@@ -84,7 +84,7 @@ static int disconnect(const struct path *path, char *buf, char **name, + * When no error the path name is returned in @name which points to + * to a position in @buf + */ +-static int d_namespace_path(struct path *path, char *buf, int buflen, ++static int d_namespace_path(const struct path *path, char *buf, int buflen, + char **name, int flags) + { + char *res; +@@ -169,7 +169,7 @@ out: + * + * Returns: %0 else error on failure + */ +-static int get_name_to_buffer(struct path *path, int flags, char *buffer, ++static int get_name_to_buffer(const struct path *path, int flags, char *buffer, + int size, char **name, const char **info) + { + int adjust = (flags & PATH_IS_DIR) ? 1 : 0; +@@ -215,8 +215,8 @@ static int get_name_to_buffer(struct path *path, int flags, char *buffer, + * + * Returns: %0 else error code if could retrieve name + */ +-int aa_path_name(struct path *path, int flags, char **buffer, const char **name, +- const char **info) ++int aa_path_name(const struct path *path, int flags, char **buffer, ++ const char **name, const char **info) + { + char *buf, *str = NULL; + int size = 256; +-- +2.7.4 + diff --git a/kernel-patches/4.6/0027-UBUNTU-SAUCE-AppArmor-fix-boolreturn.cocci-warnings.patch b/kernel-patches/4.6/0027-UBUNTU-SAUCE-AppArmor-fix-boolreturn.cocci-warnings.patch new file mode 100644 index 000000000..01ebdf75d --- /dev/null +++ b/kernel-patches/4.6/0027-UBUNTU-SAUCE-AppArmor-fix-boolreturn.cocci-warnings.patch @@ -0,0 +1,180 @@ +From 1eff686074a6af0cf47fc24c45ebb001c570a98b Mon Sep 17 00:00:00 2001 +From: kbuild test robot +Date: Fri, 29 Jul 2016 12:44:43 +0800 +Subject: [PATCH 27/27] UBUNTU: SAUCE: AppArmor: fix boolreturn.cocci warnings + +security/apparmor/policy_unpack.c:143:9-10: WARNING: return of 0/1 in function 'unpack_X' with return type bool +security/apparmor/policy_unpack.c:189:9-10: WARNING: return of 0/1 in function 'unpack_nameX' with return type bool +security/apparmor/policy_unpack.c:475:8-9: WARNING: return of 0/1 in function 'unpack_rlimits' with return type bool +security/apparmor/policy_unpack.c:440:8-9: WARNING: return of 0/1 in function 'unpack_trans_table' with return type bool +security/apparmor/policy_unpack.c:200:10-11: WARNING: return of 0/1 in function 'unpack_u16' with return type bool +security/apparmor/policy_unpack.c:213:10-11: WARNING: return of 0/1 in function 'unpack_u32' with return type bool +security/apparmor/policy_unpack.c:226:10-11: WARNING: return of 0/1 in function 'unpack_u64' with return type bool +security/apparmor/policy_unpack.c:325:10-11: WARNING: return of 0/1 in function 'verify_accept' with return type bool +security/apparmor/policy_unpack.c:739:10-11: WARNING: return of 0/1 in function 'verify_dfa_xindex' with return type bool +security/apparmor/policy_unpack.c:729:9-10: WARNING: return of 0/1 in function 'verify_xindex' with return type bool + + Return statements in functions returning bool should use + true/false instead of 1/0. +Generated by: scripts/coccinelle/misc/boolreturn.cocci +Signed-off-by: Fengguang Wu +Signed-off-by: John Johansen +--- + security/apparmor/policy_unpack.c | 52 +++++++++++++++++++-------------------- + 1 file changed, 26 insertions(+), 26 deletions(-) + +diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c +index 0107bc4..af14626 100644 +--- a/security/apparmor/policy_unpack.c ++++ b/security/apparmor/policy_unpack.c +@@ -140,11 +140,11 @@ static size_t unpack_u16_chunk(struct aa_ext *e, char **chunk) + static bool unpack_X(struct aa_ext *e, enum aa_code code) + { + if (!inbounds(e, 1)) +- return 0; ++ return false; + if (*(u8 *) e->pos != code) +- return 0; ++ return false; + e->pos++; +- return 1; ++ return true; + } + + /** +@@ -186,50 +186,50 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) + + /* now check if type code matches */ + if (unpack_X(e, code)) +- return 1; ++ return true; + + fail: + e->pos = pos; +- return 0; ++ return false; + } + + static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) + { + if (unpack_nameX(e, AA_U16, name)) { + if (!inbounds(e, sizeof(u16))) +- return 0; ++ return false; + if (data) + *data = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); +- return 1; ++ return true; + } +- return 0; ++ return false; + } + + static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) + { + if (unpack_nameX(e, AA_U32, name)) { + if (!inbounds(e, sizeof(u32))) +- return 0; ++ return false; + if (data) + *data = le32_to_cpu(get_unaligned((u32 *) e->pos)); + e->pos += sizeof(u32); +- return 1; ++ return true; + } +- return 0; ++ return false; + } + + static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name) + { + if (unpack_nameX(e, AA_U64, name)) { + if (!inbounds(e, sizeof(u64))) +- return 0; ++ return false; + if (data) + *data = le64_to_cpu(get_unaligned((u64 *) e->pos)); + e->pos += sizeof(u64); +- return 1; ++ return true; + } +- return 0; ++ return false; + } + + static size_t unpack_array(struct aa_ext *e, const char *name) +@@ -322,12 +322,12 @@ static bool verify_accept(struct aa_dfa *dfa, int flags) + int mode = ACCEPT_TABLE(dfa)[i]; + + if (mode & ~DFA_VALID_PERM_MASK) +- return 0; ++ return false; + + if (ACCEPT_TABLE2(dfa)[i] & ~DFA_VALID_PERM2_MASK) +- return 0; ++ return false; + } +- return 1; ++ return true; + } + + /** +@@ -437,12 +437,12 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + goto fail; + } +- return 1; ++ return true; + + fail: + aa_free_domain_entries(&profile->file.trans); + e->pos = pos; +- return 0; ++ return false; + } + + static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) +@@ -472,11 +472,11 @@ static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + goto fail; + } +- return 1; ++ return true; + + fail: + e->pos = pos; +- return 0; ++ return false; + } + + /** +@@ -726,8 +726,8 @@ static bool verify_xindex(int xindex, int table_size) + xtype = xindex & AA_X_TYPE_MASK; + index = xindex & AA_X_INDEX_MASK; + if (xtype == AA_X_TABLE && index >= table_size) +- return 0; +- return 1; ++ return false; ++ return true; + } + + /* verify dfa xindexes are in range of transition tables */ +@@ -736,11 +736,11 @@ static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size) + int i; + for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { + if (!verify_xindex(dfa_user_xindex(dfa, i), table_size)) +- return 0; ++ return false; + if (!verify_xindex(dfa_other_xindex(dfa, i), table_size)) +- return 0; ++ return false; + } +- return 1; ++ return true; + } + + /** +-- +2.7.4 +