From 6260deec9a8e64e929dbe18f4e9b44e7fca9b077 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 11 Sep 2017 09:16:39 -0700 Subject: [PATCH] Add the patches for 4.13 and 4.14. These are based on security-next for 4.14 The old out of tree patchseries has been completely dropped. v4.13 has most of the newer apparmor 3.x code in it. v4.14 has the rest except the af_unix mediation which is included as the last patch --- ...-lockdown-MODSIGN-Fix-module-signatu.patch | 41 + ...dowed-local-variable-in-unpack_trans.patch | 51 + ...r-Fix-logical-error-in-verify_header.patch | 32 + ...mor-Fix-an-error-code-in-aafs_create.patch | 37 + ...nt-condition-prev_ns.-in-label.c-149.patch | 29 + ...r-add-the-ability-to-mediate-signals.patch | 397 +++++ .../0007-apparmor-add-mount-mediation.patch | 1051 +++++++++++++ ...-conditional-check-for-label-in-labe.patch | 71 + ...port-for-absolute-root-view-based-la.patch | 63 + ...licy_unpack-able-to-audit-different-.patch | 219 +++ ...add-more-debug-asserts-to-apparmorfs.patch | 78 + ...e-infastructure-for-socket-mediation.patch | 1123 +++++++++++++ ...w_null_profile-to-after-profile-look.patch | 194 +++ ...e-condition-in-null-profile-creation.patch | 60 + ...unconfined-profiles-have-dfas-initia.patch | 36 + ...orrect-type-assignment-when-freeing-.patch | 39 + ...NTU-SAUCE-apparmor-af_unix-mediation.patch | 1394 +++++++++++++++++ kernel-patches/v4.13/README | 5 + ...NTU-SAUCE-apparmor-af_unix-mediation.patch | 1394 +++++++++++++++++ kernel-patches/v4.14/README | 4 + 20 files changed, 6318 insertions(+) create mode 100644 kernel-patches/v4.13/0001-UBUNTU-SAUCE-efi-lockdown-MODSIGN-Fix-module-signatu.patch create mode 100644 kernel-patches/v4.13/0002-apparmor-Fix-shadowed-local-variable-in-unpack_trans.patch create mode 100644 kernel-patches/v4.13/0003-apparmor-Fix-logical-error-in-verify_header.patch create mode 100644 kernel-patches/v4.13/0004-apparmor-Fix-an-error-code-in-aafs_create.patch create mode 100644 kernel-patches/v4.13/0005-apparmor-Redundant-condition-prev_ns.-in-label.c-149.patch create mode 100644 kernel-patches/v4.13/0006-apparmor-add-the-ability-to-mediate-signals.patch create mode 100644 kernel-patches/v4.13/0007-apparmor-add-mount-mediation.patch create mode 100644 kernel-patches/v4.13/0008-apparmor-cleanup-conditional-check-for-label-in-labe.patch create mode 100644 kernel-patches/v4.13/0009-apparmor-add-support-for-absolute-root-view-based-la.patch create mode 100644 kernel-patches/v4.13/0010-apparmor-make-policy_unpack-able-to-audit-different-.patch create mode 100644 kernel-patches/v4.13/0011-apparmor-add-more-debug-asserts-to-apparmorfs.patch create mode 100644 kernel-patches/v4.13/0012-apparmor-add-base-infastructure-for-socket-mediation.patch create mode 100644 kernel-patches/v4.13/0013-apparmor-move-new_null_profile-to-after-profile-look.patch create mode 100644 kernel-patches/v4.13/0014-apparmor-fix-race-condition-in-null-profile-creation.patch create mode 100644 kernel-patches/v4.13/0015-apparmor-ensure-unconfined-profiles-have-dfas-initia.patch create mode 100644 kernel-patches/v4.13/0016-apparmor-fix-incorrect-type-assignment-when-freeing-.patch create mode 100644 kernel-patches/v4.13/0017-UBUNTU-SAUCE-apparmor-af_unix-mediation.patch create mode 100644 kernel-patches/v4.13/README create mode 100644 kernel-patches/v4.14/0001-UBUNTU-SAUCE-apparmor-af_unix-mediation.patch create mode 100644 kernel-patches/v4.14/README diff --git a/kernel-patches/v4.13/0001-UBUNTU-SAUCE-efi-lockdown-MODSIGN-Fix-module-signatu.patch b/kernel-patches/v4.13/0001-UBUNTU-SAUCE-efi-lockdown-MODSIGN-Fix-module-signatu.patch new file mode 100644 index 000000000..6401a552e --- /dev/null +++ b/kernel-patches/v4.13/0001-UBUNTU-SAUCE-efi-lockdown-MODSIGN-Fix-module-signatu.patch @@ -0,0 +1,41 @@ +From 00c72bc198aa85e5da02de2c0c4cc423c82a54f1 Mon Sep 17 00:00:00 2001 +From: Fedora Kernel Team +Date: Thu, 3 Aug 2017 13:46:51 -0500 +Subject: [PATCH 01/17] UBUNTU: SAUCE: (efi-lockdown) MODSIGN: Fix module + signature verification + +BugLink: http://bugs.launchpad.net/bugs/1712168 + +Currently mod_verify_sig() calls verify_pkcs_7_signature() with +trusted_keys=NULL, which causes only the builtin keys to be used +to verify the signature. This breaks self-signing of modules with +a MOK, as the MOK is loaded into the secondary trusted keyring. +Fix this by passing the spacial value trusted_keys=(void *)1UL, +which tells verify_pkcs_7_signature() to use the secondary +keyring instead. + +(cherry picked from commit cff4523d65b848f9c41c9e998a735ae2a820da2d + git://git.kernel.org/pub/scm/linux/kernel/git/jwboyer/fedora.git) +[ saf: Taken from fedora commit without authorship information or much + of a commit message; modified so that commit will describe the + problem being fixed. ] +Signed-off-by: Seth Forshee +--- + kernel/module_signing.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/kernel/module_signing.c b/kernel/module_signing.c +index 937c844bee4a..d3d6f95a96b4 100644 +--- a/kernel/module_signing.c ++++ b/kernel/module_signing.c +@@ -81,6 +81,6 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen) + } + + return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len, +- NULL, VERIFYING_MODULE_SIGNATURE, ++ (void *)1UL, VERIFYING_MODULE_SIGNATURE, + NULL, NULL); + } +-- +2.11.0 + diff --git a/kernel-patches/v4.13/0002-apparmor-Fix-shadowed-local-variable-in-unpack_trans.patch b/kernel-patches/v4.13/0002-apparmor-Fix-shadowed-local-variable-in-unpack_trans.patch new file mode 100644 index 000000000..4d9836d94 --- /dev/null +++ b/kernel-patches/v4.13/0002-apparmor-Fix-shadowed-local-variable-in-unpack_trans.patch @@ -0,0 +1,51 @@ +From c6cad5e65a23dcafa1821ca381901297664d9c64 Mon Sep 17 00:00:00 2001 +From: Geert Uytterhoeven +Date: Thu, 6 Jul 2017 10:56:21 +0200 +Subject: [PATCH 02/17] apparmor: Fix shadowed local variable in + unpack_trans_table() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +with W=2: + + security/apparmor/policy_unpack.c: In function ‘unpack_trans_table’: + security/apparmor/policy_unpack.c:469: warning: declaration of ‘pos’ shadows a previous local + security/apparmor/policy_unpack.c:451: warning: shadowed declaration is here + +Rename the old "pos" to "saved_pos" to fix this. + +Fixes: 5379a3312024a8be ("apparmor: support v7 transition format compatible with label_parse") +Signed-off-by: Geert Uytterhoeven +Reviewed-by: Serge Hallyn +Signed-off-by: John Johansen +(cherry picked from commit 966d631935a578fadb5770f17a957ee1a969d868) +--- + security/apparmor/policy_unpack.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c +index c600f4dd1783..2d5a1a007b06 100644 +--- a/security/apparmor/policy_unpack.c ++++ b/security/apparmor/policy_unpack.c +@@ -448,7 +448,7 @@ static struct aa_dfa *unpack_dfa(struct aa_ext *e) + */ + static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) + { +- void *pos = e->pos; ++ void *saved_pos = e->pos; + + /* exec table is optional */ + if (unpack_nameX(e, AA_STRUCT, "xtable")) { +@@ -511,7 +511,7 @@ static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) + + fail: + aa_free_domain_entries(&profile->file.trans); +- e->pos = pos; ++ e->pos = saved_pos; + return 0; + } + +-- +2.11.0 + diff --git a/kernel-patches/v4.13/0003-apparmor-Fix-logical-error-in-verify_header.patch b/kernel-patches/v4.13/0003-apparmor-Fix-logical-error-in-verify_header.patch new file mode 100644 index 000000000..be4f854c3 --- /dev/null +++ b/kernel-patches/v4.13/0003-apparmor-Fix-logical-error-in-verify_header.patch @@ -0,0 +1,32 @@ +From 9934296cba701d429a0fc0cf071a40c8c3a1587e Mon Sep 17 00:00:00 2001 +From: Christos Gkekas +Date: Sat, 8 Jul 2017 20:50:21 +0100 +Subject: [PATCH 03/17] apparmor: Fix logical error in verify_header() + +verify_header() is currently checking whether interface version is less +than 5 *and* greater than 7, which always evaluates to false. Instead it +should check whether it is less than 5 *or* greater than 7. + +Signed-off-by: Christos Gkekas +Signed-off-by: John Johansen +(cherry picked from commit c54a2175e3a6bf6c697d249bba1aa729e06c7ba8) +--- + 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 2d5a1a007b06..bda0dce3b582 100644 +--- a/security/apparmor/policy_unpack.c ++++ b/security/apparmor/policy_unpack.c +@@ -832,7 +832,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns) + * if not specified use previous version + * Mask off everything that is not kernel abi version + */ +- if (VERSION_LT(e->version, v5) && VERSION_GT(e->version, v7)) { ++ if (VERSION_LT(e->version, v5) || VERSION_GT(e->version, v7)) { + audit_iface(NULL, NULL, NULL, "unsupported interface version", + e, error); + return error; +-- +2.11.0 + diff --git a/kernel-patches/v4.13/0004-apparmor-Fix-an-error-code-in-aafs_create.patch b/kernel-patches/v4.13/0004-apparmor-Fix-an-error-code-in-aafs_create.patch new file mode 100644 index 000000000..4e937e00c --- /dev/null +++ b/kernel-patches/v4.13/0004-apparmor-Fix-an-error-code-in-aafs_create.patch @@ -0,0 +1,37 @@ +From 8b3851c7b83f32f2be9d4b48371ddf033afedf62 Mon Sep 17 00:00:00 2001 +From: Dan Carpenter +Date: Thu, 13 Jul 2017 10:39:20 +0300 +Subject: [PATCH 04/17] apparmor: Fix an error code in aafs_create() + +We accidentally forgot to set the error code on this path. It means we +return NULL instead of an error pointer. I looked through a bunch of +callers and I don't think it really causes a big issue, but the +documentation says we're supposed to return error pointers here. + +Signed-off-by: Dan Carpenter +Acked-by: Serge Hallyn +Signed-off-by: John Johansen +(cherry picked from commit aee58bf341db52a3a3563c6b972bfd4fc2d41e46) +--- + security/apparmor/apparmorfs.c | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c +index 853c2ec8e0c9..2caeb748070c 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -248,8 +248,10 @@ static struct dentry *aafs_create(const char *name, umode_t mode, + + inode_lock(dir); + dentry = lookup_one_len(name, parent, strlen(name)); +- if (IS_ERR(dentry)) ++ if (IS_ERR(dentry)) { ++ error = PTR_ERR(dentry); + goto fail_lock; ++ } + + if (d_really_is_positive(dentry)) { + error = -EEXIST; +-- +2.11.0 + diff --git a/kernel-patches/v4.13/0005-apparmor-Redundant-condition-prev_ns.-in-label.c-149.patch b/kernel-patches/v4.13/0005-apparmor-Redundant-condition-prev_ns.-in-label.c-149.patch new file mode 100644 index 000000000..beea3de99 --- /dev/null +++ b/kernel-patches/v4.13/0005-apparmor-Redundant-condition-prev_ns.-in-label.c-149.patch @@ -0,0 +1,29 @@ +From 4b56e146905bbad2c79ea92e3f49e210ca527572 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Mon, 31 Jul 2017 23:44:37 -0700 +Subject: [PATCH 05/17] apparmor: Redundant condition: prev_ns. in + [label.c:1498] + +Reported-by: David Binderman +Signed-off-by: John Johansen +(cherry picked from commit d323d2c17cfcc54b6845bfc1d13bca5cef210fc7) +--- + security/apparmor/label.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/security/apparmor/label.c b/security/apparmor/label.c +index e052eaba1cf6..e324f4df3e34 100644 +--- a/security/apparmor/label.c ++++ b/security/apparmor/label.c +@@ -1495,7 +1495,7 @@ static int aa_profile_snxprint(char *str, size_t size, struct aa_ns *view, + view = profiles_ns(profile); + + if (view != profile->ns && +- (!prev_ns || (prev_ns && *prev_ns != profile->ns))) { ++ (!prev_ns || (*prev_ns != profile->ns))) { + if (prev_ns) + *prev_ns = profile->ns; + ns_name = aa_ns_name(view, profile->ns, +-- +2.11.0 + diff --git a/kernel-patches/v4.13/0006-apparmor-add-the-ability-to-mediate-signals.patch b/kernel-patches/v4.13/0006-apparmor-add-the-ability-to-mediate-signals.patch new file mode 100644 index 000000000..ee3672db2 --- /dev/null +++ b/kernel-patches/v4.13/0006-apparmor-add-the-ability-to-mediate-signals.patch @@ -0,0 +1,397 @@ +From f9e20353a6c5726775867db81b6085e8ab425a36 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Tue, 18 Jul 2017 22:56:22 -0700 +Subject: [PATCH 06/17] apparmor: add the ability to mediate signals + +Add signal mediation where the signal can be mediated based on the +signal, direction, or the label or the peer/target. The signal perms +are verified on a cross check to ensure policy consistency in the case +of incremental policy load/replacement. + +The optimization of skipping the cross check when policy is guaranteed +to be consistent (single compile unit) remains to be done. + +policy rules have the form of + SIGNAL_RULE = [ QUALIFIERS ] 'signal' [ SIGNAL ACCESS PERMISSIONS ] + [ SIGNAL SET ] [ SIGNAL PEER ] + + SIGNAL ACCESS PERMISSIONS = SIGNAL ACCESS | SIGNAL ACCESS LIST + + SIGNAL ACCESS LIST = '(' Comma or space separated list of SIGNAL + ACCESS ')' + + SIGNAL ACCESS = ( 'r' | 'w' | 'rw' | 'read' | 'write' | 'send' | + 'receive' ) + + SIGNAL SET = 'set' '=' '(' SIGNAL LIST ')' + + SIGNAL LIST = Comma or space separated list of SIGNALS + + SIGNALS = ( 'hup' | 'int' | 'quit' | 'ill' | 'trap' | 'abrt' | + 'bus' | 'fpe' | 'kill' | 'usr1' | 'segv' | 'usr2' | + 'pipe' | 'alrm' | 'term' | 'stkflt' | 'chld' | 'cont' | + 'stop' | 'stp' | 'ttin' | 'ttou' | 'urg' | 'xcpu' | + 'xfsz' | 'vtalrm' | 'prof' | 'winch' | 'io' | 'pwr' | + 'sys' | 'emt' | 'exists' | 'rtmin+0' ... 'rtmin+32' + ) + + SIGNAL PEER = 'peer' '=' AARE + +eg. + signal, # allow all signals + signal send set=(hup, kill) peer=foo, + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +(cherry picked from commit c6bf1adaecaa719d7c56338cc43b2982214f2f44) +--- + security/apparmor/apparmorfs.c | 7 +++ + security/apparmor/include/apparmor.h | 1 + + security/apparmor/include/audit.h | 2 + + security/apparmor/include/ipc.h | 6 +++ + security/apparmor/include/sig_names.h | 95 +++++++++++++++++++++++++++++++++ + security/apparmor/ipc.c | 99 +++++++++++++++++++++++++++++++++++ + security/apparmor/lsm.c | 21 ++++++++ + 7 files changed, 231 insertions(+) + create mode 100644 security/apparmor/include/sig_names.h + +diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c +index 2caeb748070c..a5f9e1aa51f7 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -32,6 +32,7 @@ + #include "include/audit.h" + #include "include/context.h" + #include "include/crypto.h" ++#include "include/ipc.h" + #include "include/policy_ns.h" + #include "include/label.h" + #include "include/policy.h" +@@ -2129,6 +2130,11 @@ static struct aa_sfs_entry aa_sfs_entry_ptrace[] = { + { } + }; + ++static struct aa_sfs_entry aa_sfs_entry_signal[] = { ++ AA_SFS_FILE_STRING("mask", AA_SFS_SIG_MASK), ++ { } ++}; ++ + static struct aa_sfs_entry aa_sfs_entry_domain[] = { + AA_SFS_FILE_BOOLEAN("change_hat", 1), + AA_SFS_FILE_BOOLEAN("change_hatv", 1), +@@ -2179,6 +2185,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { + AA_SFS_DIR("rlimit", aa_sfs_entry_rlimit), + AA_SFS_DIR("caps", aa_sfs_entry_caps), + AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace), ++ AA_SFS_DIR("signal", aa_sfs_entry_signal), + AA_SFS_DIR("query", aa_sfs_entry_query), + { } + }; +diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h +index aaf893f4e4f5..962a20a75e01 100644 +--- a/security/apparmor/include/apparmor.h ++++ b/security/apparmor/include/apparmor.h +@@ -28,6 +28,7 @@ + #define AA_CLASS_RLIMITS 5 + #define AA_CLASS_DOMAIN 6 + #define AA_CLASS_PTRACE 9 ++#define AA_CLASS_SIGNAL 10 + #define AA_CLASS_LABEL 16 + + #define AA_CLASS_LAST AA_CLASS_LABEL +diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h +index c68839a44351..d9a156ae11b9 100644 +--- a/security/apparmor/include/audit.h ++++ b/security/apparmor/include/audit.h +@@ -86,6 +86,7 @@ enum audit_type { + #define OP_SHUTDOWN "socket_shutdown" + + #define OP_PTRACE "ptrace" ++#define OP_SIGNAL "signal" + + #define OP_EXEC "exec" + +@@ -126,6 +127,7 @@ struct apparmor_audit_data { + long pos; + const char *ns; + } iface; ++ int signal; + struct { + int rlim; + unsigned long max; +diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h +index 656fdb81c8a0..5ffc218d1e74 100644 +--- a/security/apparmor/include/ipc.h ++++ b/security/apparmor/include/ipc.h +@@ -27,8 +27,14 @@ struct aa_profile; + + #define AA_PTRACE_PERM_MASK (AA_PTRACE_READ | AA_PTRACE_TRACE | \ + AA_MAY_BE_READ | AA_MAY_BE_TRACED) ++#define AA_SIGNAL_PERM_MASK (MAY_READ | MAY_WRITE) ++ ++#define AA_SFS_SIG_MASK "hup int quit ill trap abrt bus fpe kill usr1 " \ ++ "segv usr2 pipe alrm term stkflt chld cont stop stp ttin ttou urg " \ ++ "xcpu xfsz vtalrm prof winch io pwr sys emt lost" + + int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, + u32 request); ++int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig); + + #endif /* __AA_IPC_H */ +diff --git a/security/apparmor/include/sig_names.h b/security/apparmor/include/sig_names.h +new file mode 100644 +index 000000000000..0d4395f231ca +--- /dev/null ++++ b/security/apparmor/include/sig_names.h +@@ -0,0 +1,95 @@ ++#include ++ ++#define SIGUNKNOWN 0 ++#define MAXMAPPED_SIG 35 ++/* provide a mapping of arch signal to internal signal # for mediation ++ * those that are always an alias SIGCLD for SIGCLHD and SIGPOLL for SIGIO ++ * map to the same entry those that may/or may not get a separate entry ++ */ ++static const int sig_map[MAXMAPPED_SIG] = { ++ [0] = MAXMAPPED_SIG, /* existence test */ ++ [SIGHUP] = 1, ++ [SIGINT] = 2, ++ [SIGQUIT] = 3, ++ [SIGILL] = 4, ++ [SIGTRAP] = 5, /* -, 5, - */ ++ [SIGABRT] = 6, /* SIGIOT: -, 6, - */ ++ [SIGBUS] = 7, /* 10, 7, 10 */ ++ [SIGFPE] = 8, ++ [SIGKILL] = 9, ++ [SIGUSR1] = 10, /* 30, 10, 16 */ ++ [SIGSEGV] = 11, ++ [SIGUSR2] = 12, /* 31, 12, 17 */ ++ [SIGPIPE] = 13, ++ [SIGALRM] = 14, ++ [SIGTERM] = 15, ++ [SIGSTKFLT] = 16, /* -, 16, - */ ++ [SIGCHLD] = 17, /* 20, 17, 18. SIGCHLD -, -, 18 */ ++ [SIGCONT] = 18, /* 19, 18, 25 */ ++ [SIGSTOP] = 19, /* 17, 19, 23 */ ++ [SIGTSTP] = 20, /* 18, 20, 24 */ ++ [SIGTTIN] = 21, /* 21, 21, 26 */ ++ [SIGTTOU] = 22, /* 22, 22, 27 */ ++ [SIGURG] = 23, /* 16, 23, 21 */ ++ [SIGXCPU] = 24, /* 24, 24, 30 */ ++ [SIGXFSZ] = 25, /* 25, 25, 31 */ ++ [SIGVTALRM] = 26, /* 26, 26, 28 */ ++ [SIGPROF] = 27, /* 27, 27, 29 */ ++ [SIGWINCH] = 28, /* 28, 28, 20 */ ++ [SIGIO] = 29, /* SIGPOLL: 23, 29, 22 */ ++ [SIGPWR] = 30, /* 29, 30, 19. SIGINFO 29, -, - */ ++#ifdef SIGSYS ++ [SIGSYS] = 31, /* 12, 31, 12. often SIG LOST/UNUSED */ ++#endif ++#ifdef SIGEMT ++ [SIGEMT] = 32, /* 7, - , 7 */ ++#endif ++#if defined(SIGLOST) && SIGPWR != SIGLOST /* sparc */ ++ [SIGLOST] = 33, /* unused on Linux */ ++#endif ++#if defined(SIGLOST) && defined(SIGSYS) && SIGLOST != SIGSYS ++ [SIGUNUSED] = 34, /* -, 31, - */ ++#endif ++}; ++ ++/* this table is ordered post sig_map[sig] mapping */ ++static const char *const sig_names[MAXMAPPED_SIG + 1] = { ++ "unknown", ++ "hup", ++ "int", ++ "quit", ++ "ill", ++ "trap", ++ "abrt", ++ "bus", ++ "fpe", ++ "kill", ++ "usr1", ++ "segv", ++ "usr2", ++ "pipe", ++ "alrm", ++ "term", ++ "stkflt", ++ "chld", ++ "cont", ++ "stop", ++ "stp", ++ "ttin", ++ "ttou", ++ "urg", ++ "xcpu", ++ "xfsz", ++ "vtalrm", ++ "prof", ++ "winch", ++ "io", ++ "pwr", ++ "sys", ++ "emt", ++ "lost", ++ "unused", ++ ++ "exists", /* always last existence test mapped to MAXMAPPED_SIG */ ++}; ++ +diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c +index 11e66b5bbc42..66fb9ede9447 100644 +--- a/security/apparmor/ipc.c ++++ b/security/apparmor/ipc.c +@@ -20,6 +20,7 @@ + #include "include/context.h" + #include "include/policy.h" + #include "include/ipc.h" ++#include "include/sig_names.h" + + /** + * audit_ptrace_mask - convert mask to permission string +@@ -121,3 +122,101 @@ int aa_may_ptrace(struct aa_label *tracer, struct aa_label *tracee, + } + + ++static inline int map_signal_num(int sig) ++{ ++ if (sig > SIGRTMAX) ++ return SIGUNKNOWN; ++ else if (sig >= SIGRTMIN) ++ return sig - SIGRTMIN + 128; /* rt sigs mapped to 128 */ ++ else if (sig <= MAXMAPPED_SIG) ++ return sig_map[sig]; ++ return SIGUNKNOWN; ++} ++ ++/** ++ * audit_file_mask - convert mask to permission string ++ * @buffer: buffer to write string to (NOT NULL) ++ * @mask: permission mask to convert ++ */ ++static void audit_signal_mask(struct audit_buffer *ab, u32 mask) ++{ ++ if (mask & MAY_READ) ++ audit_log_string(ab, "receive"); ++ if (mask & MAY_WRITE) ++ audit_log_string(ab, "send"); ++} ++ ++/** ++ * audit_cb - call back for signal specific audit fields ++ * @ab: audit_buffer (NOT NULL) ++ * @va: audit struct to audit values of (NOT NULL) ++ */ ++static void audit_signal_cb(struct audit_buffer *ab, void *va) ++{ ++ struct common_audit_data *sa = va; ++ ++ if (aad(sa)->request & AA_SIGNAL_PERM_MASK) { ++ audit_log_format(ab, " requested_mask="); ++ audit_signal_mask(ab, aad(sa)->request); ++ if (aad(sa)->denied & AA_SIGNAL_PERM_MASK) { ++ audit_log_format(ab, " denied_mask="); ++ audit_signal_mask(ab, aad(sa)->denied); ++ } ++ } ++ if (aad(sa)->signal <= MAXMAPPED_SIG) ++ audit_log_format(ab, " signal=%s", sig_names[aad(sa)->signal]); ++ else ++ audit_log_format(ab, " signal=rtmin+%d", ++ aad(sa)->signal - 128); ++ audit_log_format(ab, " peer="); ++ aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, ++ FLAGS_NONE, GFP_ATOMIC); ++} ++ ++/* TODO: update to handle compound name&name2, conditionals */ ++static void profile_match_signal(struct aa_profile *profile, const char *label, ++ int signal, struct aa_perms *perms) ++{ ++ unsigned int state; ++ ++ /* TODO: secondary cache check */ ++ state = aa_dfa_next(profile->policy.dfa, ++ profile->policy.start[AA_CLASS_SIGNAL], ++ signal); ++ state = aa_dfa_match(profile->policy.dfa, state, label); ++ aa_compute_perms(profile->policy.dfa, state, perms); ++} ++ ++static int profile_signal_perm(struct aa_profile *profile, ++ struct aa_profile *peer, u32 request, ++ struct common_audit_data *sa) ++{ ++ struct aa_perms perms; ++ ++ if (profile_unconfined(profile) || ++ !PROFILE_MEDIATES(profile, AA_CLASS_SIGNAL)) ++ return 0; ++ ++ aad(sa)->peer = &peer->label; ++ profile_match_signal(profile, peer->base.hname, aad(sa)->signal, ++ &perms); ++ aa_apply_modes_to_perms(profile, &perms); ++ return aa_check_perms(profile, &perms, request, sa, audit_signal_cb); ++} ++ ++static int aa_signal_cross_perm(struct aa_profile *sender, ++ struct aa_profile *target, ++ struct common_audit_data *sa) ++{ ++ return xcheck(profile_signal_perm(sender, target, MAY_WRITE, sa), ++ profile_signal_perm(target, sender, MAY_READ, sa)); ++} ++ ++int aa_may_signal(struct aa_label *sender, struct aa_label *target, int sig) ++{ ++ DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, OP_SIGNAL); ++ ++ aad(&sa)->signal = map_signal_num(sig); ++ return xcheck_labels_profiles(sender, target, aa_signal_cross_perm, ++ &sa); ++} +diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c +index 867bcd154c7e..af22f3dfbcce 100644 +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -656,6 +656,26 @@ static int apparmor_task_setrlimit(struct task_struct *task, + return error; + } + ++static int apparmor_task_kill(struct task_struct *target, struct siginfo *info, ++ int sig, u32 secid) ++{ ++ struct aa_label *cl, *tl; ++ int error; ++ ++ if (secid) ++ /* TODO: after secid to label mapping is done. ++ * Dealing with USB IO specific behavior ++ */ ++ return 0; ++ cl = __begin_current_label_crit_section(); ++ tl = aa_get_task_label(target); ++ error = aa_may_signal(cl, tl, sig); ++ aa_put_label(tl); ++ __end_current_label_crit_section(cl); ++ ++ return error; ++} ++ + static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { + LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), + LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), +@@ -697,6 +717,7 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { + LSM_HOOK_INIT(bprm_secureexec, apparmor_bprm_secureexec), + + LSM_HOOK_INIT(task_setrlimit, apparmor_task_setrlimit), ++ LSM_HOOK_INIT(task_kill, apparmor_task_kill), + }; + + /* +-- +2.11.0 + diff --git a/kernel-patches/v4.13/0007-apparmor-add-mount-mediation.patch b/kernel-patches/v4.13/0007-apparmor-add-mount-mediation.patch new file mode 100644 index 000000000..99dac5c17 --- /dev/null +++ b/kernel-patches/v4.13/0007-apparmor-add-mount-mediation.patch @@ -0,0 +1,1051 @@ +From f37356d0a41499f9222f9f2b9c0147b500ae4285 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Tue, 18 Jul 2017 23:04:47 -0700 +Subject: [PATCH 07/17] apparmor: add mount mediation + +Add basic mount mediation. That allows controlling based on basic +mount parameters. It does not include special mount parameters for +apparmor, super block labeling, or any triggers for apparmor namespace +parameter modifications on pivot root. + +default userspace policy rules have the form of + MOUNT RULE = ( MOUNT | REMOUNT | UMOUNT ) + + MOUNT = [ QUALIFIERS ] 'mount' [ MOUNT CONDITIONS ] [ SOURCE FILEGLOB ] + [ '->' MOUNTPOINT FILEGLOB ] + + REMOUNT = [ QUALIFIERS ] 'remount' [ MOUNT CONDITIONS ] + MOUNTPOINT FILEGLOB + + UMOUNT = [ QUALIFIERS ] 'umount' [ MOUNT CONDITIONS ] MOUNTPOINT FILEGLOB + + MOUNT CONDITIONS = [ ( 'fstype' | 'vfstype' ) ( '=' | 'in' ) + MOUNT FSTYPE EXPRESSION ] + [ 'options' ( '=' | 'in' ) MOUNT FLAGS EXPRESSION ] + + MOUNT FSTYPE EXPRESSION = ( MOUNT FSTYPE LIST | MOUNT EXPRESSION ) + + MOUNT FSTYPE LIST = Comma separated list of valid filesystem and + virtual filesystem types (eg ext4, debugfs, etc) + + MOUNT FLAGS EXPRESSION = ( MOUNT FLAGS LIST | MOUNT EXPRESSION ) + + MOUNT FLAGS LIST = Comma separated list of MOUNT FLAGS. + + MOUNT FLAGS = ( 'ro' | 'rw' | 'nosuid' | 'suid' | 'nodev' | 'dev' | + 'noexec' | 'exec' | 'sync' | 'async' | 'remount' | + 'mand' | 'nomand' | 'dirsync' | 'noatime' | 'atime' | + 'nodiratime' | 'diratime' | 'bind' | 'rbind' | 'move' | + 'verbose' | 'silent' | 'loud' | 'acl' | 'noacl' | + 'unbindable' | 'runbindable' | 'private' | 'rprivate' | + 'slave' | 'rslave' | 'shared' | 'rshared' | + 'relatime' | 'norelatime' | 'iversion' | 'noiversion' | + 'strictatime' | 'nouser' | 'user' ) + + MOUNT EXPRESSION = ( ALPHANUMERIC | AARE ) ... + + PIVOT ROOT RULE = [ QUALIFIERS ] pivot_root [ oldroot=OLD PUT FILEGLOB ] + [ NEW ROOT FILEGLOB ] + + SOURCE FILEGLOB = FILEGLOB + + MOUNTPOINT FILEGLOB = FILEGLOB + +eg. + mount, + mount /dev/foo, + mount options=ro /dev/foo -> /mnt/, + mount options in (ro,atime) /dev/foo -> /mnt/, + mount options=ro options=atime, + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +(cherry picked from commit fa488437d0f95b2e5db1e624341fe0d5a233f729) +--- + security/apparmor/Makefile | 2 +- + security/apparmor/apparmorfs.c | 8 +- + security/apparmor/domain.c | 4 +- + security/apparmor/include/apparmor.h | 1 + + security/apparmor/include/audit.h | 11 + + security/apparmor/include/domain.h | 5 + + security/apparmor/include/mount.h | 54 +++ + security/apparmor/lsm.c | 64 ++++ + security/apparmor/mount.c | 696 +++++++++++++++++++++++++++++++++++ + 9 files changed, 841 insertions(+), 4 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 a16b195274de..81a34426d024 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 secid.o file.o policy_ns.o label.o ++ resource.o secid.o file.o policy_ns.o label.o mount.o + apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o + + clean-files := capability_names.h rlim_names.h +diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c +index a5f9e1aa51f7..8fa6c898c44b 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -2159,9 +2159,14 @@ static struct aa_sfs_entry aa_sfs_entry_policy[] = { + { } + }; + ++static struct aa_sfs_entry aa_sfs_entry_mount[] = { ++ AA_SFS_FILE_STRING("mask", "mount umount pivot_root"), ++ { } ++}; ++ + static struct aa_sfs_entry aa_sfs_entry_ns[] = { + AA_SFS_FILE_BOOLEAN("profile", 1), +- AA_SFS_FILE_BOOLEAN("pivot_root", 1), ++ AA_SFS_FILE_BOOLEAN("pivot_root", 0), + { } + }; + +@@ -2180,6 +2185,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { + AA_SFS_DIR("policy", aa_sfs_entry_policy), + AA_SFS_DIR("domain", aa_sfs_entry_domain), + AA_SFS_DIR("file", aa_sfs_entry_file), ++ AA_SFS_DIR("mount", aa_sfs_entry_mount), + AA_SFS_DIR("namespaces", aa_sfs_entry_ns), + AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), + AA_SFS_DIR("rlimit", aa_sfs_entry_rlimit), +diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c +index d0594446ae3f..ffc8c75a6785 100644 +--- a/security/apparmor/domain.c ++++ b/security/apparmor/domain.c +@@ -374,8 +374,8 @@ static const char *next_name(int xtype, const char *name) + * + * Returns: refcounted label, or NULL on failure (MAYBE NULL) + */ +-static struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, +- const char **name) ++struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, ++ const char **name) + { + struct aa_label *label = NULL; + u32 xtype = xindex & AA_X_TYPE_MASK; +diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h +index 962a20a75e01..829082c35faa 100644 +--- a/security/apparmor/include/apparmor.h ++++ b/security/apparmor/include/apparmor.h +@@ -27,6 +27,7 @@ + #define AA_CLASS_NET 4 + #define AA_CLASS_RLIMITS 5 + #define AA_CLASS_DOMAIN 6 ++#define AA_CLASS_MOUNT 7 + #define AA_CLASS_PTRACE 9 + #define AA_CLASS_SIGNAL 10 + #define AA_CLASS_LABEL 16 +diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h +index d9a156ae11b9..c3fe1c5ef3bc 100644 +--- a/security/apparmor/include/audit.h ++++ b/security/apparmor/include/audit.h +@@ -71,6 +71,10 @@ enum audit_type { + #define OP_FMPROT "file_mprotect" + #define OP_INHERIT "file_inherit" + ++#define OP_PIVOTROOT "pivotroot" ++#define OP_MOUNT "mount" ++#define OP_UMOUNT "umount" ++ + #define OP_CREATE "create" + #define OP_POST_CREATE "post_create" + #define OP_BIND "bind" +@@ -132,6 +136,13 @@ struct apparmor_audit_data { + int rlim; + unsigned long max; + } rlim; ++ struct { ++ const char *src_name; ++ const char *type; ++ const char *trans; ++ const char *data; ++ unsigned long flags; ++ } mnt; + }; + }; + +diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h +index bab5810b6e9a..db27403346c5 100644 +--- a/security/apparmor/include/domain.h ++++ b/security/apparmor/include/domain.h +@@ -15,6 +15,8 @@ + #include + #include + ++#include "label.h" ++ + #ifndef __AA_DOMAIN_H + #define __AA_DOMAIN_H + +@@ -29,6 +31,9 @@ struct aa_domain { + #define AA_CHANGE_ONEXEC 4 + #define AA_CHANGE_STACK 8 + ++struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex, ++ const char **name); ++ + int apparmor_bprm_set_creds(struct linux_binprm *bprm); + int apparmor_bprm_secureexec(struct linux_binprm *bprm); + +diff --git a/security/apparmor/include/mount.h b/security/apparmor/include/mount.h +new file mode 100644 +index 000000000000..25d6067fa6ef +--- /dev/null ++++ b/security/apparmor/include/mount.h +@@ -0,0 +1,54 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor file mediation function definitions. ++ * ++ * Copyright 2017 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_MNT_CONT_MATCH 0x40 ++ ++#define AA_MS_IGNORE_MASK (MS_KERNMOUNT | MS_NOSEC | MS_ACTIVE | MS_BORN) ++ ++int aa_remount(struct aa_label *label, const struct path *path, ++ unsigned long flags, void *data); ++ ++int aa_bind_mount(struct aa_label *label, const struct path *path, ++ const char *old_name, unsigned long flags); ++ ++ ++int aa_mount_change_type(struct aa_label *label, const struct path *path, ++ unsigned long flags); ++ ++int aa_move_mount(struct aa_label *label, const struct path *path, ++ const char *old_name); ++ ++int aa_new_mount(struct aa_label *label, const char *dev_name, ++ const struct path *path, const char *type, unsigned long flags, ++ void *data); ++ ++int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags); ++ ++int aa_pivotroot(struct aa_label *label, const struct path *old_path, ++ const struct path *new_path); ++ ++#endif /* __AA_MOUNT_H */ +diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c +index af22f3dfbcce..4ad0b3a45142 100644 +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -38,6 +38,7 @@ + #include "include/policy.h" + #include "include/policy_ns.h" + #include "include/procattr.h" ++#include "include/mount.h" + + /* Flag indicating whether initialization completed */ + int apparmor_initialized; +@@ -511,6 +512,65 @@ 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, const struct path *path, ++ const char *type, unsigned long flags, void *data) ++{ ++ struct aa_label *label; ++ int error = 0; ++ ++ /* Discard magic */ ++ if ((flags & MS_MGC_MSK) == MS_MGC_VAL) ++ flags &= ~MS_MGC_MSK; ++ ++ flags &= ~AA_MS_IGNORE_MASK; ++ ++ label = __begin_current_label_crit_section(); ++ if (!unconfined(label)) { ++ if (flags & MS_REMOUNT) ++ error = aa_remount(label, path, flags, data); ++ else if (flags & MS_BIND) ++ error = aa_bind_mount(label, path, dev_name, flags); ++ else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | ++ MS_UNBINDABLE)) ++ error = aa_mount_change_type(label, path, flags); ++ else if (flags & MS_MOVE) ++ error = aa_move_mount(label, path, dev_name); ++ else ++ error = aa_new_mount(label, dev_name, path, type, ++ flags, data); ++ } ++ __end_current_label_crit_section(label); ++ ++ return error; ++} ++ ++static int apparmor_sb_umount(struct vfsmount *mnt, int flags) ++{ ++ struct aa_label *label; ++ int error = 0; ++ ++ label = __begin_current_label_crit_section(); ++ if (!unconfined(label)) ++ error = aa_umount(label, mnt, flags); ++ __end_current_label_crit_section(label); ++ ++ return error; ++} ++ ++static int apparmor_sb_pivotroot(const struct path *old_path, ++ const struct path *new_path) ++{ ++ struct aa_label *label; ++ int error = 0; ++ ++ label = aa_get_current_label(); ++ if (!unconfined(label)) ++ error = aa_pivotroot(label, old_path, new_path); ++ aa_put_label(label); ++ ++ return error; ++} ++ + static int apparmor_getprocattr(struct task_struct *task, char *name, + char **value) + { +@@ -682,6 +742,10 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { + 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 000000000000..82a64b58041d +--- /dev/null ++++ b/security/apparmor/mount.c +@@ -0,0 +1,696 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor mediation of files ++ * ++ * Copyright (C) 1998-2008 Novell/SUSE ++ * Copyright 2009-2017 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 (aad(sa)->mnt.type) { ++ audit_log_format(ab, " fstype="); ++ audit_log_untrustedstring(ab, aad(sa)->mnt.type); ++ } ++ if (aad(sa)->mnt.src_name) { ++ audit_log_format(ab, " srcname="); ++ audit_log_untrustedstring(ab, aad(sa)->mnt.src_name); ++ } ++ if (aad(sa)->mnt.trans) { ++ audit_log_format(ab, " trans="); ++ audit_log_untrustedstring(ab, aad(sa)->mnt.trans); ++ } ++ if (aad(sa)->mnt.flags) { ++ audit_log_format(ab, " flags=\""); ++ audit_mnt_flags(ab, aad(sa)->mnt.flags); ++ audit_log_format(ab, "\""); ++ } ++ if (aad(sa)->mnt.data) { ++ audit_log_format(ab, " options="); ++ audit_log_untrustedstring(ab, aad(sa)->mnt.data); ++ } ++} ++ ++/** ++ * audit_mount - handle the auditing of mount operations ++ * @profile: the profile being enforced (NOT NULL) ++ * @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, const char *op, ++ const char *name, const char *src_name, ++ const char *type, const char *trans, ++ unsigned long flags, const void *data, u32 request, ++ struct aa_perms *perms, const char *info, int error) ++{ ++ int audit_type = AUDIT_APPARMOR_AUTO; ++ DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, op); ++ ++ 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 error; ++ } ++ ++ aad(&sa)->name = name; ++ aad(&sa)->mnt.src_name = src_name; ++ aad(&sa)->mnt.type = type; ++ aad(&sa)->mnt.trans = trans; ++ aad(&sa)->mnt.flags = flags; ++ if (data && (perms->audit & AA_AUDIT_DATA)) ++ aad(&sa)->mnt.data = data; ++ aad(&sa)->info = info; ++ aad(&sa)->error = error; ++ ++ return aa_audit(audit_type, profile, &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 aa_perms compute_mnt_perms(struct aa_dfa *dfa, ++ unsigned int state) ++{ ++ struct aa_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 aa_perms *perms) ++{ ++ unsigned int state; ++ ++ AA_BUG(!dfa); ++ AA_BUG(!perms); ++ ++ 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_MNT_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; ++} ++ ++ ++static int path_flags(struct aa_profile *profile, const struct path *path) ++{ ++ AA_BUG(!profile); ++ AA_BUG(!path); ++ ++ return profile->path_flags | ++ (S_ISDIR(path->dentry->d_inode->i_mode) ? PATH_IS_DIR : 0); ++} ++ ++/** ++ * match_mnt_path_str - handle path matching for mount ++ * @profile: the confining profile ++ * @mntpath: for the mntpnt (NOT NULL) ++ * @buffer: buffer to be used to lookup mntpath ++ * @devnme: string for the devname/src_name (MAY BE NULL OR ERRPTR) ++ * @type: string for the dev type (MAYBE NULL) ++ * @flags: mount flags to match ++ * @data: fs mount data (MAYBE NULL) ++ * @binary: whether @data is binary ++ * @devinfo: error str if (IS_ERR(@devname)) ++ * ++ * Returns: 0 on success else error ++ */ ++static int match_mnt_path_str(struct aa_profile *profile, ++ const struct path *mntpath, char *buffer, ++ const char *devname, const char *type, ++ unsigned long flags, void *data, bool binary, ++ const char *devinfo) ++{ ++ struct aa_perms perms = { }; ++ const char *mntpnt = NULL, *info = NULL; ++ int pos, error; ++ ++ AA_BUG(!profile); ++ AA_BUG(!mntpath); ++ AA_BUG(!buffer); ++ ++ error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer, ++ &mntpnt, &info, profile->disconnected); ++ if (error) ++ goto audit; ++ if (IS_ERR(devname)) { ++ error = PTR_ERR(devname); ++ devname = NULL; ++ info = devinfo; ++ goto audit; ++ } ++ ++ error = -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]; ++ goto audit; ++ } ++ error = 0; ++ ++audit: ++ return audit_mount(profile, OP_MOUNT, mntpnt, devname, type, NULL, ++ flags, data, AA_MAY_MOUNT, &perms, info, error); ++} ++ ++/** ++ * match_mnt - handle path matching for mount ++ * @profile: the confining profile ++ * @mntpath: for the mntpnt (NOT NULL) ++ * @buffer: buffer to be used to lookup mntpath ++ * @devpath: path devname/src_name (MAYBE NULL) ++ * @devbuffer: buffer to be used to lookup devname/src_name ++ * @type: string for the dev type (MAYBE NULL) ++ * @flags: mount flags to match ++ * @data: fs mount data (MAYBE NULL) ++ * @binary: whether @data is binary ++ * ++ * Returns: 0 on success else error ++ */ ++static int match_mnt(struct aa_profile *profile, const struct path *path, ++ char *buffer, struct path *devpath, char *devbuffer, ++ const char *type, unsigned long flags, void *data, ++ bool binary) ++{ ++ const char *devname = NULL, *info = NULL; ++ int error = -EACCES; ++ ++ AA_BUG(!profile); ++ AA_BUG(devpath && !devbuffer); ++ ++ if (devpath) { ++ error = aa_path_name(devpath, path_flags(profile, devpath), ++ devbuffer, &devname, &info, ++ profile->disconnected); ++ if (error) ++ devname = ERR_PTR(error); ++ } ++ ++ return match_mnt_path_str(profile, path, buffer, devname, type, flags, ++ data, binary, info); ++} ++ ++int aa_remount(struct aa_label *label, const struct path *path, ++ unsigned long flags, void *data) ++{ ++ struct aa_profile *profile; ++ char *buffer = NULL; ++ bool binary; ++ int error; ++ ++ AA_BUG(!label); ++ AA_BUG(!path); ++ ++ binary = path->dentry->d_sb->s_type->fs_flags & FS_BINARY_MOUNTDATA; ++ ++ get_buffers(buffer); ++ error = fn_for_each_confined(label, profile, ++ match_mnt(profile, path, buffer, NULL, NULL, NULL, ++ flags, data, binary)); ++ put_buffers(buffer); ++ ++ return error; ++} ++ ++int aa_bind_mount(struct aa_label *label, const struct path *path, ++ const char *dev_name, unsigned long flags) ++{ ++ struct aa_profile *profile; ++ char *buffer = NULL, *old_buffer = NULL; ++ struct path old_path; ++ int error; ++ ++ AA_BUG(!label); ++ AA_BUG(!path); ++ ++ if (!dev_name || !*dev_name) ++ return -EINVAL; ++ ++ flags &= MS_REC | MS_BIND; ++ ++ error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path); ++ if (error) ++ return error; ++ ++ get_buffers(buffer, old_buffer); ++ error = fn_for_each_confined(label, profile, ++ match_mnt(profile, path, buffer, &old_path, old_buffer, ++ NULL, flags, NULL, false)); ++ put_buffers(buffer, old_buffer); ++ path_put(&old_path); ++ ++ return error; ++} ++ ++int aa_mount_change_type(struct aa_label *label, const struct path *path, ++ unsigned long flags) ++{ ++ struct aa_profile *profile; ++ char *buffer = NULL; ++ int error; ++ ++ AA_BUG(!label); ++ AA_BUG(!path); ++ ++ /* These are the flags allowed by do_change_type() */ ++ flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE | ++ MS_UNBINDABLE); ++ ++ get_buffers(buffer); ++ error = fn_for_each_confined(label, profile, ++ match_mnt(profile, path, buffer, NULL, NULL, NULL, ++ flags, NULL, false)); ++ put_buffers(buffer); ++ ++ return error; ++} ++ ++int aa_move_mount(struct aa_label *label, const struct path *path, ++ const char *orig_name) ++{ ++ struct aa_profile *profile; ++ char *buffer = NULL, *old_buffer = NULL; ++ struct path old_path; ++ int error; ++ ++ AA_BUG(!label); ++ AA_BUG(!path); ++ ++ if (!orig_name || !*orig_name) ++ return -EINVAL; ++ ++ error = kern_path(orig_name, LOOKUP_FOLLOW, &old_path); ++ if (error) ++ return error; ++ ++ get_buffers(buffer, old_buffer); ++ error = fn_for_each_confined(label, profile, ++ match_mnt(profile, path, buffer, &old_path, old_buffer, ++ NULL, MS_MOVE, NULL, false)); ++ put_buffers(buffer, old_buffer); ++ path_put(&old_path); ++ ++ return error; ++} ++ ++int aa_new_mount(struct aa_label *label, const char *dev_name, ++ const struct path *path, const char *type, unsigned long flags, ++ void *data) ++{ ++ struct aa_profile *profile; ++ char *buffer = NULL, *dev_buffer = NULL; ++ bool binary = true; ++ int error; ++ int requires_dev = 0; ++ struct path tmp_path, *dev_path = NULL; ++ ++ AA_BUG(!label); ++ AA_BUG(!path); ++ ++ if (type) { ++ struct file_system_type *fstype; ++ ++ 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) { ++ if (!dev_name || !*dev_name) ++ return -ENOENT; ++ ++ error = kern_path(dev_name, LOOKUP_FOLLOW, &tmp_path); ++ if (error) ++ return error; ++ dev_path = &tmp_path; ++ } ++ } ++ ++ get_buffers(buffer, dev_buffer); ++ if (dev_path) { ++ error = fn_for_each_confined(label, profile, ++ match_mnt(profile, path, buffer, dev_path, dev_buffer, ++ type, flags, data, binary)); ++ } else { ++ error = fn_for_each_confined(label, profile, ++ match_mnt_path_str(profile, path, buffer, dev_name, ++ type, flags, data, binary, NULL)); ++ } ++ put_buffers(buffer, dev_buffer); ++ if (dev_path) ++ path_put(dev_path); ++ ++ return error; ++} ++ ++static int profile_umount(struct aa_profile *profile, struct path *path, ++ char *buffer) ++{ ++ struct aa_perms perms = { }; ++ const char *name = NULL, *info = NULL; ++ unsigned int state; ++ int error; ++ ++ AA_BUG(!profile); ++ AA_BUG(!path); ++ ++ error = aa_path_name(path, path_flags(profile, path), buffer, &name, ++ &info, profile->disconnected); ++ if (error) ++ goto audit; ++ ++ 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: ++ return audit_mount(profile, OP_UMOUNT, name, NULL, NULL, NULL, 0, NULL, ++ AA_MAY_UMOUNT, &perms, info, error); ++} ++ ++int aa_umount(struct aa_label *label, struct vfsmount *mnt, int flags) ++{ ++ struct aa_profile *profile; ++ char *buffer = NULL; ++ int error; ++ struct path path = { .mnt = mnt, .dentry = mnt->mnt_root }; ++ ++ AA_BUG(!label); ++ AA_BUG(!mnt); ++ ++ get_buffers(buffer); ++ error = fn_for_each_confined(label, profile, ++ profile_umount(profile, &path, buffer)); ++ put_buffers(buffer); ++ ++ return error; ++} ++ ++/* helper fn for transition on pivotroot ++ * ++ * Returns: label for transition or ERR_PTR. Does not return NULL ++ */ ++static struct aa_label *build_pivotroot(struct aa_profile *profile, ++ const struct path *new_path, ++ char *new_buffer, ++ const struct path *old_path, ++ char *old_buffer) ++{ ++ const char *old_name, *new_name = NULL, *info = NULL; ++ const char *trans_name = NULL; ++ struct aa_perms perms = { }; ++ unsigned int state; ++ int error; ++ ++ AA_BUG(!profile); ++ AA_BUG(!new_path); ++ AA_BUG(!old_path); ++ ++ if (profile_unconfined(profile)) ++ return aa_get_newest_label(&profile->label); ++ ++ error = aa_path_name(old_path, path_flags(profile, old_path), ++ old_buffer, &old_name, &info, ++ profile->disconnected); ++ if (error) ++ goto audit; ++ error = aa_path_name(new_path, path_flags(profile, new_path), ++ new_buffer, &new_name, &info, ++ profile->disconnected); ++ if (error) ++ goto audit; ++ ++ error = -EACCES; ++ 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) ++ error = 0; ++ ++audit: ++ error = audit_mount(profile, OP_PIVOTROOT, new_name, old_name, ++ NULL, trans_name, 0, NULL, AA_MAY_PIVOTROOT, ++ &perms, info, error); ++ if (error) ++ return ERR_PTR(error); ++ ++ return aa_get_newest_label(&profile->label); ++} ++ ++int aa_pivotroot(struct aa_label *label, const struct path *old_path, ++ const struct path *new_path) ++{ ++ struct aa_profile *profile; ++ struct aa_label *target = NULL; ++ char *old_buffer = NULL, *new_buffer = NULL, *info = NULL; ++ int error; ++ ++ AA_BUG(!label); ++ AA_BUG(!old_path); ++ AA_BUG(!new_path); ++ ++ get_buffers(old_buffer, new_buffer); ++ target = fn_label_build(label, profile, GFP_ATOMIC, ++ build_pivotroot(profile, new_path, new_buffer, ++ old_path, old_buffer)); ++ if (!target) { ++ info = "label build failed"; ++ error = -ENOMEM; ++ goto fail; ++ } else if (!IS_ERR(target)) { ++ error = aa_replace_current_label(target); ++ if (error) { ++ /* TODO: audit target */ ++ aa_put_label(target); ++ goto out; ++ } ++ } else ++ /* already audited error */ ++ error = PTR_ERR(target); ++out: ++ put_buffers(old_buffer, new_buffer); ++ ++ return error; ++ ++fail: ++ /* TODO: add back in auditing of new_name and old_name */ ++ error = fn_for_each(label, profile, ++ audit_mount(profile, OP_PIVOTROOT, NULL /*new_name */, ++ NULL /* old_name */, ++ NULL, NULL, ++ 0, NULL, AA_MAY_PIVOTROOT, &nullperms, info, ++ error)); ++ goto out; ++} +-- +2.11.0 + diff --git a/kernel-patches/v4.13/0008-apparmor-cleanup-conditional-check-for-label-in-labe.patch b/kernel-patches/v4.13/0008-apparmor-cleanup-conditional-check-for-label-in-labe.patch new file mode 100644 index 000000000..20892df48 --- /dev/null +++ b/kernel-patches/v4.13/0008-apparmor-cleanup-conditional-check-for-label-in-labe.patch @@ -0,0 +1,71 @@ +From 763d17c9a18b0df7dbec2740f10dc40d378e3cc1 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Sun, 6 Aug 2017 05:36:40 -0700 +Subject: [PATCH 08/17] apparmor: cleanup conditional check for label in + label_print + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +(cherry picked from commit 7e57939b9d67dcfc2c8348fd0e2c76a2f0349c75) +--- + security/apparmor/label.c | 22 ++++++++-------------- + 1 file changed, 8 insertions(+), 14 deletions(-) + +diff --git a/security/apparmor/label.c b/security/apparmor/label.c +index e324f4df3e34..38be7a89cc31 100644 +--- a/security/apparmor/label.c ++++ b/security/apparmor/label.c +@@ -1450,9 +1450,11 @@ bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp) + * cached label name is present and visible + * @label->hname only exists if label is namespace hierachical + */ +-static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label) ++static inline bool use_label_hname(struct aa_ns *ns, struct aa_label *label, ++ int flags) + { +- if (label->hname && labels_ns(label) == ns) ++ if (label->hname && (!ns || labels_ns(label) == ns) && ++ !(flags & ~FLAG_SHOW_MODE)) + return true; + + return false; +@@ -1710,10 +1712,8 @@ void aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns, + AA_BUG(!ab); + AA_BUG(!label); + +- if (!ns) +- ns = labels_ns(label); +- +- if (!use_label_hname(ns, label) || display_mode(ns, label, flags)) { ++ if (!use_label_hname(ns, label, flags) || ++ display_mode(ns, label, flags)) { + len = aa_label_asxprint(&name, ns, label, flags, gfp); + if (len == -1) { + AA_DEBUG("label print error"); +@@ -1738,10 +1738,7 @@ void aa_label_seq_xprint(struct seq_file *f, struct aa_ns *ns, + AA_BUG(!f); + AA_BUG(!label); + +- if (!ns) +- ns = labels_ns(label); +- +- if (!use_label_hname(ns, label)) { ++ if (!use_label_hname(ns, label, flags)) { + char *str; + int len; + +@@ -1764,10 +1761,7 @@ void aa_label_xprintk(struct aa_ns *ns, struct aa_label *label, int flags, + { + AA_BUG(!label); + +- if (!ns) +- ns = labels_ns(label); +- +- if (!use_label_hname(ns, label)) { ++ if (!use_label_hname(ns, label, flags)) { + char *str; + int len; + +-- +2.11.0 + diff --git a/kernel-patches/v4.13/0009-apparmor-add-support-for-absolute-root-view-based-la.patch b/kernel-patches/v4.13/0009-apparmor-add-support-for-absolute-root-view-based-la.patch new file mode 100644 index 000000000..84ee7a1da --- /dev/null +++ b/kernel-patches/v4.13/0009-apparmor-add-support-for-absolute-root-view-based-la.patch @@ -0,0 +1,63 @@ +From 6b092bbbf9e17b10f709d11b3bc2d7e493617934 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Sun, 6 Aug 2017 05:39:08 -0700 +Subject: [PATCH 09/17] apparmor: add support for absolute root view based + labels + +With apparmor policy virtualization based on policy namespace View's +we don't generally want/need absolute root based views, however there +are cases like debugging and some secid based conversions where +using a root based view is important. + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +(cherry picked from commit eadfbf0898eda94cee0d982626aa24a3146db48b) +--- + security/apparmor/include/label.h | 1 + + security/apparmor/label.c | 10 +++++++++- + 2 files changed, 10 insertions(+), 1 deletion(-) + +diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h +index 9a283b722755..af22dcbbcb8a 100644 +--- a/security/apparmor/include/label.h ++++ b/security/apparmor/include/label.h +@@ -310,6 +310,7 @@ bool aa_update_label_name(struct aa_ns *ns, struct aa_label *label, gfp_t gfp); + #define FLAG_SHOW_MODE 1 + #define FLAG_VIEW_SUBNS 2 + #define FLAG_HIDDEN_UNCONFINED 4 ++#define FLAG_ABS_ROOT 8 + int aa_label_snxprint(char *str, size_t size, struct aa_ns *view, + struct aa_label *label, int flags); + int aa_label_asxprint(char **strp, struct aa_ns *ns, struct aa_label *label, +diff --git a/security/apparmor/label.c b/security/apparmor/label.c +index 38be7a89cc31..52b4ef14840d 100644 +--- a/security/apparmor/label.c ++++ b/security/apparmor/label.c +@@ -1607,8 +1607,13 @@ int aa_label_snxprint(char *str, size_t size, struct aa_ns *ns, + AA_BUG(!str && size != 0); + AA_BUG(!label); + +- if (!ns) ++ if (flags & FLAG_ABS_ROOT) { ++ ns = root_ns; ++ len = snprintf(str, size, "="); ++ update_for_len(total, len, size, str); ++ } else if (!ns) { + ns = labels_ns(label); ++ } + + label_for_each(i, label, profile) { + if (aa_ns_visible(ns, profile->ns, flags & FLAG_VIEW_SUBNS)) { +@@ -1868,6 +1873,9 @@ struct aa_label *aa_label_parse(struct aa_label *base, const char *str, + if (*str == '&') + str++; + } ++ if (*str == '=') ++ base = &root_ns->unconfined->label; ++ + error = vec_setup(profile, vec, len, gfp); + if (error) + return ERR_PTR(error); +-- +2.11.0 + diff --git a/kernel-patches/v4.13/0010-apparmor-make-policy_unpack-able-to-audit-different-.patch b/kernel-patches/v4.13/0010-apparmor-make-policy_unpack-able-to-audit-different-.patch new file mode 100644 index 000000000..7e481b286 --- /dev/null +++ b/kernel-patches/v4.13/0010-apparmor-make-policy_unpack-able-to-audit-different-.patch @@ -0,0 +1,219 @@ +From aa4b6bded85552bc5f9f22d2e18ce86c5c17947c Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Tue, 18 Jul 2017 23:37:18 -0700 +Subject: [PATCH 10/17] apparmor: make policy_unpack able to audit different + info messages + +Switch unpack auditing to using the generic name field in the audit +struct and make it so we can start adding new info messages about +why an unpack failed. + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +(cherry picked from commit 1489d896c5649e9ce1b6000b4857f8baa7a6ab63) +--- + security/apparmor/include/audit.h | 4 +-- + security/apparmor/policy_unpack.c | 52 ++++++++++++++++++++++++++++----------- + 2 files changed, 40 insertions(+), 16 deletions(-) + +diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h +index c3fe1c5ef3bc..620e81169659 100644 +--- a/security/apparmor/include/audit.h ++++ b/security/apparmor/include/audit.h +@@ -127,9 +127,9 @@ struct apparmor_audit_data { + } fs; + }; + struct { +- const char *name; +- long pos; ++ struct aa_profile *profile; + const char *ns; ++ long pos; + } iface; + int signal; + struct { +diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c +index bda0dce3b582..4ede87c30f8b 100644 +--- a/security/apparmor/policy_unpack.c ++++ b/security/apparmor/policy_unpack.c +@@ -85,9 +85,9 @@ static void audit_cb(struct audit_buffer *ab, void *va) + audit_log_format(ab, " ns="); + audit_log_untrustedstring(ab, aad(sa)->iface.ns); + } +- if (aad(sa)->iface.name) { ++ if (aad(sa)->name) { + audit_log_format(ab, " name="); +- audit_log_untrustedstring(ab, aad(sa)->iface.name); ++ audit_log_untrustedstring(ab, aad(sa)->name); + } + if (aad(sa)->iface.pos) + audit_log_format(ab, " offset=%ld", aad(sa)->iface.pos); +@@ -114,9 +114,9 @@ static int audit_iface(struct aa_profile *new, const char *ns_name, + aad(&sa)->iface.pos = e->pos - e->start; + aad(&sa)->iface.ns = ns_name; + if (new) +- aad(&sa)->iface.name = new->base.hname; ++ aad(&sa)->name = new->base.hname; + else +- aad(&sa)->iface.name = name; ++ aad(&sa)->name = name; + aad(&sa)->info = info; + aad(&sa)->error = error; + +@@ -583,6 +583,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) + { + struct aa_profile *profile = NULL; + const char *tmpname, *tmpns = NULL, *name = NULL; ++ const char *info = "failed to unpack profile"; + size_t ns_len; + struct rhashtable_params params = { 0 }; + char *key = NULL; +@@ -604,8 +605,10 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) + tmpname = aa_splitn_fqname(name, strlen(name), &tmpns, &ns_len); + if (tmpns) { + *ns_name = kstrndup(tmpns, ns_len, GFP_KERNEL); +- if (!*ns_name) ++ if (!*ns_name) { ++ info = "out of memory"; + goto fail; ++ } + name = tmpname; + } + +@@ -624,12 +627,15 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) + if (IS_ERR(profile->xmatch)) { + error = PTR_ERR(profile->xmatch); + profile->xmatch = NULL; ++ info = "bad xmatch"; + goto fail; + } + /* xmatch_len is not optional if xmatch is set */ + if (profile->xmatch) { +- if (!unpack_u32(e, &tmp, NULL)) ++ if (!unpack_u32(e, &tmp, NULL)) { ++ info = "missing xmatch len"; + goto fail; ++ } + profile->xmatch_len = tmp; + } + +@@ -637,8 +643,11 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) + (void) unpack_str(e, &profile->disconnected, "disconnected"); + + /* per profile debug flags (complain, audit) */ +- if (!unpack_nameX(e, AA_STRUCT, "flags")) ++ if (!unpack_nameX(e, AA_STRUCT, "flags")) { ++ info = "profile missing flags"; + goto fail; ++ } ++ info = "failed to unpack profile flags"; + if (!unpack_u32(e, &tmp, NULL)) + goto fail; + if (tmp & PACKED_FLAG_HAT) +@@ -667,6 +676,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) + /* set a default value if path_flags field is not present */ + profile->path_flags = PATH_MEDIATE_DELETED; + ++ info = "failed to unpack profile capabilities"; + if (!unpack_u32(e, &(profile->caps.allow.cap[0]), NULL)) + goto fail; + if (!unpack_u32(e, &(profile->caps.audit.cap[0]), NULL)) +@@ -676,6 +686,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) + if (!unpack_u32(e, &tmpcap.cap[0], NULL)) + goto fail; + ++ info = "failed to unpack upper profile capabilities"; + if (unpack_nameX(e, AA_STRUCT, "caps64")) { + /* optional upper half of 64 bit caps */ + if (!unpack_u32(e, &(profile->caps.allow.cap[1]), NULL)) +@@ -690,6 +701,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) + goto fail; + } + ++ info = "failed to unpack extended profile capabilities"; + if (unpack_nameX(e, AA_STRUCT, "capsx")) { + /* optional extended caps mediation mask */ + if (!unpack_u32(e, &(profile->caps.extended.cap[0]), NULL)) +@@ -700,11 +712,14 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) + goto fail; + } + +- if (!unpack_rlimits(e, profile)) ++ if (!unpack_rlimits(e, profile)) { ++ info = "failed to unpack profile rlimits"; + goto fail; ++ } + + if (unpack_nameX(e, AA_STRUCT, "policydb")) { + /* generic policy dfa - optional and may be NULL */ ++ info = "failed to unpack policydb"; + profile->policy.dfa = unpack_dfa(e); + if (IS_ERR(profile->policy.dfa)) { + error = PTR_ERR(profile->policy.dfa); +@@ -734,6 +749,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) + if (IS_ERR(profile->file.dfa)) { + error = PTR_ERR(profile->file.dfa); + profile->file.dfa = NULL; ++ info = "failed to unpack profile file rules"; + goto fail; + } else if (profile->file.dfa) { + if (!unpack_u32(e, &profile->file.start, "dfa_start")) +@@ -746,10 +762,13 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) + } else + profile->file.dfa = aa_get_dfa(nulldfa); + +- if (!unpack_trans_table(e, profile)) ++ if (!unpack_trans_table(e, profile)) { ++ info = "failed to unpack profile transition table"; + goto fail; ++ } + + if (unpack_nameX(e, AA_STRUCT, "data")) { ++ info = "out of memory"; + profile->data = kzalloc(sizeof(*profile->data), GFP_KERNEL); + if (!profile->data) + goto fail; +@@ -761,8 +780,10 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) + params.hashfn = strhash; + params.obj_cmpfn = datacmp; + +- if (rhashtable_init(profile->data, ¶ms)) ++ if (rhashtable_init(profile->data, ¶ms)) { ++ info = "failed to init key, value hash table"; + goto fail; ++ } + + while (unpack_strdup(e, &key, NULL)) { + data = kzalloc(sizeof(*data), GFP_KERNEL); +@@ -784,12 +805,16 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) + profile->data->p); + } + +- if (!unpack_nameX(e, AA_STRUCTEND, NULL)) ++ if (!unpack_nameX(e, AA_STRUCTEND, NULL)) { ++ info = "failed to unpack end of key, value data table"; + goto fail; ++ } + } + +- if (!unpack_nameX(e, AA_STRUCTEND, NULL)) ++ if (!unpack_nameX(e, AA_STRUCTEND, NULL)) { ++ info = "failed to unpack end of profile"; + goto fail; ++ } + + return profile; + +@@ -798,8 +823,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) + name = NULL; + else if (!name) + name = "unknown"; +- audit_iface(profile, NULL, name, "failed to unpack profile", e, +- error); ++ audit_iface(profile, NULL, name, info, e, error); + aa_free_profile(profile); + + return ERR_PTR(error); +-- +2.11.0 + diff --git a/kernel-patches/v4.13/0011-apparmor-add-more-debug-asserts-to-apparmorfs.patch b/kernel-patches/v4.13/0011-apparmor-add-more-debug-asserts-to-apparmorfs.patch new file mode 100644 index 000000000..85eafc85d --- /dev/null +++ b/kernel-patches/v4.13/0011-apparmor-add-more-debug-asserts-to-apparmorfs.patch @@ -0,0 +1,78 @@ +From ba3f778a2ef31454032c2ca9c99d9212feb4dcf1 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Tue, 18 Jul 2017 23:41:13 -0700 +Subject: [PATCH 11/17] apparmor: add more debug asserts to apparmorfs + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +(cherry picked from commit 52c9542126fb04df1f12c605b6c22719c9096794) +--- + security/apparmor/apparmorfs.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c +index 8fa6c898c44b..7acea14c850b 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -1446,6 +1446,10 @@ void __aafs_profile_migrate_dents(struct aa_profile *old, + { + int i; + ++ AA_BUG(!old); ++ AA_BUG(!new); ++ AA_BUG(!mutex_is_locked(&profiles_ns(old)->lock)); ++ + for (i = 0; i < AAFS_PROF_SIZEOF; i++) { + new->dents[i] = old->dents[i]; + if (new->dents[i]) +@@ -1509,6 +1513,9 @@ int __aafs_profile_mkdir(struct aa_profile *profile, struct dentry *parent) + struct dentry *dent = NULL, *dir; + int error; + ++ AA_BUG(!profile); ++ AA_BUG(!mutex_is_locked(&profiles_ns(profile)->lock)); ++ + if (!parent) { + struct aa_profile *p; + p = aa_deref_parent(profile); +@@ -1734,6 +1741,7 @@ void __aafs_ns_rmdir(struct aa_ns *ns) + + if (!ns) + return; ++ AA_BUG(!mutex_is_locked(&ns->lock)); + + list_for_each_entry(child, &ns->base.profiles, base.list) + __aafs_profile_rmdir(child); +@@ -1906,6 +1914,10 @@ static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns) + { + struct aa_ns *parent, *next; + ++ AA_BUG(!root); ++ AA_BUG(!ns); ++ AA_BUG(ns != root && !mutex_is_locked(&ns->parent->lock)); ++ + /* is next namespace a child */ + if (!list_empty(&ns->sub_ns)) { + next = list_first_entry(&ns->sub_ns, typeof(*ns), base.list); +@@ -1940,6 +1952,9 @@ static struct aa_ns *__next_ns(struct aa_ns *root, struct aa_ns *ns) + static struct aa_profile *__first_profile(struct aa_ns *root, + struct aa_ns *ns) + { ++ AA_BUG(!root); ++ AA_BUG(ns && !mutex_is_locked(&ns->lock)); ++ + for (; ns; ns = __next_ns(root, ns)) { + if (!list_empty(&ns->base.profiles)) + return list_first_entry(&ns->base.profiles, +@@ -1962,6 +1977,8 @@ static struct aa_profile *__next_profile(struct aa_profile *p) + struct aa_profile *parent; + struct aa_ns *ns = p->ns; + ++ AA_BUG(!mutex_is_locked(&profiles_ns(p)->lock)); ++ + /* is next profile a child */ + if (!list_empty(&p->base.profiles)) + return list_first_entry(&p->base.profiles, typeof(*p), +-- +2.11.0 + diff --git a/kernel-patches/v4.13/0012-apparmor-add-base-infastructure-for-socket-mediation.patch b/kernel-patches/v4.13/0012-apparmor-add-base-infastructure-for-socket-mediation.patch new file mode 100644 index 000000000..2a29c9608 --- /dev/null +++ b/kernel-patches/v4.13/0012-apparmor-add-base-infastructure-for-socket-mediation.patch @@ -0,0 +1,1123 @@ +From 853cbdfb6924857a2ee2a1cd5b9fa494f8e7efa2 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Tue, 18 Jul 2017 23:18:33 -0700 +Subject: [PATCH 12/17] apparmor: add base infastructure for socket mediation + +Provide a basic mediation of sockets. This is not a full net mediation +but just whether a spcific family of socket can be used by an +application, along with setting up some basic infrastructure for +network mediation to follow. + +the user space rule hav the basic form of + NETWORK RULE = [ QUALIFIERS ] 'network' [ DOMAIN ] + [ TYPE | PROTOCOL ] + + DOMAIN = ( 'inet' | 'ax25' | 'ipx' | 'appletalk' | 'netrom' | + 'bridge' | 'atmpvc' | 'x25' | 'inet6' | 'rose' | + 'netbeui' | 'security' | 'key' | 'packet' | 'ash' | + 'econet' | 'atmsvc' | 'sna' | 'irda' | 'pppox' | + 'wanpipe' | 'bluetooth' | 'netlink' | 'unix' | 'rds' | + 'llc' | 'can' | 'tipc' | 'iucv' | 'rxrpc' | 'isdn' | + 'phonet' | 'ieee802154' | 'caif' | 'alg' | 'nfc' | + 'vsock' | 'mpls' | 'ib' | 'kcm' ) ',' + + TYPE = ( 'stream' | 'dgram' | 'seqpacket' | 'rdm' | 'raw' | + 'packet' ) + + PROTOCOL = ( 'tcp' | 'udp' | 'icmp' ) + +eg. + network, + network inet, + +Signed-off-by: John Johansen +Acked-by: Seth Arnold +(cherry picked from commit 56387cbe3f287034ee6959cb9e8f419889e38bd9) +--- + security/apparmor/.gitignore | 1 + + security/apparmor/Makefile | 43 ++++- + security/apparmor/apparmorfs.c | 1 + + security/apparmor/file.c | 30 +++ + security/apparmor/include/audit.h | 26 ++- + security/apparmor/include/net.h | 114 +++++++++++ + security/apparmor/include/perms.h | 5 +- + security/apparmor/include/policy.h | 13 ++ + security/apparmor/lib.c | 5 +- + security/apparmor/lsm.c | 387 +++++++++++++++++++++++++++++++++++++ + security/apparmor/net.c | 184 ++++++++++++++++++ + security/apparmor/policy_unpack.c | 47 ++++- + 12 files changed, 840 insertions(+), 16 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 9cdec70d72b8..d5b291e94264 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 81a34426d024..dafdd387d42b 100644 +--- a/security/apparmor/Makefile ++++ b/security/apparmor/Makefile +@@ -4,11 +4,44 @@ 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 secid.o file.o policy_ns.o label.o mount.o ++ resource.o secid.o file.o policy_ns.o label.o mount.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 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_SFS_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 "/AF_ROUTE/d" -e \ ++ 's/^\#define[ \t]+AF_([A-Z0-9_]+)[ \t]+([0-9]+)(.*)/[\2] = "\L\1",/p';\ ++ echo "};" >> $@ ;\ ++ printf '%s' '\#define AA_SFS_AF_MASK "' >> $@ ;\ ++ sed -r -n -e "/AF_MAX/d" -e "/AF_LOCAL/d" -e "/AF_ROUTE/d" -e \ ++ '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 capability names + # Transforms lines from +@@ -61,6 +94,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 +102,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 7acea14c850b..125dad5c3fde 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -2202,6 +2202,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { + AA_SFS_DIR("policy", aa_sfs_entry_policy), + AA_SFS_DIR("domain", aa_sfs_entry_domain), + AA_SFS_DIR("file", aa_sfs_entry_file), ++ AA_SFS_DIR("network", aa_sfs_entry_network), + AA_SFS_DIR("mount", aa_sfs_entry_mount), + AA_SFS_DIR("namespaces", aa_sfs_entry_ns), + AA_SFS_FILE_U64("capability", VFS_CAP_FLAGS_MASK), +diff --git a/security/apparmor/file.c b/security/apparmor/file.c +index 3382518b87fa..db80221891c6 100644 +--- a/security/apparmor/file.c ++++ b/security/apparmor/file.c +@@ -21,6 +21,7 @@ + #include "include/context.h" + #include "include/file.h" + #include "include/match.h" ++#include "include/net.h" + #include "include/path.h" + #include "include/policy.h" + #include "include/label.h" +@@ -566,6 +567,32 @@ static int __file_path_perm(const char *op, struct aa_label *label, + return error; + } + ++static int __file_sock_perm(const char *op, struct aa_label *label, ++ struct aa_label *flabel, struct file *file, ++ u32 request, u32 denied) ++{ ++ struct socket *sock = (struct socket *) file->private_data; ++ int error; ++ ++ AA_BUG(!sock); ++ ++ /* revalidation due to label out of date. No revocation at this time */ ++ if (!denied && aa_label_is_subset(flabel, label)) ++ return 0; ++ ++ /* TODO: improve to skip profiles cached in flabel */ ++ error = aa_sock_file_perm(label, op, request, sock); ++ if (denied) { ++ /* TODO: improve to skip profiles checked above */ ++ /* check every profile in file label to is cached */ ++ last_error(error, aa_sock_file_perm(flabel, op, request, sock)); ++ } ++ if (!error) ++ update_file_ctx(file_ctx(file), label, request); ++ ++ return error; ++} ++ + /** + * aa_file_perm - do permission revalidation check & audit for @file + * @op: operation being checked +@@ -610,6 +637,9 @@ int aa_file_perm(const char *op, struct aa_label *label, struct file *file, + error = __file_path_perm(op, label, flabel, file, request, + denied); + ++ else if (S_ISSOCK(file_inode(file)->i_mode)) ++ error = __file_sock_perm(op, label, flabel, file, request, ++ denied); + done: + rcu_read_unlock(); + +diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h +index 620e81169659..ff4316e1068d 100644 +--- a/security/apparmor/include/audit.h ++++ b/security/apparmor/include/audit.h +@@ -121,21 +121,29 @@ struct apparmor_audit_data { + /* these entries require a custom callback fn */ + struct { + struct aa_label *peer; +- struct { +- const char *target; +- kuid_t ouid; +- } fs; ++ union { ++ struct { ++ kuid_t ouid; ++ const char *target; ++ } fs; ++ struct { ++ int type, protocol; ++ struct sock *peer_sk; ++ void *addr; ++ int addrlen; ++ } net; ++ int signal; ++ struct { ++ int rlim; ++ unsigned long max; ++ } rlim; ++ }; + }; + struct { + struct aa_profile *profile; + const char *ns; + long pos; + } iface; +- int signal; +- struct { +- int rlim; +- unsigned long max; +- } rlim; + struct { + const char *src_name; + const char *type; +diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h +new file mode 100644 +index 000000000000..140c8efcf364 +--- /dev/null ++++ b/security/apparmor/include/net.h +@@ -0,0 +1,114 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor network mediation definitions. ++ * ++ * Copyright (C) 1998-2008 Novell/SUSE ++ * Copyright 2009-2017 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 ++ ++#include "apparmorfs.h" ++#include "label.h" ++#include "perms.h" ++#include "policy.h" ++ ++#define AA_MAY_SEND AA_MAY_WRITE ++#define AA_MAY_RECEIVE AA_MAY_READ ++ ++#define AA_MAY_SHUTDOWN AA_MAY_DELETE ++ ++#define AA_MAY_CONNECT AA_MAY_OPEN ++#define AA_MAY_ACCEPT 0x00100000 ++ ++#define AA_MAY_BIND 0x00200000 ++#define AA_MAY_LISTEN 0x00400000 ++ ++#define AA_MAY_SETOPT 0x01000000 ++#define AA_MAY_GETOPT 0x02000000 ++ ++#define NET_PERMS_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE | \ ++ AA_MAY_SHUTDOWN | AA_MAY_BIND | AA_MAY_LISTEN | \ ++ AA_MAY_CONNECT | AA_MAY_ACCEPT | AA_MAY_SETATTR | \ ++ AA_MAY_GETATTR | AA_MAY_SETOPT | AA_MAY_GETOPT) ++ ++#define NET_FS_PERMS (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CREATE | \ ++ AA_MAY_SHUTDOWN | AA_MAY_CONNECT | AA_MAY_RENAME |\ ++ AA_MAY_SETATTR | AA_MAY_GETATTR | AA_MAY_CHMOD | \ ++ AA_MAY_CHOWN | AA_MAY_CHGRP | AA_MAY_LOCK | \ ++ AA_MAY_MPROT) ++ ++#define NET_PEER_MASK (AA_MAY_SEND | AA_MAY_RECEIVE | AA_MAY_CONNECT | \ ++ AA_MAY_ACCEPT) ++struct aa_sk_ctx { ++ struct aa_label *label; ++ struct aa_label *peer; ++ struct path path; ++}; ++ ++#define SK_CTX(X) ((X)->sk_security) ++#define SOCK_ctx(X) SOCK_INODE(X)->i_security ++#define DEFINE_AUDIT_NET(NAME, OP, SK, F, T, P) \ ++ struct lsm_network_audit NAME ## _net = { .sk = (SK), \ ++ .family = (F)}; \ ++ DEFINE_AUDIT_DATA(NAME, \ ++ ((SK) && (F) != AF_UNIX) ? LSM_AUDIT_DATA_NET : \ ++ LSM_AUDIT_DATA_NONE, \ ++ OP); \ ++ NAME.u.net = &(NAME ## _net); \ ++ aad(&NAME)->net.type = (T); \ ++ aad(&NAME)->net.protocol = (P) ++ ++#define DEFINE_AUDIT_SK(NAME, OP, SK) \ ++ DEFINE_AUDIT_NET(NAME, OP, SK, (SK)->sk_family, (SK)->sk_type, \ ++ (SK)->sk_protocol) ++ ++/* struct aa_net - network confinement data ++ * @allow: basic network families permissions ++ * @audit: which network permissions to force audit ++ * @quiet: which network permissions to quiet rejects ++ */ ++struct aa_net { ++ u16 allow[AF_MAX]; ++ u16 audit[AF_MAX]; ++ u16 quiet[AF_MAX]; ++}; ++ ++ ++extern struct aa_sfs_entry aa_sfs_entry_network[]; ++ ++void audit_net_cb(struct audit_buffer *ab, void *va); ++int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, ++ u32 request, u16 family, int type); ++int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, ++ int type, int protocol); ++static inline int aa_profile_af_sk_perm(struct aa_profile *profile, ++ struct common_audit_data *sa, ++ u32 request, ++ struct sock *sk) ++{ ++ return aa_profile_af_perm(profile, sa, request, sk->sk_family, ++ sk->sk_type); ++} ++int aa_sk_perm(const char *op, u32 request, struct sock *sk); ++ ++int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, ++ struct socket *sock); ++ ++ ++static inline void aa_free_net_rules(struct aa_net *new) ++{ ++ /* NOP */ ++} ++ ++#endif /* __AA_NET_H */ +diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h +index 2b27bb79aec4..af04d5a7d73d 100644 +--- a/security/apparmor/include/perms.h ++++ b/security/apparmor/include/perms.h +@@ -135,9 +135,10 @@ extern struct aa_perms allperms; + + + void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask); +-void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask); ++void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names, ++ u32 mask); + void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, +- u32 chrsmask, const char **names, u32 namesmask); ++ u32 chrsmask, const char * const *names, u32 namesmask); + void aa_apply_modes_to_perms(struct aa_profile *profile, + struct aa_perms *perms); + void aa_compute_perms(struct aa_dfa *dfa, unsigned int state, +diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h +index 17fe41a9cac3..4364088a0b9e 100644 +--- a/security/apparmor/include/policy.h ++++ b/security/apparmor/include/policy.h +@@ -30,6 +30,7 @@ + #include "file.h" + #include "lib.h" + #include "label.h" ++#include "net.h" + #include "perms.h" + #include "resource.h" + +@@ -111,6 +112,7 @@ struct aa_data { + * @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 +@@ -148,6 +150,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; + + struct aa_loaddata *rawdata; +@@ -220,6 +223,16 @@ static inline unsigned int PROFILE_MEDIATES_SAFE(struct aa_profile *profile, + return 0; + } + ++static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile, ++ u16 AF) { ++ unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET); ++ u16 be_af = cpu_to_be16(AF); ++ ++ if (!state) ++ return 0; ++ return aa_dfa_match_len(profile->policy.dfa, state, (char *) &be_af, 2); ++} ++ + /** + * aa_get_profile - increment refcount on profile @p + * @p: profile (MAYBE NULL) +diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c +index 08ca26bcca77..8818621b5d95 100644 +--- a/security/apparmor/lib.c ++++ b/security/apparmor/lib.c +@@ -211,7 +211,8 @@ void aa_perm_mask_to_str(char *str, const char *chrs, u32 mask) + *str = '\0'; + } + +-void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask) ++void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names, ++ u32 mask) + { + const char *fmt = "%s"; + unsigned int i, perm = 1; +@@ -229,7 +230,7 @@ void aa_audit_perm_names(struct audit_buffer *ab, const char **names, u32 mask) + } + + void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs, +- u32 chrsmask, const char **names, u32 namesmask) ++ u32 chrsmask, const char * const *names, u32 namesmask) + { + char str[33]; + +diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c +index 4ad0b3a45142..cc5ab23a2d84 100644 +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -33,6 +33,7 @@ + #include "include/context.h" + #include "include/file.h" + #include "include/ipc.h" ++#include "include/net.h" + #include "include/path.h" + #include "include/label.h" + #include "include/policy.h" +@@ -736,6 +737,368 @@ static int apparmor_task_kill(struct task_struct *target, struct siginfo *info, + return error; + } + ++/** ++ * apparmor_sk_alloc_security - allocate and attach the sk_security field ++ */ ++static int apparmor_sk_alloc_security(struct sock *sk, int family, gfp_t flags) ++{ ++ struct aa_sk_ctx *ctx; ++ ++ ctx = kzalloc(sizeof(*ctx), flags); ++ if (!ctx) ++ return -ENOMEM; ++ ++ SK_CTX(sk) = ctx; ++ ++ return 0; ++} ++ ++/** ++ * apparmor_sk_free_security - free the sk_security field ++ */ ++static void apparmor_sk_free_security(struct sock *sk) ++{ ++ struct aa_sk_ctx *ctx = SK_CTX(sk); ++ ++ SK_CTX(sk) = NULL; ++ aa_put_label(ctx->label); ++ aa_put_label(ctx->peer); ++ path_put(&ctx->path); ++ kfree(ctx); ++} ++ ++/** ++ * apparmor_clone_security - clone the sk_security field ++ */ ++static void apparmor_sk_clone_security(const struct sock *sk, ++ struct sock *newsk) ++{ ++ struct aa_sk_ctx *ctx = SK_CTX(sk); ++ struct aa_sk_ctx *new = SK_CTX(newsk); ++ ++ new->label = aa_get_label(ctx->label); ++ new->peer = aa_get_label(ctx->peer); ++ new->path = ctx->path; ++ path_get(&new->path); ++} ++ ++static int aa_sock_create_perm(struct aa_label *label, int family, int type, ++ int protocol) ++{ ++ AA_BUG(!label); ++ AA_BUG(in_interrupt()); ++ ++ return aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, type, ++ protocol); ++} ++ ++ ++/** ++ * apparmor_socket_create - check perms before creating a new socket ++ */ ++static int apparmor_socket_create(int family, int type, int protocol, int kern) ++{ ++ struct aa_label *label; ++ int error = 0; ++ ++ label = begin_current_label_crit_section(); ++ if (!(kern || unconfined(label))) ++ error = aa_sock_create_perm(label, family, type, protocol); ++ end_current_label_crit_section(label); ++ ++ return error; ++} ++ ++/** ++ * apparmor_socket_post_create - setup the per-socket security struct ++ * ++ * Note: ++ * - kernel sockets currently labeled unconfined but we may want to ++ * move to a special kernel label ++ * - socket may not have sk here if created with sock_create_lite or ++ * sock_alloc. These should be accept cases which will be handled in ++ * sock_graft. ++ */ ++static int apparmor_socket_post_create(struct socket *sock, int family, ++ int type, int protocol, int kern) ++{ ++ struct aa_label *label; ++ ++ if (kern) { ++ struct aa_ns *ns = aa_get_current_ns(); ++ ++ label = aa_get_label(ns_unconfined(ns)); ++ aa_put_ns(ns); ++ } else ++ label = aa_get_current_label(); ++ ++ if (sock->sk) { ++ struct aa_sk_ctx *ctx = SK_CTX(sock->sk); ++ ++ aa_put_label(ctx->label); ++ ctx->label = aa_get_label(label); ++ } ++ aa_put_label(label); ++ ++ return 0; ++} ++ ++/** ++ * apparmor_socket_bind - check perms before bind addr to socket ++ */ ++static int apparmor_socket_bind(struct socket *sock, ++ struct sockaddr *address, int addrlen) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(!address); ++ AA_BUG(in_interrupt()); ++ ++ return aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk); ++} ++ ++/** ++ * apparmor_socket_connect - check perms before connecting @sock to @address ++ */ ++static int apparmor_socket_connect(struct socket *sock, ++ struct sockaddr *address, int addrlen) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(!address); ++ AA_BUG(in_interrupt()); ++ ++ return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk); ++} ++ ++/** ++ * apparmor_socket_list - check perms before allowing listen ++ */ ++static int apparmor_socket_listen(struct socket *sock, int backlog) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(in_interrupt()); ++ ++ return aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk); ++} ++ ++/** ++ * apparmor_socket_accept - check perms before accepting a new connection. ++ * ++ * Note: while @newsock is created and has some information, the accept ++ * has not been done. ++ */ ++static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(!newsock); ++ AA_BUG(in_interrupt()); ++ ++ return aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk); ++} ++ ++static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, ++ struct msghdr *msg, int size) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(!msg); ++ AA_BUG(in_interrupt()); ++ ++ return aa_sk_perm(op, request, sock->sk); ++} ++ ++/** ++ * apparmor_socket_sendmsg - check perms before sending msg to another socket ++ */ ++static int apparmor_socket_sendmsg(struct socket *sock, ++ struct msghdr *msg, int size) ++{ ++ return aa_sock_msg_perm(OP_SENDMSG, AA_MAY_SEND, sock, msg, size); ++} ++ ++/** ++ * apparmor_socket_recvmsg - check perms before receiving a message ++ */ ++static int apparmor_socket_recvmsg(struct socket *sock, ++ struct msghdr *msg, int size, int flags) ++{ ++ return aa_sock_msg_perm(OP_RECVMSG, AA_MAY_RECEIVE, sock, msg, size); ++} ++ ++/* revaliation, get/set attr, shutdown */ ++static int aa_sock_perm(const char *op, u32 request, struct socket *sock) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(in_interrupt()); ++ ++ return aa_sk_perm(op, request, sock->sk); ++} ++ ++/** ++ * apparmor_socket_getsockname - check perms before getting the local address ++ */ ++static int apparmor_socket_getsockname(struct socket *sock) ++{ ++ return aa_sock_perm(OP_GETSOCKNAME, AA_MAY_GETATTR, sock); ++} ++ ++/** ++ * apparmor_socket_getpeername - check perms before getting remote address ++ */ ++static int apparmor_socket_getpeername(struct socket *sock) ++{ ++ return aa_sock_perm(OP_GETPEERNAME, AA_MAY_GETATTR, sock); ++} ++ ++/* revaliation, get/set attr, opt */ ++static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, ++ int level, int optname) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(in_interrupt()); ++ ++ return aa_sk_perm(op, request, sock->sk); ++} ++ ++/** ++ * apparmor_getsockopt - check perms before getting socket options ++ */ ++static int apparmor_socket_getsockopt(struct socket *sock, int level, ++ int optname) ++{ ++ return aa_sock_opt_perm(OP_GETSOCKOPT, AA_MAY_GETOPT, sock, ++ level, optname); ++} ++ ++/** ++ * apparmor_setsockopt - check perms before setting socket options ++ */ ++static int apparmor_socket_setsockopt(struct socket *sock, int level, ++ int optname) ++{ ++ return aa_sock_opt_perm(OP_SETSOCKOPT, AA_MAY_SETOPT, sock, ++ level, optname); ++} ++ ++/** ++ * apparmor_socket_shutdown - check perms before shutting down @sock conn ++ */ ++static int apparmor_socket_shutdown(struct socket *sock, int how) ++{ ++ return aa_sock_perm(OP_SHUTDOWN, AA_MAY_SHUTDOWN, sock); ++} ++ ++/** ++ * apparmor_socket_sock_recv_skb - check perms before associating skb to sk ++ * ++ * Note: can not sleep may be called with locks held ++ * ++ * dont want protocol specific in __skb_recv_datagram() ++ * to deny an incoming connection socket_sock_rcv_skb() ++ */ ++static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) ++{ ++ return 0; ++} ++ ++ ++static struct aa_label *sk_peer_label(struct sock *sk) ++{ ++ struct aa_sk_ctx *ctx = SK_CTX(sk); ++ ++ if (ctx->peer) ++ return ctx->peer; ++ ++ return ERR_PTR(-ENOPROTOOPT); ++} ++ ++/** ++ * apparmor_socket_getpeersec_stream - get security context of peer ++ * ++ * Note: for tcp only valid if using ipsec or cipso on lan ++ */ ++static int apparmor_socket_getpeersec_stream(struct socket *sock, ++ char __user *optval, ++ int __user *optlen, ++ unsigned int len) ++{ ++ char *name; ++ int slen, error = 0; ++ struct aa_label *label; ++ struct aa_label *peer; ++ ++ label = begin_current_label_crit_section(); ++ peer = sk_peer_label(sock->sk); ++ if (IS_ERR(peer)) { ++ error = PTR_ERR(peer); ++ goto done; ++ } ++ slen = aa_label_asxprint(&name, labels_ns(label), peer, ++ FLAG_SHOW_MODE | FLAG_VIEW_SUBNS | ++ FLAG_HIDDEN_UNCONFINED, GFP_KERNEL); ++ /* don't include terminating \0 in slen, it breaks some apps */ ++ if (slen < 0) { ++ error = -ENOMEM; ++ } else { ++ if (slen > len) { ++ error = -ERANGE; ++ } else if (copy_to_user(optval, name, slen)) { ++ error = -EFAULT; ++ goto out; ++ } ++ if (put_user(slen, optlen)) ++ error = -EFAULT; ++out: ++ kfree(name); ++ ++ } ++ ++done: ++ end_current_label_crit_section(label); ++ ++ return error; ++} ++ ++/** ++ * apparmor_socket_getpeersec_dgram - get security label of packet ++ * @sock: the peer socket ++ * @skb: packet data ++ * @secid: pointer to where to put the secid of the packet ++ * ++ * Sets the netlabel socket state on sk from parent ++ */ ++static int apparmor_socket_getpeersec_dgram(struct socket *sock, ++ struct sk_buff *skb, u32 *secid) ++ ++{ ++ /* TODO: requires secid support */ ++ return -ENOPROTOOPT; ++} ++ ++/** ++ * apparmor_sock_graft - Initialize newly created socket ++ * @sk: child sock ++ * @parent: parent socket ++ * ++ * Note: could set off of SOCK_CTX(parent) but need to track inode and we can ++ * just set sk security information off of current creating process label ++ * Labeling of sk for accept case - probably should be sock based ++ * instead of task, because of the case where an implicitly labeled ++ * socket is shared by different tasks. ++ */ ++static void apparmor_sock_graft(struct sock *sk, struct socket *parent) ++{ ++ struct aa_sk_ctx *ctx = SK_CTX(sk); ++ ++ if (!ctx->label) ++ ctx->label = aa_get_current_label(); ++} ++ + static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { + LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), + LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), +@@ -770,6 +1133,30 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { + LSM_HOOK_INIT(getprocattr, apparmor_getprocattr), + LSM_HOOK_INIT(setprocattr, apparmor_setprocattr), + ++ LSM_HOOK_INIT(sk_alloc_security, apparmor_sk_alloc_security), ++ LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security), ++ LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security), ++ ++ LSM_HOOK_INIT(socket_create, apparmor_socket_create), ++ LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_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(socket_sock_rcv_skb, apparmor_socket_sock_rcv_skb), ++ LSM_HOOK_INIT(socket_getpeersec_stream, ++ apparmor_socket_getpeersec_stream), ++ LSM_HOOK_INIT(socket_getpeersec_dgram, ++ apparmor_socket_getpeersec_dgram), ++ LSM_HOOK_INIT(sock_graft, apparmor_sock_graft), ++ + 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 000000000000..33d54435f8d6 +--- /dev/null ++++ b/security/apparmor/net.c +@@ -0,0 +1,184 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor network mediation ++ * ++ * Copyright (C) 1998-2008 Novell/SUSE ++ * Copyright 2009-2017 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/label.h" ++#include "include/net.h" ++#include "include/policy.h" ++ ++#include "net_names.h" ++ ++ ++struct aa_sfs_entry aa_sfs_entry_network[] = { ++ AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), ++ { } ++}; ++ ++static const char * const net_mask_names[] = { ++ "unknown", ++ "send", ++ "receive", ++ "unknown", ++ ++ "create", ++ "shutdown", ++ "connect", ++ "unknown", ++ ++ "setattr", ++ "getattr", ++ "setcred", ++ "getcred", ++ ++ "chmod", ++ "chown", ++ "chgrp", ++ "lock", ++ ++ "mmap", ++ "mprot", ++ "unknown", ++ "unknown", ++ ++ "accept", ++ "bind", ++ "listen", ++ "unknown", ++ ++ "setopt", ++ "getopt", ++ "unknown", ++ "unknown", ++ ++ "unknown", ++ "unknown", ++ "unknown", ++ "unknown", ++}; ++ ++ ++/* audit callback for net specific fields */ ++void audit_net_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[aad(sa)->net.type]) ++ audit_log_string(ab, sock_type_names[aad(sa)->net.type]); ++ else ++ audit_log_format(ab, "\"unknown(%d)\"", aad(sa)->net.type); ++ audit_log_format(ab, " protocol=%d", aad(sa)->net.protocol); ++ ++ if (aad(sa)->request & NET_PERMS_MASK) { ++ audit_log_format(ab, " requested_mask="); ++ aa_audit_perm_mask(ab, aad(sa)->request, NULL, 0, ++ net_mask_names, NET_PERMS_MASK); ++ ++ if (aad(sa)->denied & NET_PERMS_MASK) { ++ audit_log_format(ab, " denied_mask="); ++ aa_audit_perm_mask(ab, aad(sa)->denied, NULL, 0, ++ net_mask_names, NET_PERMS_MASK); ++ } ++ } ++ if (aad(sa)->peer) { ++ audit_log_format(ab, " peer="); ++ aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, ++ FLAGS_NONE, GFP_ATOMIC); ++ } ++} ++ ++ ++/* Generic af perm */ ++int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, ++ u32 request, u16 family, int type) ++{ ++ struct aa_perms perms = { }; ++ ++ AA_BUG(family >= AF_MAX); ++ AA_BUG(type < 0 || type >= SOCK_MAX); ++ ++ if (profile_unconfined(profile)) ++ return 0; ++ ++ perms.allow = (profile->net.allow[family] & (1 << type)) ? ++ ALL_PERMS_MASK : 0; ++ perms.audit = (profile->net.audit[family] & (1 << type)) ? ++ ALL_PERMS_MASK : 0; ++ perms.quiet = (profile->net.quiet[family] & (1 << type)) ? ++ ALL_PERMS_MASK : 0; ++ aa_apply_modes_to_perms(profile, &perms); ++ ++ return aa_check_perms(profile, &perms, request, sa, audit_net_cb); ++} ++ ++int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, ++ int type, int protocol) ++{ ++ struct aa_profile *profile; ++ DEFINE_AUDIT_NET(sa, op, NULL, family, type, protocol); ++ ++ return fn_for_each_confined(label, profile, ++ aa_profile_af_perm(profile, &sa, request, family, ++ type)); ++} ++ ++static int aa_label_sk_perm(struct aa_label *label, const char *op, u32 request, ++ struct sock *sk) ++{ ++ struct aa_profile *profile; ++ DEFINE_AUDIT_SK(sa, op, sk); ++ ++ AA_BUG(!label); ++ AA_BUG(!sk); ++ ++ if (unconfined(label)) ++ return 0; ++ ++ return fn_for_each_confined(label, profile, ++ aa_profile_af_sk_perm(profile, &sa, request, sk)); ++} ++ ++int aa_sk_perm(const char *op, u32 request, struct sock *sk) ++{ ++ struct aa_label *label; ++ int error; ++ ++ AA_BUG(!sk); ++ AA_BUG(in_interrupt()); ++ ++ /* TODO: switch to begin_current_label ???? */ ++ label = begin_current_label_crit_section(); ++ error = aa_label_sk_perm(label, op, request, sk); ++ end_current_label_crit_section(label); ++ ++ return error; ++} ++ ++ ++int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, ++ struct socket *sock) ++{ ++ AA_BUG(!label); ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ ++ return aa_label_sk_perm(label, op, request, sock->sk); ++} +diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c +index 4ede87c30f8b..5a2aec358322 100644 +--- a/security/apparmor/policy_unpack.c ++++ b/security/apparmor/policy_unpack.c +@@ -275,6 +275,19 @@ static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) + 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((__le16 *) 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)) { +@@ -584,7 +597,7 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) + struct aa_profile *profile = NULL; + const char *tmpname, *tmpns = NULL, *name = NULL; + const char *info = "failed to unpack profile"; +- size_t ns_len; ++ size_t size = 0, ns_len; + struct rhashtable_params params = { 0 }; + char *key = NULL; + struct aa_data *data; +@@ -717,6 +730,38 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) + 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; ++ } ++ if (VERSION_LT(e->version, v7)) { ++ /* pre v7 policy always allowed these */ ++ 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 */ + info = "failed to unpack policydb"; +-- +2.11.0 + diff --git a/kernel-patches/v4.13/0013-apparmor-move-new_null_profile-to-after-profile-look.patch b/kernel-patches/v4.13/0013-apparmor-move-new_null_profile-to-after-profile-look.patch new file mode 100644 index 000000000..f1337f3ce --- /dev/null +++ b/kernel-patches/v4.13/0013-apparmor-move-new_null_profile-to-after-profile-look.patch @@ -0,0 +1,194 @@ +From 50d30adbef98a0b6cc531a9413d05f564eb633ee Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Wed, 16 Aug 2017 08:59:57 -0700 +Subject: [PATCH 13/17] apparmor: move new_null_profile to after profile lookup + fns() + +new_null_profile will need to use some of the profile lookup fns() +so move instead of doing forward fn declarations. + +Signed-off-by: John Johansen +(cherry picked from commit cf1e50dfc6f627bc2989b57076b129c330fb3f0a) +--- + security/apparmor/policy.c | 158 ++++++++++++++++++++++----------------------- + 1 file changed, 79 insertions(+), 79 deletions(-) + +diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c +index 244ea4a4a8f0..a81a384a63b1 100644 +--- a/security/apparmor/policy.c ++++ b/security/apparmor/policy.c +@@ -289,85 +289,6 @@ struct aa_profile *aa_alloc_profile(const char *hname, struct aa_proxy *proxy, + return NULL; + } + +-/** +- * aa_new_null_profile - create or find a null-X learning profile +- * @parent: profile that caused this profile to be created (NOT NULL) +- * @hat: true if the null- learning profile is a hat +- * @base: name to base the null profile off of +- * @gfp: type of allocation +- * +- * Find/Create a null- complain mode profile used in learning mode. The +- * name of the profile is unique and follows the format of parent//null-XXX. +- * where XXX is based on the @name or if that fails or is not supplied +- * a unique number +- * +- * null profiles are added to the profile list but the list does not +- * hold a count on them so that they are automatically released when +- * not in use. +- * +- * Returns: new refcounted profile else NULL on failure +- */ +-struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, +- const char *base, gfp_t gfp) +-{ +- struct aa_profile *profile; +- char *name; +- +- AA_BUG(!parent); +- +- if (base) { +- name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base), +- gfp); +- if (name) { +- sprintf(name, "%s//null-%s", parent->base.hname, base); +- goto name; +- } +- /* fall through to try shorter uniq */ +- } +- +- name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, gfp); +- if (!name) +- return NULL; +- sprintf(name, "%s//null-%x", parent->base.hname, +- atomic_inc_return(&parent->ns->uniq_null)); +- +-name: +- /* lookup to see if this is a dup creation */ +- profile = aa_find_child(parent, basename(name)); +- if (profile) +- goto out; +- +- profile = aa_alloc_profile(name, NULL, gfp); +- if (!profile) +- goto fail; +- +- profile->mode = APPARMOR_COMPLAIN; +- profile->label.flags |= FLAG_NULL; +- if (hat) +- profile->label.flags |= FLAG_HAT; +- profile->path_flags = parent->path_flags; +- +- /* released on free_profile */ +- rcu_assign_pointer(profile->parent, aa_get_profile(parent)); +- profile->ns = aa_get_ns(parent->ns); +- profile->file.dfa = aa_get_dfa(nulldfa); +- profile->policy.dfa = aa_get_dfa(nulldfa); +- +- mutex_lock(&profile->ns->lock); +- __add_profile(&parent->base.profiles, profile); +- mutex_unlock(&profile->ns->lock); +- +- /* refcount released by caller */ +-out: +- kfree(name); +- +- return profile; +- +-fail: +- aa_free_profile(profile); +- return NULL; +-} +- + /* TODO: profile accounting - setup in remove */ + + /** +@@ -559,6 +480,85 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, + } + + /** ++ * aa_new_null_profile - create or find a null-X learning profile ++ * @parent: profile that caused this profile to be created (NOT NULL) ++ * @hat: true if the null- learning profile is a hat ++ * @base: name to base the null profile off of ++ * @gfp: type of allocation ++ * ++ * Find/Create a null- complain mode profile used in learning mode. The ++ * name of the profile is unique and follows the format of parent//null-XXX. ++ * where XXX is based on the @name or if that fails or is not supplied ++ * a unique number ++ * ++ * null profiles are added to the profile list but the list does not ++ * hold a count on them so that they are automatically released when ++ * not in use. ++ * ++ * Returns: new refcounted profile else NULL on failure ++ */ ++struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, ++ const char *base, gfp_t gfp) ++{ ++ struct aa_profile *profile; ++ char *name; ++ ++ AA_BUG(!parent); ++ ++ if (base) { ++ name = kmalloc(strlen(parent->base.hname) + 8 + strlen(base), ++ gfp); ++ if (name) { ++ sprintf(name, "%s//null-%s", parent->base.hname, base); ++ goto name; ++ } ++ /* fall through to try shorter uniq */ ++ } ++ ++ name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, gfp); ++ if (!name) ++ return NULL; ++ sprintf(name, "%s//null-%x", parent->base.hname, ++ atomic_inc_return(&parent->ns->uniq_null)); ++ ++name: ++ /* lookup to see if this is a dup creation */ ++ profile = aa_find_child(parent, basename(name)); ++ if (profile) ++ goto out; ++ ++ profile = aa_alloc_profile(name, NULL, gfp); ++ if (!profile) ++ goto fail; ++ ++ profile->mode = APPARMOR_COMPLAIN; ++ profile->label.flags |= FLAG_NULL; ++ if (hat) ++ profile->label.flags |= FLAG_HAT; ++ profile->path_flags = parent->path_flags; ++ ++ /* released on free_profile */ ++ rcu_assign_pointer(profile->parent, aa_get_profile(parent)); ++ profile->ns = aa_get_ns(parent->ns); ++ profile->file.dfa = aa_get_dfa(nulldfa); ++ profile->policy.dfa = aa_get_dfa(nulldfa); ++ ++ mutex_lock(&profile->ns->lock); ++ __add_profile(&parent->base.profiles, profile); ++ mutex_unlock(&profile->ns->lock); ++ ++ /* refcount released by caller */ ++out: ++ kfree(name); ++ ++ return profile; ++ ++fail: ++ aa_free_profile(profile); ++ return NULL; ++} ++ ++/** + * replacement_allowed - test to see if replacement is allowed + * @profile: profile to test if it can be replaced (MAYBE NULL) + * @noreplace: true if replacement shouldn't be allowed but addition is okay +-- +2.11.0 + diff --git a/kernel-patches/v4.13/0014-apparmor-fix-race-condition-in-null-profile-creation.patch b/kernel-patches/v4.13/0014-apparmor-fix-race-condition-in-null-profile-creation.patch new file mode 100644 index 000000000..ae57b4bde --- /dev/null +++ b/kernel-patches/v4.13/0014-apparmor-fix-race-condition-in-null-profile-creation.patch @@ -0,0 +1,60 @@ +From ab3b869791b6122c7be7e68ca4c08e2c2e8815ac Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Wed, 16 Aug 2017 05:40:49 -0700 +Subject: [PATCH 14/17] apparmor: fix race condition in null profile creation + +There is a race when null- profile is being created between the +initial lookup/creation of the profile and lock/addition of the +profile. This could result in multiple version of a profile being +added to the list which need to be removed/replaced. + +Since these are learning profile their is no affect on mediation. + +Signed-off-by: John Johansen +(cherry picked from commit 3aa3de2a4fb8f33ec62b00998bc6b6c6850d41b1) +--- + security/apparmor/policy.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c +index a81a384a63b1..4243b0c3f0e4 100644 +--- a/security/apparmor/policy.c ++++ b/security/apparmor/policy.c +@@ -500,7 +500,8 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base, + struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, + const char *base, gfp_t gfp) + { +- struct aa_profile *profile; ++ struct aa_profile *p, *profile; ++ const char *bname; + char *name; + + AA_BUG(!parent); +@@ -523,7 +524,8 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, + + name: + /* lookup to see if this is a dup creation */ +- profile = aa_find_child(parent, basename(name)); ++ bname = basename(name); ++ profile = aa_find_child(parent, bname); + if (profile) + goto out; + +@@ -544,7 +546,13 @@ struct aa_profile *aa_new_null_profile(struct aa_profile *parent, bool hat, + profile->policy.dfa = aa_get_dfa(nulldfa); + + mutex_lock(&profile->ns->lock); +- __add_profile(&parent->base.profiles, profile); ++ p = __find_child(&parent->base.profiles, bname); ++ if (p) { ++ aa_free_profile(profile); ++ profile = aa_get_profile(p); ++ } else { ++ __add_profile(&parent->base.profiles, profile); ++ } + mutex_unlock(&profile->ns->lock); + + /* refcount released by caller */ +-- +2.11.0 + diff --git a/kernel-patches/v4.13/0015-apparmor-ensure-unconfined-profiles-have-dfas-initia.patch b/kernel-patches/v4.13/0015-apparmor-ensure-unconfined-profiles-have-dfas-initia.patch new file mode 100644 index 000000000..1ceb9900f --- /dev/null +++ b/kernel-patches/v4.13/0015-apparmor-ensure-unconfined-profiles-have-dfas-initia.patch @@ -0,0 +1,36 @@ +From 7f2cdd6453518ff76c3855255c91306a2b928c9a Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Wed, 16 Aug 2017 05:48:06 -0700 +Subject: [PATCH 15/17] apparmor: ensure unconfined profiles have dfas + initialized + +Generally unconfined has early bailout tests and does not need the +dfas initialized, however if an early bailout test is ever missed +it will result in an oops. + +Be defensive and initialize the unconfined profile to have null dfas +(no permission) so if an early bailout test is missed we fail +closed (no perms granted) instead of oopsing. + +Signed-off-by: John Johansen +(cherry picked from commit 034ad2d248927722bdcd1aedb62634cdc2049113) +--- + security/apparmor/policy_ns.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c +index 351d3bab3a3d..62a3589c62ab 100644 +--- a/security/apparmor/policy_ns.c ++++ b/security/apparmor/policy_ns.c +@@ -112,6 +112,8 @@ static struct aa_ns *alloc_ns(const char *prefix, const char *name) + ns->unconfined->label.flags |= FLAG_IX_ON_NAME_ERROR | + FLAG_IMMUTIBLE | FLAG_NS_COUNT | FLAG_UNCONFINED; + ns->unconfined->mode = APPARMOR_UNCONFINED; ++ ns->unconfined->file.dfa = aa_get_dfa(nulldfa); ++ ns->unconfined->policy.dfa = aa_get_dfa(nulldfa); + + /* ns and ns->unconfined share ns->unconfined refcount */ + ns->unconfined->ns = ns; +-- +2.11.0 + diff --git a/kernel-patches/v4.13/0016-apparmor-fix-incorrect-type-assignment-when-freeing-.patch b/kernel-patches/v4.13/0016-apparmor-fix-incorrect-type-assignment-when-freeing-.patch new file mode 100644 index 000000000..9ff09f805 --- /dev/null +++ b/kernel-patches/v4.13/0016-apparmor-fix-incorrect-type-assignment-when-freeing-.patch @@ -0,0 +1,39 @@ +From 8daf877473653c06a28c86bf72d63ce7e5c1d542 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Wed, 16 Aug 2017 09:33:48 -0700 +Subject: [PATCH 16/17] apparmor: fix incorrect type assignment when freeing + proxies + +sparse reports + +poisoning the proxy->label before freeing the struct is resulting in +a sparse build warning. +../security/apparmor/label.c:52:30: warning: incorrect type in assignment (different address spaces) +../security/apparmor/label.c:52:30: expected struct aa_label [noderef] *label +../security/apparmor/label.c:52:30: got struct aa_label * + +fix with RCU_INIT_POINTER as this is one of those cases where +rcu_assign_pointer() is not needed. + +Signed-off-by: John Johansen +(cherry picked from commit 76e22e212a850bbd16cf49f9c586d4635507e0b5) +--- + security/apparmor/label.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/security/apparmor/label.c b/security/apparmor/label.c +index 52b4ef14840d..c5b99b954580 100644 +--- a/security/apparmor/label.c ++++ b/security/apparmor/label.c +@@ -49,7 +49,7 @@ static void free_proxy(struct aa_proxy *proxy) + /* p->label will not updated any more as p is dead */ + aa_put_label(rcu_dereference_protected(proxy->label, true)); + memset(proxy, 0, sizeof(*proxy)); +- proxy->label = (struct aa_label *) PROXY_POISON; ++ RCU_INIT_POINTER(proxy->label, (struct aa_label *)PROXY_POISON); + kfree(proxy); + } + } +-- +2.11.0 + diff --git a/kernel-patches/v4.13/0017-UBUNTU-SAUCE-apparmor-af_unix-mediation.patch b/kernel-patches/v4.13/0017-UBUNTU-SAUCE-apparmor-af_unix-mediation.patch new file mode 100644 index 000000000..5a1b22082 --- /dev/null +++ b/kernel-patches/v4.13/0017-UBUNTU-SAUCE-apparmor-af_unix-mediation.patch @@ -0,0 +1,1394 @@ +From a3b0cb6676a04cdad5cc357bc422d0398083b435 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Tue, 18 Jul 2017 23:27:23 -0700 +Subject: [PATCH 17/17] UBUNTU: SAUCE: apparmor: af_unix mediation + +af_socket mediation did not make it into 4.14 so add remaining out +of tree patch + +Signed-off-by: John Johansen +--- + security/apparmor/Makefile | 3 +- + security/apparmor/af_unix.c | 651 ++++++++++++++++++++++++++++++++++++ + security/apparmor/apparmorfs.c | 6 + + security/apparmor/file.c | 4 +- + security/apparmor/include/af_unix.h | 114 +++++++ + security/apparmor/include/net.h | 16 +- + security/apparmor/include/path.h | 1 + + security/apparmor/include/policy.h | 2 +- + security/apparmor/lsm.c | 169 ++++++---- + security/apparmor/net.c | 174 +++++++++- + 10 files changed, 1072 insertions(+), 68 deletions(-) + create mode 100644 security/apparmor/af_unix.c + create mode 100644 security/apparmor/include/af_unix.h + +diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile +index dafdd387d42b..ef39226ff4aa 100644 +--- a/security/apparmor/Makefile ++++ b/security/apparmor/Makefile +@@ -4,7 +4,8 @@ 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 secid.o file.o policy_ns.o label.o mount.o net.o ++ resource.o secid.o file.o policy_ns.o label.o mount.o net.o \ ++ af_unix.o + apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o + + clean-files := capability_names.h rlim_names.h net_names.h +diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c +new file mode 100644 +index 000000000000..c6876db2dbde +--- /dev/null ++++ b/security/apparmor/af_unix.c +@@ -0,0 +1,651 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor af_unix fine grained mediation ++ * ++ * Copyright 2014 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/af_unix.h" ++#include "include/apparmor.h" ++#include "include/context.h" ++#include "include/file.h" ++#include "include/label.h" ++#include "include/path.h" ++#include "include/policy.h" ++ ++static inline struct sock *aa_sock(struct unix_sock *u) ++{ ++ return &u->sk; ++} ++ ++static inline int unix_fs_perm(const char *op, u32 mask, struct aa_label *label, ++ struct unix_sock *u, int flags) ++{ ++ AA_BUG(!label); ++ AA_BUG(!u); ++ AA_BUG(!UNIX_FS(aa_sock(u))); ++ ++ if (unconfined(label) || !LABEL_MEDIATES(label, AA_CLASS_FILE)) ++ return 0; ++ ++ mask &= NET_FS_PERMS; ++ if (!u->path.dentry) { ++ struct path_cond cond = { }; ++ struct aa_perms perms = { }; ++ struct aa_profile *profile; ++ ++ /* socket path has been cleared because it is being shutdown ++ * can only fall back to original sun_path request ++ */ ++ struct aa_sk_ctx *ctx = SK_CTX(&u->sk); ++ if (ctx->path.dentry) ++ return aa_path_perm(op, label, &ctx->path, flags, mask, ++ &cond); ++ return fn_for_each_confined(label, profile, ++ ((flags | profile->path_flags) & PATH_MEDIATE_DELETED) ? ++ __aa_path_perm(op, profile, ++ u->addr->name->sun_path, mask, ++ &cond, flags, &perms) : ++ aa_audit_file(profile, &nullperms, op, mask, ++ u->addr->name->sun_path, NULL, ++ NULL, cond.uid, ++ "Failed name lookup - " ++ "deleted entry", -EACCES)); ++ } else { ++ /* the sunpath may not be valid for this ns so use the path */ ++ struct path_cond cond = { u->path.dentry->d_inode->i_uid, ++ u->path.dentry->d_inode->i_mode ++ }; ++ ++ return aa_path_perm(op, label, &u->path, flags, mask, &cond); ++ } ++ ++ return 0; ++} ++ ++/* passing in state returned by PROFILE_MEDIATES_AF */ ++static unsigned int match_to_prot(struct aa_profile *profile, ++ unsigned int state, int type, int protocol, ++ const char **info) ++{ ++ __be16 buffer[2]; ++ buffer[0] = cpu_to_be16(type); ++ buffer[1] = cpu_to_be16(protocol); ++ state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &buffer, ++ 4); ++ if (!state) ++ *info = "failed type and protocol match"; ++ return state; ++} ++ ++static unsigned int match_addr(struct aa_profile *profile, unsigned int state, ++ struct sockaddr_un *addr, int addrlen) ++{ ++ if (addr) ++ /* include leading \0 */ ++ state = aa_dfa_match_len(profile->policy.dfa, state, ++ addr->sun_path, ++ unix_addr_len(addrlen)); ++ else ++ /* anonymous end point */ ++ state = aa_dfa_match_len(profile->policy.dfa, state, "\x01", ++ 1); ++ /* todo change to out of band */ ++ state = aa_dfa_null_transition(profile->policy.dfa, state); ++ return state; ++} ++ ++static unsigned int match_to_local(struct aa_profile *profile, ++ unsigned int state, int type, int protocol, ++ struct sockaddr_un *addr, int addrlen, ++ const char **info) ++{ ++ state = match_to_prot(profile, state, type, protocol, info); ++ if (state) { ++ state = match_addr(profile, state, addr, addrlen); ++ if (state) { ++ /* todo: local label matching */ ++ state = aa_dfa_null_transition(profile->policy.dfa, ++ state); ++ if (!state) ++ *info = "failed local label match"; ++ } else ++ *info = "failed local address match"; ++ } ++ ++ return state; ++} ++ ++static unsigned int match_to_sk(struct aa_profile *profile, ++ unsigned int state, struct unix_sock *u, ++ const char **info) ++{ ++ struct sockaddr_un *addr = NULL; ++ int addrlen = 0; ++ ++ if (u->addr) { ++ addr = u->addr->name; ++ addrlen = u->addr->len; ++ } ++ ++ return match_to_local(profile, state, u->sk.sk_type, u->sk.sk_protocol, ++ addr, addrlen, info); ++} ++ ++#define CMD_ADDR 1 ++#define CMD_LISTEN 2 ++#define CMD_OPT 4 ++ ++static inline unsigned int match_to_cmd(struct aa_profile *profile, ++ unsigned int state, struct unix_sock *u, ++ char cmd, const char **info) ++{ ++ state = match_to_sk(profile, state, u, info); ++ if (state) { ++ state = aa_dfa_match_len(profile->policy.dfa, state, &cmd, 1); ++ if (!state) ++ *info = "failed cmd selection match"; ++ } ++ ++ return state; ++} ++ ++static inline unsigned int match_to_peer(struct aa_profile *profile, ++ unsigned int state, ++ struct unix_sock *u, ++ struct sockaddr_un *peer_addr, ++ int peer_addrlen, ++ const char **info) ++{ ++ state = match_to_cmd(profile, state, u, CMD_ADDR, info); ++ if (state) { ++ state = match_addr(profile, state, peer_addr, peer_addrlen); ++ if (!state) ++ *info = "failed peer address match"; ++ } ++ return state; ++} ++ ++static int do_perms(struct aa_profile *profile, unsigned int state, u32 request, ++ struct common_audit_data *sa) ++{ ++ struct aa_perms perms; ++ ++ AA_BUG(!profile); ++ ++ aa_compute_perms(profile->policy.dfa, state, &perms); ++ aa_apply_modes_to_perms(profile, &perms); ++ return aa_check_perms(profile, &perms, request, sa, ++ audit_net_cb); ++} ++ ++static int match_label(struct aa_profile *profile, struct aa_profile *peer, ++ unsigned int state, u32 request, ++ struct common_audit_data *sa) ++{ ++ AA_BUG(!profile); ++ AA_BUG(!peer); ++ ++ aad(sa)->peer = &peer->label; ++ ++ if (state) { ++ state = aa_dfa_match(profile->policy.dfa, state, ++ peer->base.hname); ++ if (!state) ++ aad(sa)->info = "failed peer label match"; ++ } ++ return do_perms(profile, state, request, sa); ++} ++ ++ ++/* unix sock creation comes before we know if the socket will be an fs ++ * socket ++ * v6 - semantics are handled by mapping in profile load ++ * v7 - semantics require sock create for tasks creating an fs socket. ++ */ ++static int profile_create_perm(struct aa_profile *profile, int family, ++ int type, int protocol) ++{ ++ unsigned int state; ++ DEFINE_AUDIT_NET(sa, OP_CREATE, NULL, family, type, protocol); ++ ++ AA_BUG(!profile); ++ AA_BUG(profile_unconfined(profile)); ++ ++ if ((state = PROFILE_MEDIATES_AF(profile, AF_UNIX))) { ++ state = match_to_prot(profile, state, type, protocol, ++ &aad(&sa)->info); ++ return do_perms(profile, state, AA_MAY_CREATE, &sa); ++ } ++ ++ return aa_profile_af_perm(profile, &sa, AA_MAY_CREATE, family, type); ++} ++ ++int aa_unix_create_perm(struct aa_label *label, int family, int type, ++ int protocol) ++{ ++ struct aa_profile *profile; ++ ++ if (unconfined(label)) ++ return 0; ++ ++ return fn_for_each_confined(label, profile, ++ profile_create_perm(profile, family, type, protocol)); ++} ++ ++ ++static inline int profile_sk_perm(struct aa_profile *profile, const char *op, ++ u32 request, struct sock *sk) ++{ ++ unsigned int state; ++ DEFINE_AUDIT_SK(sa, op, sk); ++ ++ AA_BUG(!profile); ++ AA_BUG(!sk); ++ AA_BUG(UNIX_FS(sk)); ++ AA_BUG(profile_unconfined(profile)); ++ ++ state = PROFILE_MEDIATES_AF(profile, AF_UNIX); ++ if (state) { ++ state = match_to_sk(profile, state, unix_sk(sk), ++ &aad(&sa)->info); ++ return do_perms(profile, state, request, &sa); ++ } ++ ++ return aa_profile_af_sk_perm(profile, &sa, request, sk); ++} ++ ++int aa_unix_label_sk_perm(struct aa_label *label, const char *op, u32 request, ++ struct sock *sk) ++{ ++ struct aa_profile *profile; ++ ++ return fn_for_each_confined(label, profile, ++ profile_sk_perm(profile, op, request, sk)); ++} ++ ++static int unix_label_sock_perm(struct aa_label *label, const char *op, u32 request, ++ struct socket *sock) ++{ ++ if (unconfined(label)) ++ return 0; ++ if (UNIX_FS(sock->sk)) ++ return unix_fs_perm(op, request, label, unix_sk(sock->sk), 0); ++ ++ return aa_unix_label_sk_perm(label, op, request, sock->sk); ++} ++ ++/* revaliation, get/set attr */ ++int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock) ++{ ++ struct aa_label *label; ++ int error; ++ ++ label = begin_current_label_crit_section(); ++ error = unix_label_sock_perm(label, op, request, sock); ++ end_current_label_crit_section(label); ++ ++ return error; ++} ++ ++static int profile_bind_perm(struct aa_profile *profile, struct sock *sk, ++ struct sockaddr *addr, int addrlen) ++{ ++ unsigned int state; ++ DEFINE_AUDIT_SK(sa, OP_BIND, sk); ++ ++ AA_BUG(!profile); ++ AA_BUG(!sk); ++ AA_BUG(addr->sa_family != AF_UNIX); ++ AA_BUG(profile_unconfined(profile)); ++ AA_BUG(unix_addr_fs(addr, addrlen)); ++ ++ state = PROFILE_MEDIATES_AF(profile, AF_UNIX); ++ if (state) { ++ /* bind for abstract socket */ ++ aad(&sa)->net.addr = unix_addr(addr); ++ aad(&sa)->net.addrlen = addrlen; ++ ++ state = match_to_local(profile, state, ++ sk->sk_type, sk->sk_protocol, ++ unix_addr(addr), addrlen, ++ &aad(&sa)->info); ++ return do_perms(profile, state, AA_MAY_BIND, &sa); ++ } ++ ++ return aa_profile_af_sk_perm(profile, &sa, AA_MAY_BIND, sk); ++} ++ ++int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address, ++ int addrlen) ++{ ++ struct aa_profile *profile; ++ struct aa_label *label; ++ int error = 0; ++ ++ label = begin_current_label_crit_section(); ++ /* fs bind is handled by mknod */ ++ if (!(unconfined(label) || unix_addr_fs(address, addrlen))) ++ error = fn_for_each_confined(label, profile, ++ profile_bind_perm(profile, sock->sk, address, ++ addrlen)); ++ end_current_label_crit_section(label); ++ ++ return error; ++} ++ ++int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address, ++ int addrlen) ++{ ++ /* unix connections are covered by the ++ * - unix_stream_connect (stream) and unix_may_send hooks (dgram) ++ * - fs connect is handled by open ++ */ ++ return 0; ++} ++ ++static int profile_listen_perm(struct aa_profile *profile, struct sock *sk, ++ int backlog) ++{ ++ unsigned int state; ++ DEFINE_AUDIT_SK(sa, OP_LISTEN, sk); ++ ++ AA_BUG(!profile); ++ AA_BUG(!sk); ++ AA_BUG(UNIX_FS(sk)); ++ AA_BUG(profile_unconfined(profile)); ++ ++ state = PROFILE_MEDIATES_AF(profile, AF_UNIX); ++ if (state) { ++ __be16 b = cpu_to_be16(backlog); ++ ++ state = match_to_cmd(profile, state, unix_sk(sk), CMD_LISTEN, ++ &aad(&sa)->info); ++ if (state) { ++ state = aa_dfa_match_len(profile->policy.dfa, state, ++ (char *) &b, 2); ++ if (!state) ++ aad(&sa)->info = "failed listen backlog match"; ++ } ++ return do_perms(profile, state, AA_MAY_LISTEN, &sa); ++ } ++ ++ return aa_profile_af_sk_perm(profile, &sa, AA_MAY_LISTEN, sk); ++} ++ ++int aa_unix_listen_perm(struct socket *sock, int backlog) ++{ ++ struct aa_profile *profile; ++ struct aa_label *label; ++ int error = 0; ++ ++ label = begin_current_label_crit_section(); ++ if (!(unconfined(label) || UNIX_FS(sock->sk))) ++ error = fn_for_each_confined(label, profile, ++ profile_listen_perm(profile, sock->sk, ++ backlog)); ++ end_current_label_crit_section(label); ++ ++ return error; ++} ++ ++ ++static inline int profile_accept_perm(struct aa_profile *profile, ++ struct sock *sk, ++ struct sock *newsk) ++{ ++ unsigned int state; ++ DEFINE_AUDIT_SK(sa, OP_ACCEPT, sk); ++ ++ AA_BUG(!profile); ++ AA_BUG(!sk); ++ AA_BUG(UNIX_FS(sk)); ++ AA_BUG(profile_unconfined(profile)); ++ ++ state = PROFILE_MEDIATES_AF(profile, AF_UNIX); ++ if (state) { ++ state = match_to_sk(profile, state, unix_sk(sk), ++ &aad(&sa)->info); ++ return do_perms(profile, state, AA_MAY_ACCEPT, &sa); ++ } ++ ++ return aa_profile_af_sk_perm(profile, &sa, AA_MAY_ACCEPT, sk); ++} ++ ++/* ability of sock to connect, not peer address binding */ ++int aa_unix_accept_perm(struct socket *sock, struct socket *newsock) ++{ ++ struct aa_profile *profile; ++ struct aa_label *label; ++ int error = 0; ++ ++ label = begin_current_label_crit_section(); ++ if (!(unconfined(label) || UNIX_FS(sock->sk))) ++ error = fn_for_each_confined(label, profile, ++ profile_accept_perm(profile, sock->sk, ++ newsock->sk)); ++ end_current_label_crit_section(label); ++ ++ return error; ++} ++ ++ ++/* dgram handled by unix_may_sendmsg, right to send on stream done at connect ++ * could do per msg unix_stream here ++ */ ++/* sendmsg, recvmsg */ ++int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock, ++ struct msghdr *msg, int size) ++{ ++ return 0; ++} ++ ++ ++static int profile_opt_perm(struct aa_profile *profile, const char *op, u32 request, ++ struct sock *sk, int level, int optname) ++{ ++ unsigned int state; ++ DEFINE_AUDIT_SK(sa, op, sk); ++ ++ AA_BUG(!profile); ++ AA_BUG(!sk); ++ AA_BUG(UNIX_FS(sk)); ++ AA_BUG(profile_unconfined(profile)); ++ ++ state = PROFILE_MEDIATES_AF(profile, AF_UNIX); ++ if (state) { ++ __be16 b = cpu_to_be16(optname); ++ ++ state = match_to_cmd(profile, state, unix_sk(sk), CMD_OPT, ++ &aad(&sa)->info); ++ if (state) { ++ state = aa_dfa_match_len(profile->policy.dfa, state, ++ (char *) &b, 2); ++ if (!state) ++ aad(&sa)->info = "failed sockopt match"; ++ } ++ return do_perms(profile, state, request, &sa); ++ } ++ ++ return aa_profile_af_sk_perm(profile, &sa, request, sk); ++} ++ ++int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level, ++ int optname) ++{ ++ struct aa_profile *profile; ++ struct aa_label *label; ++ int error = 0; ++ ++ label = begin_current_label_crit_section(); ++ if (!(unconfined(label) || UNIX_FS(sock->sk))) ++ error = fn_for_each_confined(label, profile, ++ profile_opt_perm(profile, op, request, ++ sock->sk, level, optname)); ++ end_current_label_crit_section(label); ++ ++ return error; ++} ++ ++/* null peer_label is allowed, in which case the peer_sk label is used */ ++static int profile_peer_perm(struct aa_profile *profile, const char *op, u32 request, ++ struct sock *sk, struct sock *peer_sk, ++ struct aa_label *peer_label, ++ struct common_audit_data *sa) ++{ ++ unsigned int state; ++ ++ AA_BUG(!profile); ++ AA_BUG(profile_unconfined(profile)); ++ AA_BUG(!sk); ++ AA_BUG(!peer_sk); ++ AA_BUG(UNIX_FS(peer_sk)); ++ ++ state = PROFILE_MEDIATES_AF(profile, AF_UNIX); ++ if (state) { ++ struct aa_sk_ctx *peer_ctx = SK_CTX(peer_sk); ++ struct aa_profile *peerp; ++ struct sockaddr_un *addr = NULL; ++ int len = 0; ++ if (unix_sk(peer_sk)->addr) { ++ addr = unix_sk(peer_sk)->addr->name; ++ len = unix_sk(peer_sk)->addr->len; ++ } ++ state = match_to_peer(profile, state, unix_sk(sk), ++ addr, len, &aad(sa)->info); ++ if (!peer_label) ++ peer_label = peer_ctx->label; ++ return fn_for_each_in_ns(peer_label, peerp, ++ match_label(profile, peerp, state, request, ++ sa)); ++ } ++ ++ return aa_profile_af_sk_perm(profile, sa, request, sk); ++} ++ ++/** ++ * ++ * Requires: lock held on both @sk and @peer_sk ++ */ ++int aa_unix_peer_perm(struct aa_label *label, const char *op, u32 request, ++ struct sock *sk, struct sock *peer_sk, ++ struct aa_label *peer_label) ++{ ++ struct unix_sock *peeru = unix_sk(peer_sk); ++ struct unix_sock *u = unix_sk(sk); ++ ++ AA_BUG(!label); ++ AA_BUG(!sk); ++ AA_BUG(!peer_sk); ++ ++ if (UNIX_FS(aa_sock(peeru))) ++ return unix_fs_perm(op, request, label, peeru, 0); ++ else if (UNIX_FS(aa_sock(u))) ++ return unix_fs_perm(op, request, label, u, 0); ++ else { ++ struct aa_profile *profile; ++ DEFINE_AUDIT_SK(sa, op, sk); ++ aad(&sa)->net.peer_sk = peer_sk; ++ ++ /* TODO: ns!!! */ ++ if (!net_eq(sock_net(sk), sock_net(peer_sk))) { ++ ; ++ } ++ ++ if (unconfined(label)) ++ return 0; ++ ++ return fn_for_each_confined(label, profile, ++ profile_peer_perm(profile, op, request, sk, ++ peer_sk, peer_label, &sa)); ++ } ++} ++ ++ ++/* from net/unix/af_unix.c */ ++static void unix_state_double_lock(struct sock *sk1, struct sock *sk2) ++{ ++ if (unlikely(sk1 == sk2) || !sk2) { ++ unix_state_lock(sk1); ++ return; ++ } ++ if (sk1 < sk2) { ++ unix_state_lock(sk1); ++ unix_state_lock_nested(sk2); ++ } else { ++ unix_state_lock(sk2); ++ unix_state_lock_nested(sk1); ++ } ++} ++ ++static void unix_state_double_unlock(struct sock *sk1, struct sock *sk2) ++{ ++ if (unlikely(sk1 == sk2) || !sk2) { ++ unix_state_unlock(sk1); ++ return; ++ } ++ unix_state_unlock(sk1); ++ unix_state_unlock(sk2); ++} ++ ++int aa_unix_file_perm(struct aa_label *label, const char *op, u32 request, ++ struct socket *sock) ++{ ++ struct sock *peer_sk = NULL; ++ u32 sk_req = request & ~NET_PEER_MASK; ++ int error = 0; ++ ++ AA_BUG(!label); ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(sock->sk->sk_family != AF_UNIX); ++ ++ /* TODO: update sock label with new task label */ ++ unix_state_lock(sock->sk); ++ peer_sk = unix_peer(sock->sk); ++ if (peer_sk) ++ sock_hold(peer_sk); ++ if (!unix_connected(sock) && sk_req) { ++ error = unix_label_sock_perm(label, op, sk_req, sock); ++ if (!error) { ++ // update label ++ } ++ } ++ unix_state_unlock(sock->sk); ++ if (!peer_sk) ++ return error; ++ ++ unix_state_double_lock(sock->sk, peer_sk); ++ if (UNIX_FS(sock->sk)) { ++ error = unix_fs_perm(op, request, label, unix_sk(sock->sk), ++ PATH_SOCK_COND); ++ } else if (UNIX_FS(peer_sk)) { ++ error = unix_fs_perm(op, request, label, unix_sk(peer_sk), ++ PATH_SOCK_COND); ++ } else { ++ struct aa_sk_ctx *pctx = SK_CTX(peer_sk); ++ if (sk_req) ++ error = aa_unix_label_sk_perm(label, op, sk_req, ++ sock->sk); ++ last_error(error, ++ xcheck(aa_unix_peer_perm(label, op, ++ MAY_READ | MAY_WRITE, ++ sock->sk, peer_sk, NULL), ++ aa_unix_peer_perm(pctx->label, op, ++ MAY_READ | MAY_WRITE, ++ peer_sk, sock->sk, label))); ++ } ++ ++ unix_state_double_unlock(sock->sk, peer_sk); ++ sock_put(peer_sk); ++ ++ return error; ++} +diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c +index 125dad5c3fde..20cdb1c4b266 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -2187,6 +2187,11 @@ static struct aa_sfs_entry aa_sfs_entry_ns[] = { + { } + }; + ++static struct aa_sfs_entry aa_sfs_entry_dbus[] = { ++ AA_SFS_FILE_STRING("mask", "acquire send receive"), ++ { } ++}; ++ + static struct aa_sfs_entry aa_sfs_entry_query_label[] = { + AA_SFS_FILE_STRING("perms", "allow deny audit quiet"), + AA_SFS_FILE_BOOLEAN("data", 1), +@@ -2210,6 +2215,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { + AA_SFS_DIR("caps", aa_sfs_entry_caps), + AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace), + AA_SFS_DIR("signal", aa_sfs_entry_signal), ++ AA_SFS_DIR("dbus", aa_sfs_entry_dbus), + AA_SFS_DIR("query", aa_sfs_entry_query), + { } + }; +diff --git a/security/apparmor/file.c b/security/apparmor/file.c +index db80221891c6..e62791106900 100644 +--- a/security/apparmor/file.c ++++ b/security/apparmor/file.c +@@ -16,6 +16,7 @@ + #include + #include + ++#include "include/af_unix.h" + #include "include/apparmor.h" + #include "include/audit.h" + #include "include/context.h" +@@ -289,7 +290,8 @@ int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name, + { + int e = 0; + +- if (profile_unconfined(profile)) ++ if (profile_unconfined(profile) || ++ ((flags & PATH_SOCK_COND) && !PROFILE_MEDIATES_AF(profile, AF_UNIX))) + return 0; + aa_str_perms(profile->file.dfa, profile->file.start, name, cond, perms); + if (request & ~perms->allow) +diff --git a/security/apparmor/include/af_unix.h b/security/apparmor/include/af_unix.h +new file mode 100644 +index 000000000000..d1b7f2316be4 +--- /dev/null ++++ b/security/apparmor/include/af_unix.h +@@ -0,0 +1,114 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor af_unix fine grained mediation ++ * ++ * Copyright 2014 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_AF_UNIX_H ++ ++#include ++ ++#include "label.h" ++//#include "include/net.h" ++ ++#define unix_addr_len(L) ((L) - sizeof(sa_family_t)) ++#define unix_abstract_name_len(L) (unix_addr_len(L) - 1) ++#define unix_abstract_len(U) (unix_abstract_name_len((U)->addr->len)) ++#define addr_unix_abstract_name(B) ((B)[0] == 0) ++#define addr_unix_anonymous(U) (addr_unix_len(U) <= 0) ++#define addr_unix_abstract(U) (!addr_unix_anonymous(U) && addr_unix_abstract_name((U)->addr)) ++//#define unix_addr_fs(U) (!unix_addr_anonymous(U) && !unix_addr_abstract_name((U)->addr)) ++ ++#define unix_addr(A) ((struct sockaddr_un *)(A)) ++#define unix_addr_anon(A, L) ((A) && unix_addr_len(L) <= 0) ++#define unix_addr_fs(A, L) (!unix_addr_anon(A, L) && !addr_unix_abstract_name(unix_addr(A)->sun_path)) ++ ++#define UNIX_ANONYMOUS(U) (!unix_sk(U)->addr) ++/* from net/unix/af_unix.c */ ++#define UNIX_ABSTRACT(U) (!UNIX_ANONYMOUS(U) && \ ++ unix_sk(U)->addr->hash < UNIX_HASH_SIZE) ++#define UNIX_FS(U) (!UNIX_ANONYMOUS(U) && unix_sk(U)->addr->name->sun_path[0]) ++#define unix_peer(sk) (unix_sk(sk)->peer) ++#define unix_connected(S) ((S)->state == SS_CONNECTED) ++ ++static inline void print_unix_addr(struct sockaddr_un *A, int L) ++{ ++ char *buf = (A) ? (char *) &(A)->sun_path : NULL; ++ int len = unix_addr_len(L); ++ if (!buf || len <= 0) ++ printk(" "); ++ else if (buf[0]) ++ printk(" %s", buf); ++ else ++ /* abstract name len includes leading \0 */ ++ printk(" %d @%.*s", len - 1, len - 1, buf+1); ++}; ++ ++/* ++ printk("%s: %s: f %d, t %d, p %d", __FUNCTION__, \ ++ #SK , \ ++*/ ++#define print_unix_sk(SK) \ ++do { \ ++ struct unix_sock *u = unix_sk(SK); \ ++ printk("%s: f %d, t %d, p %d", #SK , \ ++ (SK)->sk_family, (SK)->sk_type, (SK)->sk_protocol); \ ++ if (u->addr) \ ++ print_unix_addr(u->addr->name, u->addr->len); \ ++ else \ ++ print_unix_addr(NULL, sizeof(sa_family_t)); \ ++ /* printk("\n");*/ \ ++} while (0) ++ ++#define print_sk(SK) \ ++do { \ ++ if (!(SK)) { \ ++ printk("%s: %s is null\n", __FUNCTION__, #SK); \ ++ } else if ((SK)->sk_family == PF_UNIX) { \ ++ print_unix_sk(SK); \ ++ printk("\n"); \ ++ } else { \ ++ printk("%s: %s: family %d\n", __FUNCTION__, #SK , \ ++ (SK)->sk_family); \ ++ } \ ++} while (0) ++ ++#define print_sock_addr(U) \ ++do { \ ++ printk("%s:\n", __FUNCTION__); \ ++ printk(" sock %s:", sock_ctx && sock_ctx->label ? aa_label_printk(sock_ctx->label, GFP_ATOMIC); : ""); print_sk(sock); \ ++ printk(" other %s:", other_ctx && other_ctx->label ? aa_label_printk(other_ctx->label, GFP_ATOMIC); : ""); print_sk(other); \ ++ printk(" new %s", new_ctx && new_ctx->label ? aa_label_printk(new_ctx->label, GFP_ATOMIC); : ""); print_sk(newsk); \ ++} while (0) ++ ++ ++ ++ ++int aa_unix_peer_perm(struct aa_label *label, const char *op, u32 request, ++ struct sock *sk, struct sock *peer_sk, ++ struct aa_label *peer_label); ++int aa_unix_label_sk_perm(struct aa_label *label, const char *op, u32 request, ++ struct sock *sk); ++int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock); ++int aa_unix_create_perm(struct aa_label *label, int family, int type, ++ int protocol); ++int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address, ++ int addrlen); ++int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address, ++ int addrlen); ++int aa_unix_listen_perm(struct socket *sock, int backlog); ++int aa_unix_accept_perm(struct socket *sock, struct socket *newsock); ++int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock, ++ struct msghdr *msg, int size); ++int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level, ++ int optname); ++int aa_unix_file_perm(struct aa_label *label, const char *op, u32 request, ++ struct socket *sock); ++ ++#endif /* __AA_AF_UNIX_H */ +diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h +index 140c8efcf364..0ae45240c352 100644 +--- a/security/apparmor/include/net.h ++++ b/security/apparmor/include/net.h +@@ -90,8 +90,6 @@ extern struct aa_sfs_entry aa_sfs_entry_network[]; + void audit_net_cb(struct audit_buffer *ab, void *va); + int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, + u32 request, u16 family, int type); +-int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, +- int type, int protocol); + static inline int aa_profile_af_sk_perm(struct aa_profile *profile, + struct common_audit_data *sa, + u32 request, +@@ -100,8 +98,20 @@ static inline int aa_profile_af_sk_perm(struct aa_profile *profile, + return aa_profile_af_perm(profile, sa, request, sk->sk_family, + sk->sk_type); + } +-int aa_sk_perm(const char *op, u32 request, struct sock *sk); + ++int aa_sock_perm(const char *op, u32 request, struct socket *sock); ++int aa_sock_create_perm(struct aa_label *label, int family, int type, ++ int protocol); ++int aa_sock_bind_perm(struct socket *sock, struct sockaddr *address, ++ int addrlen); ++int aa_sock_connect_perm(struct socket *sock, struct sockaddr *address, ++ int addrlen); ++int aa_sock_listen_perm(struct socket *sock, int backlog); ++int aa_sock_accept_perm(struct socket *sock, struct socket *newsock); ++int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, ++ struct msghdr *msg, int size); ++int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, int level, ++ int optname); + int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock); + +diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h +index 05fb3305671e..26762db2207d 100644 +--- a/security/apparmor/include/path.h ++++ b/security/apparmor/include/path.h +@@ -18,6 +18,7 @@ + + enum path_flags { + PATH_IS_DIR = 0x1, /* path is a directory */ ++ PATH_SOCK_COND = 0x2, + PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */ + PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */ + PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ +diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h +index 4364088a0b9e..26660a1a50b0 100644 +--- a/security/apparmor/include/policy.h ++++ b/security/apparmor/include/policy.h +@@ -226,7 +226,7 @@ static inline unsigned int PROFILE_MEDIATES_SAFE(struct aa_profile *profile, + static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile, + u16 AF) { + unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET); +- u16 be_af = cpu_to_be16(AF); ++ __be16 be_af = cpu_to_be16(AF); + + if (!state) + return 0; +diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c +index cc5ab23a2d84..0ede66d80a53 100644 +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -26,6 +26,7 @@ + #include + #include + ++#include "include/af_unix.h" + #include "include/apparmor.h" + #include "include/apparmorfs.h" + #include "include/audit.h" +@@ -782,16 +783,96 @@ static void apparmor_sk_clone_security(const struct sock *sk, + path_get(&new->path); + } + +-static int aa_sock_create_perm(struct aa_label *label, int family, int type, +- int protocol) ++static struct path *UNIX_FS_CONN_PATH(struct sock *sk, struct sock *newsk) + { +- AA_BUG(!label); +- AA_BUG(in_interrupt()); ++ if (sk->sk_family == PF_UNIX && UNIX_FS(sk)) ++ return &unix_sk(sk)->path; ++ else if (newsk->sk_family == PF_UNIX && UNIX_FS(newsk)) ++ return &unix_sk(newsk)->path; ++ return NULL; ++} ++ ++/** ++ * apparmor_unix_stream_connect - check perms before making unix domain conn ++ * ++ * peer is locked when this hook is called ++ */ ++static int apparmor_unix_stream_connect(struct sock *sk, struct sock *peer_sk, ++ struct sock *newsk) ++{ ++ struct aa_sk_ctx *sk_ctx = SK_CTX(sk); ++ struct aa_sk_ctx *peer_ctx = SK_CTX(peer_sk); ++ struct aa_sk_ctx *new_ctx = SK_CTX(newsk); ++ struct aa_label *label; ++ struct path *path; ++ int error; + +- return aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, type, +- protocol); ++ label = __begin_current_label_crit_section(); ++ error = aa_unix_peer_perm(label, OP_CONNECT, ++ (AA_MAY_CONNECT | AA_MAY_SEND | AA_MAY_RECEIVE), ++ sk, peer_sk, NULL); ++ if (!UNIX_FS(peer_sk)) { ++ last_error(error, ++ aa_unix_peer_perm(peer_ctx->label, OP_CONNECT, ++ (AA_MAY_ACCEPT | AA_MAY_SEND | AA_MAY_RECEIVE), ++ peer_sk, sk, label)); ++ } ++ __end_current_label_crit_section(label); ++ ++ if (error) ++ return error; ++ ++ /* label newsk if it wasn't labeled in post_create. Normally this ++ * would be done in sock_graft, but because we are directly looking ++ * at the peer_sk to obtain peer_labeling for unix socks this ++ * does not work ++ */ ++ if (!new_ctx->label) ++ new_ctx->label = aa_get_label(peer_ctx->label); ++ ++ /* Cross reference the peer labels for SO_PEERSEC */ ++ if (new_ctx->peer) ++ aa_put_label(new_ctx->peer); ++ ++ if (sk_ctx->peer) ++ aa_put_label(sk_ctx->peer); ++ ++ new_ctx->peer = aa_get_label(sk_ctx->label); ++ sk_ctx->peer = aa_get_label(peer_ctx->label); ++ ++ path = UNIX_FS_CONN_PATH(sk, peer_sk); ++ if (path) { ++ new_ctx->path = *path; ++ sk_ctx->path = *path; ++ path_get(path); ++ path_get(path); ++ } ++ return 0; + } + ++/** ++ * apparmor_unix_may_send - check perms before conn or sending unix dgrams ++ * ++ * other is locked when this hook is called ++ * ++ * dgram connect calls may_send, peer setup but path not copied????? ++ */ ++static int apparmor_unix_may_send(struct socket *sock, struct socket *peer) ++{ ++ struct aa_sk_ctx *peer_ctx = SK_CTX(peer->sk); ++ struct aa_label *label; ++ int error; ++ ++ label = __begin_current_label_crit_section(); ++ error = xcheck(aa_unix_peer_perm(label, OP_SENDMSG, AA_MAY_SEND, ++ sock->sk, peer->sk, NULL), ++ aa_unix_peer_perm(peer_ctx->label, OP_SENDMSG, ++ AA_MAY_RECEIVE, ++ peer->sk, sock->sk, label)); ++ __end_current_label_crit_section(label); ++ ++ return error; ++} + + /** + * apparmor_socket_create - check perms before creating a new socket +@@ -849,12 +930,7 @@ static int apparmor_socket_post_create(struct socket *sock, int family, + static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) + { +- AA_BUG(!sock); +- AA_BUG(!sock->sk); +- AA_BUG(!address); +- AA_BUG(in_interrupt()); +- +- return aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk); ++ return aa_sock_bind_perm(sock, address, addrlen); + } + + /** +@@ -863,12 +939,7 @@ static int apparmor_socket_bind(struct socket *sock, + static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) + { +- AA_BUG(!sock); +- AA_BUG(!sock->sk); +- AA_BUG(!address); +- AA_BUG(in_interrupt()); +- +- return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk); ++ return aa_sock_connect_perm(sock, address, addrlen); + } + + /** +@@ -876,11 +947,7 @@ static int apparmor_socket_connect(struct socket *sock, + */ + static int apparmor_socket_listen(struct socket *sock, int backlog) + { +- AA_BUG(!sock); +- AA_BUG(!sock->sk); +- AA_BUG(in_interrupt()); +- +- return aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk); ++ return aa_sock_listen_perm(sock, backlog); + } + + /** +@@ -891,23 +958,7 @@ static int apparmor_socket_listen(struct socket *sock, int backlog) + */ + static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) + { +- AA_BUG(!sock); +- AA_BUG(!sock->sk); +- AA_BUG(!newsock); +- AA_BUG(in_interrupt()); +- +- return aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk); +-} +- +-static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, +- struct msghdr *msg, int size) +-{ +- AA_BUG(!sock); +- AA_BUG(!sock->sk); +- AA_BUG(!msg); +- AA_BUG(in_interrupt()); +- +- return aa_sk_perm(op, request, sock->sk); ++ return aa_sock_accept_perm(sock, newsock); + } + + /** +@@ -928,16 +979,6 @@ static int apparmor_socket_recvmsg(struct socket *sock, + return aa_sock_msg_perm(OP_RECVMSG, AA_MAY_RECEIVE, sock, msg, size); + } + +-/* revaliation, get/set attr, shutdown */ +-static int aa_sock_perm(const char *op, u32 request, struct socket *sock) +-{ +- AA_BUG(!sock); +- AA_BUG(!sock->sk); +- AA_BUG(in_interrupt()); +- +- return aa_sk_perm(op, request, sock->sk); +-} +- + /** + * apparmor_socket_getsockname - check perms before getting the local address + */ +@@ -954,17 +995,6 @@ static int apparmor_socket_getpeername(struct socket *sock) + return aa_sock_perm(OP_GETPEERNAME, AA_MAY_GETATTR, sock); + } + +-/* revaliation, get/set attr, opt */ +-static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, +- int level, int optname) +-{ +- AA_BUG(!sock); +- AA_BUG(!sock->sk); +- AA_BUG(in_interrupt()); +- +- return aa_sk_perm(op, request, sock->sk); +-} +- + /** + * apparmor_getsockopt - check perms before getting socket options + */ +@@ -1009,11 +1039,25 @@ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) + + static struct aa_label *sk_peer_label(struct sock *sk) + { ++ struct sock *peer_sk; + struct aa_sk_ctx *ctx = SK_CTX(sk); + + if (ctx->peer) + return ctx->peer; + ++ if (sk->sk_family != PF_UNIX) ++ return ERR_PTR(-ENOPROTOOPT); ++ ++ /* check for sockpair peering which does not go through ++ * security_unix_stream_connect ++ */ ++ peer_sk = unix_peer(sk); ++ if (peer_sk) { ++ ctx = SK_CTX(peer_sk); ++ if (ctx->label) ++ return ctx->label; ++ } ++ + return ERR_PTR(-ENOPROTOOPT); + } + +@@ -1137,6 +1181,9 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { + LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security), + LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security), + ++ LSM_HOOK_INIT(unix_stream_connect, apparmor_unix_stream_connect), ++ LSM_HOOK_INIT(unix_may_send, apparmor_unix_may_send), ++ + LSM_HOOK_INIT(socket_create, apparmor_socket_create), + LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_create), + LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), +diff --git a/security/apparmor/net.c b/security/apparmor/net.c +index 33d54435f8d6..dd1953b08e58 100644 +--- a/security/apparmor/net.c ++++ b/security/apparmor/net.c +@@ -12,6 +12,7 @@ + * License. + */ + ++#include "include/af_unix.h" + #include "include/apparmor.h" + #include "include/audit.h" + #include "include/context.h" +@@ -24,6 +25,7 @@ + + struct aa_sfs_entry aa_sfs_entry_network[] = { + AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), ++ AA_SFS_FILE_BOOLEAN("af_unix", 1), + { } + }; + +@@ -69,6 +71,36 @@ static const char * const net_mask_names[] = { + "unknown", + }; + ++static void audit_unix_addr(struct audit_buffer *ab, const char *str, ++ struct sockaddr_un *addr, int addrlen) ++{ ++ int len = unix_addr_len(addrlen); ++ ++ if (!addr || len <= 0) { ++ audit_log_format(ab, " %s=none", str); ++ } else if (addr->sun_path[0]) { ++ audit_log_format(ab, " %s=", str); ++ audit_log_untrustedstring(ab, addr->sun_path); ++ } else { ++ audit_log_format(ab, " %s=\"@", str); ++ if (audit_string_contains_control(&addr->sun_path[1], len - 1)) ++ audit_log_n_hex(ab, &addr->sun_path[1], len - 1); ++ else ++ audit_log_format(ab, "%.*s", len - 1, ++ &addr->sun_path[1]); ++ audit_log_format(ab, "\""); ++ } ++} ++ ++static void audit_unix_sk_addr(struct audit_buffer *ab, const char *str, ++ struct sock *sk) ++{ ++ struct unix_sock *u = unix_sk(sk); ++ if (u && u->addr) ++ audit_unix_addr(ab, str, u->addr->name, u->addr->len); ++ else ++ audit_unix_addr(ab, str, NULL, 0); ++} + + /* audit callback for net specific fields */ + void audit_net_cb(struct audit_buffer *ab, void *va) +@@ -98,6 +130,23 @@ void audit_net_cb(struct audit_buffer *ab, void *va) + net_mask_names, NET_PERMS_MASK); + } + } ++ if (sa->u.net->family == AF_UNIX) { ++ if ((aad(sa)->request & ~NET_PEER_MASK) && aad(sa)->net.addr) ++ audit_unix_addr(ab, "addr", ++ unix_addr(aad(sa)->net.addr), ++ aad(sa)->net.addrlen); ++ else ++ audit_unix_sk_addr(ab, "addr", sa->u.net->sk); ++ if (aad(sa)->request & NET_PEER_MASK) { ++ if (aad(sa)->net.addr) ++ audit_unix_addr(ab, "peer_addr", ++ unix_addr(aad(sa)->net.addr), ++ aad(sa)->net.addrlen); ++ else ++ audit_unix_sk_addr(ab, "peer_addr", ++ aad(sa)->net.peer_sk); ++ } ++ } + if (aad(sa)->peer) { + audit_log_format(ab, " peer="); + aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, +@@ -172,6 +221,127 @@ int aa_sk_perm(const char *op, u32 request, struct sock *sk) + return error; + } + ++#define af_select(FAMILY, FN, DEF_FN) \ ++({ \ ++ int __e; \ ++ switch ((FAMILY)) { \ ++ case AF_UNIX: \ ++ __e = aa_unix_ ## FN; \ ++ break; \ ++ default: \ ++ __e = DEF_FN; \ ++ } \ ++ __e; \ ++}) ++ ++/* TODO: push into lsm.c ???? */ ++ ++/* revaliation, get/set attr, shutdown */ ++int aa_sock_perm(const char *op, u32 request, struct socket *sock) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(in_interrupt()); ++ ++ return af_select(sock->sk->sk_family, ++ sock_perm(op, request, sock), ++ aa_sk_perm(op, request, sock->sk)); ++} ++ ++int aa_sock_create_perm(struct aa_label *label, int family, int type, ++ int protocol) ++{ ++ AA_BUG(!label); ++ /* TODO: .... */ ++ AA_BUG(in_interrupt()); ++ ++ return af_select(family, ++ create_perm(label, family, type, protocol), ++ aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, ++ type, protocol)); ++} ++ ++int aa_sock_bind_perm(struct socket *sock, struct sockaddr *address, ++ int addrlen) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(!address); ++ /* TODO: .... */ ++ AA_BUG(in_interrupt()); ++ ++ return af_select(sock->sk->sk_family, ++ bind_perm(sock, address, addrlen), ++ aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk)); ++} ++ ++int aa_sock_connect_perm(struct socket *sock, struct sockaddr *address, ++ int addrlen) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(!address); ++ /* TODO: .... */ ++ AA_BUG(in_interrupt()); ++ ++ return af_select(sock->sk->sk_family, ++ connect_perm(sock, address, addrlen), ++ aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk)); ++} ++ ++int aa_sock_listen_perm(struct socket *sock, int backlog) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ /* TODO: .... */ ++ AA_BUG(in_interrupt()); ++ ++ return af_select(sock->sk->sk_family, ++ listen_perm(sock, backlog), ++ aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk)); ++} ++ ++/* ability of sock to connect, not peer address binding */ ++int aa_sock_accept_perm(struct socket *sock, struct socket *newsock) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(!newsock); ++ /* TODO: .... */ ++ AA_BUG(in_interrupt()); ++ ++ return af_select(sock->sk->sk_family, ++ accept_perm(sock, newsock), ++ aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk)); ++} ++ ++/* sendmsg, recvmsg */ ++int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, ++ struct msghdr *msg, int size) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(!msg); ++ /* TODO: .... */ ++ AA_BUG(in_interrupt()); ++ ++ return af_select(sock->sk->sk_family, ++ msg_perm(op, request, sock, msg, size), ++ aa_sk_perm(op, request, sock->sk)); ++} ++ ++/* revaliation, get/set attr, opt */ ++int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, int level, ++ int optname) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(in_interrupt()); ++ ++ return af_select(sock->sk->sk_family, ++ opt_perm(op, request, sock, level, optname), ++ aa_sk_perm(op, request, sock->sk)); ++} + + int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock) +@@ -180,5 +350,7 @@ int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, + AA_BUG(!sock); + AA_BUG(!sock->sk); + +- return aa_label_sk_perm(label, op, request, sock->sk); ++ return af_select(sock->sk->sk_family, ++ file_perm(label, op, request, sock), ++ aa_label_sk_perm(label, op, request, sock->sk)); + } +-- +2.11.0 + diff --git a/kernel-patches/v4.13/README b/kernel-patches/v4.13/README new file mode 100644 index 000000000..20cfbe7b5 --- /dev/null +++ b/kernel-patches/v4.13/README @@ -0,0 +1,5 @@ +The old out of tree patches have been dropped. + +This series is a backport of the patches currently in security-next +scheduled for 4.14, with the exception of the last patch for af_unix +mediation. diff --git a/kernel-patches/v4.14/0001-UBUNTU-SAUCE-apparmor-af_unix-mediation.patch b/kernel-patches/v4.14/0001-UBUNTU-SAUCE-apparmor-af_unix-mediation.patch new file mode 100644 index 000000000..5a1b22082 --- /dev/null +++ b/kernel-patches/v4.14/0001-UBUNTU-SAUCE-apparmor-af_unix-mediation.patch @@ -0,0 +1,1394 @@ +From a3b0cb6676a04cdad5cc357bc422d0398083b435 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Tue, 18 Jul 2017 23:27:23 -0700 +Subject: [PATCH 17/17] UBUNTU: SAUCE: apparmor: af_unix mediation + +af_socket mediation did not make it into 4.14 so add remaining out +of tree patch + +Signed-off-by: John Johansen +--- + security/apparmor/Makefile | 3 +- + security/apparmor/af_unix.c | 651 ++++++++++++++++++++++++++++++++++++ + security/apparmor/apparmorfs.c | 6 + + security/apparmor/file.c | 4 +- + security/apparmor/include/af_unix.h | 114 +++++++ + security/apparmor/include/net.h | 16 +- + security/apparmor/include/path.h | 1 + + security/apparmor/include/policy.h | 2 +- + security/apparmor/lsm.c | 169 ++++++---- + security/apparmor/net.c | 174 +++++++++- + 10 files changed, 1072 insertions(+), 68 deletions(-) + create mode 100644 security/apparmor/af_unix.c + create mode 100644 security/apparmor/include/af_unix.h + +diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile +index dafdd387d42b..ef39226ff4aa 100644 +--- a/security/apparmor/Makefile ++++ b/security/apparmor/Makefile +@@ -4,7 +4,8 @@ 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 secid.o file.o policy_ns.o label.o mount.o net.o ++ resource.o secid.o file.o policy_ns.o label.o mount.o net.o \ ++ af_unix.o + apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o + + clean-files := capability_names.h rlim_names.h net_names.h +diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c +new file mode 100644 +index 000000000000..c6876db2dbde +--- /dev/null ++++ b/security/apparmor/af_unix.c +@@ -0,0 +1,651 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor af_unix fine grained mediation ++ * ++ * Copyright 2014 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/af_unix.h" ++#include "include/apparmor.h" ++#include "include/context.h" ++#include "include/file.h" ++#include "include/label.h" ++#include "include/path.h" ++#include "include/policy.h" ++ ++static inline struct sock *aa_sock(struct unix_sock *u) ++{ ++ return &u->sk; ++} ++ ++static inline int unix_fs_perm(const char *op, u32 mask, struct aa_label *label, ++ struct unix_sock *u, int flags) ++{ ++ AA_BUG(!label); ++ AA_BUG(!u); ++ AA_BUG(!UNIX_FS(aa_sock(u))); ++ ++ if (unconfined(label) || !LABEL_MEDIATES(label, AA_CLASS_FILE)) ++ return 0; ++ ++ mask &= NET_FS_PERMS; ++ if (!u->path.dentry) { ++ struct path_cond cond = { }; ++ struct aa_perms perms = { }; ++ struct aa_profile *profile; ++ ++ /* socket path has been cleared because it is being shutdown ++ * can only fall back to original sun_path request ++ */ ++ struct aa_sk_ctx *ctx = SK_CTX(&u->sk); ++ if (ctx->path.dentry) ++ return aa_path_perm(op, label, &ctx->path, flags, mask, ++ &cond); ++ return fn_for_each_confined(label, profile, ++ ((flags | profile->path_flags) & PATH_MEDIATE_DELETED) ? ++ __aa_path_perm(op, profile, ++ u->addr->name->sun_path, mask, ++ &cond, flags, &perms) : ++ aa_audit_file(profile, &nullperms, op, mask, ++ u->addr->name->sun_path, NULL, ++ NULL, cond.uid, ++ "Failed name lookup - " ++ "deleted entry", -EACCES)); ++ } else { ++ /* the sunpath may not be valid for this ns so use the path */ ++ struct path_cond cond = { u->path.dentry->d_inode->i_uid, ++ u->path.dentry->d_inode->i_mode ++ }; ++ ++ return aa_path_perm(op, label, &u->path, flags, mask, &cond); ++ } ++ ++ return 0; ++} ++ ++/* passing in state returned by PROFILE_MEDIATES_AF */ ++static unsigned int match_to_prot(struct aa_profile *profile, ++ unsigned int state, int type, int protocol, ++ const char **info) ++{ ++ __be16 buffer[2]; ++ buffer[0] = cpu_to_be16(type); ++ buffer[1] = cpu_to_be16(protocol); ++ state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &buffer, ++ 4); ++ if (!state) ++ *info = "failed type and protocol match"; ++ return state; ++} ++ ++static unsigned int match_addr(struct aa_profile *profile, unsigned int state, ++ struct sockaddr_un *addr, int addrlen) ++{ ++ if (addr) ++ /* include leading \0 */ ++ state = aa_dfa_match_len(profile->policy.dfa, state, ++ addr->sun_path, ++ unix_addr_len(addrlen)); ++ else ++ /* anonymous end point */ ++ state = aa_dfa_match_len(profile->policy.dfa, state, "\x01", ++ 1); ++ /* todo change to out of band */ ++ state = aa_dfa_null_transition(profile->policy.dfa, state); ++ return state; ++} ++ ++static unsigned int match_to_local(struct aa_profile *profile, ++ unsigned int state, int type, int protocol, ++ struct sockaddr_un *addr, int addrlen, ++ const char **info) ++{ ++ state = match_to_prot(profile, state, type, protocol, info); ++ if (state) { ++ state = match_addr(profile, state, addr, addrlen); ++ if (state) { ++ /* todo: local label matching */ ++ state = aa_dfa_null_transition(profile->policy.dfa, ++ state); ++ if (!state) ++ *info = "failed local label match"; ++ } else ++ *info = "failed local address match"; ++ } ++ ++ return state; ++} ++ ++static unsigned int match_to_sk(struct aa_profile *profile, ++ unsigned int state, struct unix_sock *u, ++ const char **info) ++{ ++ struct sockaddr_un *addr = NULL; ++ int addrlen = 0; ++ ++ if (u->addr) { ++ addr = u->addr->name; ++ addrlen = u->addr->len; ++ } ++ ++ return match_to_local(profile, state, u->sk.sk_type, u->sk.sk_protocol, ++ addr, addrlen, info); ++} ++ ++#define CMD_ADDR 1 ++#define CMD_LISTEN 2 ++#define CMD_OPT 4 ++ ++static inline unsigned int match_to_cmd(struct aa_profile *profile, ++ unsigned int state, struct unix_sock *u, ++ char cmd, const char **info) ++{ ++ state = match_to_sk(profile, state, u, info); ++ if (state) { ++ state = aa_dfa_match_len(profile->policy.dfa, state, &cmd, 1); ++ if (!state) ++ *info = "failed cmd selection match"; ++ } ++ ++ return state; ++} ++ ++static inline unsigned int match_to_peer(struct aa_profile *profile, ++ unsigned int state, ++ struct unix_sock *u, ++ struct sockaddr_un *peer_addr, ++ int peer_addrlen, ++ const char **info) ++{ ++ state = match_to_cmd(profile, state, u, CMD_ADDR, info); ++ if (state) { ++ state = match_addr(profile, state, peer_addr, peer_addrlen); ++ if (!state) ++ *info = "failed peer address match"; ++ } ++ return state; ++} ++ ++static int do_perms(struct aa_profile *profile, unsigned int state, u32 request, ++ struct common_audit_data *sa) ++{ ++ struct aa_perms perms; ++ ++ AA_BUG(!profile); ++ ++ aa_compute_perms(profile->policy.dfa, state, &perms); ++ aa_apply_modes_to_perms(profile, &perms); ++ return aa_check_perms(profile, &perms, request, sa, ++ audit_net_cb); ++} ++ ++static int match_label(struct aa_profile *profile, struct aa_profile *peer, ++ unsigned int state, u32 request, ++ struct common_audit_data *sa) ++{ ++ AA_BUG(!profile); ++ AA_BUG(!peer); ++ ++ aad(sa)->peer = &peer->label; ++ ++ if (state) { ++ state = aa_dfa_match(profile->policy.dfa, state, ++ peer->base.hname); ++ if (!state) ++ aad(sa)->info = "failed peer label match"; ++ } ++ return do_perms(profile, state, request, sa); ++} ++ ++ ++/* unix sock creation comes before we know if the socket will be an fs ++ * socket ++ * v6 - semantics are handled by mapping in profile load ++ * v7 - semantics require sock create for tasks creating an fs socket. ++ */ ++static int profile_create_perm(struct aa_profile *profile, int family, ++ int type, int protocol) ++{ ++ unsigned int state; ++ DEFINE_AUDIT_NET(sa, OP_CREATE, NULL, family, type, protocol); ++ ++ AA_BUG(!profile); ++ AA_BUG(profile_unconfined(profile)); ++ ++ if ((state = PROFILE_MEDIATES_AF(profile, AF_UNIX))) { ++ state = match_to_prot(profile, state, type, protocol, ++ &aad(&sa)->info); ++ return do_perms(profile, state, AA_MAY_CREATE, &sa); ++ } ++ ++ return aa_profile_af_perm(profile, &sa, AA_MAY_CREATE, family, type); ++} ++ ++int aa_unix_create_perm(struct aa_label *label, int family, int type, ++ int protocol) ++{ ++ struct aa_profile *profile; ++ ++ if (unconfined(label)) ++ return 0; ++ ++ return fn_for_each_confined(label, profile, ++ profile_create_perm(profile, family, type, protocol)); ++} ++ ++ ++static inline int profile_sk_perm(struct aa_profile *profile, const char *op, ++ u32 request, struct sock *sk) ++{ ++ unsigned int state; ++ DEFINE_AUDIT_SK(sa, op, sk); ++ ++ AA_BUG(!profile); ++ AA_BUG(!sk); ++ AA_BUG(UNIX_FS(sk)); ++ AA_BUG(profile_unconfined(profile)); ++ ++ state = PROFILE_MEDIATES_AF(profile, AF_UNIX); ++ if (state) { ++ state = match_to_sk(profile, state, unix_sk(sk), ++ &aad(&sa)->info); ++ return do_perms(profile, state, request, &sa); ++ } ++ ++ return aa_profile_af_sk_perm(profile, &sa, request, sk); ++} ++ ++int aa_unix_label_sk_perm(struct aa_label *label, const char *op, u32 request, ++ struct sock *sk) ++{ ++ struct aa_profile *profile; ++ ++ return fn_for_each_confined(label, profile, ++ profile_sk_perm(profile, op, request, sk)); ++} ++ ++static int unix_label_sock_perm(struct aa_label *label, const char *op, u32 request, ++ struct socket *sock) ++{ ++ if (unconfined(label)) ++ return 0; ++ if (UNIX_FS(sock->sk)) ++ return unix_fs_perm(op, request, label, unix_sk(sock->sk), 0); ++ ++ return aa_unix_label_sk_perm(label, op, request, sock->sk); ++} ++ ++/* revaliation, get/set attr */ ++int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock) ++{ ++ struct aa_label *label; ++ int error; ++ ++ label = begin_current_label_crit_section(); ++ error = unix_label_sock_perm(label, op, request, sock); ++ end_current_label_crit_section(label); ++ ++ return error; ++} ++ ++static int profile_bind_perm(struct aa_profile *profile, struct sock *sk, ++ struct sockaddr *addr, int addrlen) ++{ ++ unsigned int state; ++ DEFINE_AUDIT_SK(sa, OP_BIND, sk); ++ ++ AA_BUG(!profile); ++ AA_BUG(!sk); ++ AA_BUG(addr->sa_family != AF_UNIX); ++ AA_BUG(profile_unconfined(profile)); ++ AA_BUG(unix_addr_fs(addr, addrlen)); ++ ++ state = PROFILE_MEDIATES_AF(profile, AF_UNIX); ++ if (state) { ++ /* bind for abstract socket */ ++ aad(&sa)->net.addr = unix_addr(addr); ++ aad(&sa)->net.addrlen = addrlen; ++ ++ state = match_to_local(profile, state, ++ sk->sk_type, sk->sk_protocol, ++ unix_addr(addr), addrlen, ++ &aad(&sa)->info); ++ return do_perms(profile, state, AA_MAY_BIND, &sa); ++ } ++ ++ return aa_profile_af_sk_perm(profile, &sa, AA_MAY_BIND, sk); ++} ++ ++int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address, ++ int addrlen) ++{ ++ struct aa_profile *profile; ++ struct aa_label *label; ++ int error = 0; ++ ++ label = begin_current_label_crit_section(); ++ /* fs bind is handled by mknod */ ++ if (!(unconfined(label) || unix_addr_fs(address, addrlen))) ++ error = fn_for_each_confined(label, profile, ++ profile_bind_perm(profile, sock->sk, address, ++ addrlen)); ++ end_current_label_crit_section(label); ++ ++ return error; ++} ++ ++int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address, ++ int addrlen) ++{ ++ /* unix connections are covered by the ++ * - unix_stream_connect (stream) and unix_may_send hooks (dgram) ++ * - fs connect is handled by open ++ */ ++ return 0; ++} ++ ++static int profile_listen_perm(struct aa_profile *profile, struct sock *sk, ++ int backlog) ++{ ++ unsigned int state; ++ DEFINE_AUDIT_SK(sa, OP_LISTEN, sk); ++ ++ AA_BUG(!profile); ++ AA_BUG(!sk); ++ AA_BUG(UNIX_FS(sk)); ++ AA_BUG(profile_unconfined(profile)); ++ ++ state = PROFILE_MEDIATES_AF(profile, AF_UNIX); ++ if (state) { ++ __be16 b = cpu_to_be16(backlog); ++ ++ state = match_to_cmd(profile, state, unix_sk(sk), CMD_LISTEN, ++ &aad(&sa)->info); ++ if (state) { ++ state = aa_dfa_match_len(profile->policy.dfa, state, ++ (char *) &b, 2); ++ if (!state) ++ aad(&sa)->info = "failed listen backlog match"; ++ } ++ return do_perms(profile, state, AA_MAY_LISTEN, &sa); ++ } ++ ++ return aa_profile_af_sk_perm(profile, &sa, AA_MAY_LISTEN, sk); ++} ++ ++int aa_unix_listen_perm(struct socket *sock, int backlog) ++{ ++ struct aa_profile *profile; ++ struct aa_label *label; ++ int error = 0; ++ ++ label = begin_current_label_crit_section(); ++ if (!(unconfined(label) || UNIX_FS(sock->sk))) ++ error = fn_for_each_confined(label, profile, ++ profile_listen_perm(profile, sock->sk, ++ backlog)); ++ end_current_label_crit_section(label); ++ ++ return error; ++} ++ ++ ++static inline int profile_accept_perm(struct aa_profile *profile, ++ struct sock *sk, ++ struct sock *newsk) ++{ ++ unsigned int state; ++ DEFINE_AUDIT_SK(sa, OP_ACCEPT, sk); ++ ++ AA_BUG(!profile); ++ AA_BUG(!sk); ++ AA_BUG(UNIX_FS(sk)); ++ AA_BUG(profile_unconfined(profile)); ++ ++ state = PROFILE_MEDIATES_AF(profile, AF_UNIX); ++ if (state) { ++ state = match_to_sk(profile, state, unix_sk(sk), ++ &aad(&sa)->info); ++ return do_perms(profile, state, AA_MAY_ACCEPT, &sa); ++ } ++ ++ return aa_profile_af_sk_perm(profile, &sa, AA_MAY_ACCEPT, sk); ++} ++ ++/* ability of sock to connect, not peer address binding */ ++int aa_unix_accept_perm(struct socket *sock, struct socket *newsock) ++{ ++ struct aa_profile *profile; ++ struct aa_label *label; ++ int error = 0; ++ ++ label = begin_current_label_crit_section(); ++ if (!(unconfined(label) || UNIX_FS(sock->sk))) ++ error = fn_for_each_confined(label, profile, ++ profile_accept_perm(profile, sock->sk, ++ newsock->sk)); ++ end_current_label_crit_section(label); ++ ++ return error; ++} ++ ++ ++/* dgram handled by unix_may_sendmsg, right to send on stream done at connect ++ * could do per msg unix_stream here ++ */ ++/* sendmsg, recvmsg */ ++int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock, ++ struct msghdr *msg, int size) ++{ ++ return 0; ++} ++ ++ ++static int profile_opt_perm(struct aa_profile *profile, const char *op, u32 request, ++ struct sock *sk, int level, int optname) ++{ ++ unsigned int state; ++ DEFINE_AUDIT_SK(sa, op, sk); ++ ++ AA_BUG(!profile); ++ AA_BUG(!sk); ++ AA_BUG(UNIX_FS(sk)); ++ AA_BUG(profile_unconfined(profile)); ++ ++ state = PROFILE_MEDIATES_AF(profile, AF_UNIX); ++ if (state) { ++ __be16 b = cpu_to_be16(optname); ++ ++ state = match_to_cmd(profile, state, unix_sk(sk), CMD_OPT, ++ &aad(&sa)->info); ++ if (state) { ++ state = aa_dfa_match_len(profile->policy.dfa, state, ++ (char *) &b, 2); ++ if (!state) ++ aad(&sa)->info = "failed sockopt match"; ++ } ++ return do_perms(profile, state, request, &sa); ++ } ++ ++ return aa_profile_af_sk_perm(profile, &sa, request, sk); ++} ++ ++int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level, ++ int optname) ++{ ++ struct aa_profile *profile; ++ struct aa_label *label; ++ int error = 0; ++ ++ label = begin_current_label_crit_section(); ++ if (!(unconfined(label) || UNIX_FS(sock->sk))) ++ error = fn_for_each_confined(label, profile, ++ profile_opt_perm(profile, op, request, ++ sock->sk, level, optname)); ++ end_current_label_crit_section(label); ++ ++ return error; ++} ++ ++/* null peer_label is allowed, in which case the peer_sk label is used */ ++static int profile_peer_perm(struct aa_profile *profile, const char *op, u32 request, ++ struct sock *sk, struct sock *peer_sk, ++ struct aa_label *peer_label, ++ struct common_audit_data *sa) ++{ ++ unsigned int state; ++ ++ AA_BUG(!profile); ++ AA_BUG(profile_unconfined(profile)); ++ AA_BUG(!sk); ++ AA_BUG(!peer_sk); ++ AA_BUG(UNIX_FS(peer_sk)); ++ ++ state = PROFILE_MEDIATES_AF(profile, AF_UNIX); ++ if (state) { ++ struct aa_sk_ctx *peer_ctx = SK_CTX(peer_sk); ++ struct aa_profile *peerp; ++ struct sockaddr_un *addr = NULL; ++ int len = 0; ++ if (unix_sk(peer_sk)->addr) { ++ addr = unix_sk(peer_sk)->addr->name; ++ len = unix_sk(peer_sk)->addr->len; ++ } ++ state = match_to_peer(profile, state, unix_sk(sk), ++ addr, len, &aad(sa)->info); ++ if (!peer_label) ++ peer_label = peer_ctx->label; ++ return fn_for_each_in_ns(peer_label, peerp, ++ match_label(profile, peerp, state, request, ++ sa)); ++ } ++ ++ return aa_profile_af_sk_perm(profile, sa, request, sk); ++} ++ ++/** ++ * ++ * Requires: lock held on both @sk and @peer_sk ++ */ ++int aa_unix_peer_perm(struct aa_label *label, const char *op, u32 request, ++ struct sock *sk, struct sock *peer_sk, ++ struct aa_label *peer_label) ++{ ++ struct unix_sock *peeru = unix_sk(peer_sk); ++ struct unix_sock *u = unix_sk(sk); ++ ++ AA_BUG(!label); ++ AA_BUG(!sk); ++ AA_BUG(!peer_sk); ++ ++ if (UNIX_FS(aa_sock(peeru))) ++ return unix_fs_perm(op, request, label, peeru, 0); ++ else if (UNIX_FS(aa_sock(u))) ++ return unix_fs_perm(op, request, label, u, 0); ++ else { ++ struct aa_profile *profile; ++ DEFINE_AUDIT_SK(sa, op, sk); ++ aad(&sa)->net.peer_sk = peer_sk; ++ ++ /* TODO: ns!!! */ ++ if (!net_eq(sock_net(sk), sock_net(peer_sk))) { ++ ; ++ } ++ ++ if (unconfined(label)) ++ return 0; ++ ++ return fn_for_each_confined(label, profile, ++ profile_peer_perm(profile, op, request, sk, ++ peer_sk, peer_label, &sa)); ++ } ++} ++ ++ ++/* from net/unix/af_unix.c */ ++static void unix_state_double_lock(struct sock *sk1, struct sock *sk2) ++{ ++ if (unlikely(sk1 == sk2) || !sk2) { ++ unix_state_lock(sk1); ++ return; ++ } ++ if (sk1 < sk2) { ++ unix_state_lock(sk1); ++ unix_state_lock_nested(sk2); ++ } else { ++ unix_state_lock(sk2); ++ unix_state_lock_nested(sk1); ++ } ++} ++ ++static void unix_state_double_unlock(struct sock *sk1, struct sock *sk2) ++{ ++ if (unlikely(sk1 == sk2) || !sk2) { ++ unix_state_unlock(sk1); ++ return; ++ } ++ unix_state_unlock(sk1); ++ unix_state_unlock(sk2); ++} ++ ++int aa_unix_file_perm(struct aa_label *label, const char *op, u32 request, ++ struct socket *sock) ++{ ++ struct sock *peer_sk = NULL; ++ u32 sk_req = request & ~NET_PEER_MASK; ++ int error = 0; ++ ++ AA_BUG(!label); ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(sock->sk->sk_family != AF_UNIX); ++ ++ /* TODO: update sock label with new task label */ ++ unix_state_lock(sock->sk); ++ peer_sk = unix_peer(sock->sk); ++ if (peer_sk) ++ sock_hold(peer_sk); ++ if (!unix_connected(sock) && sk_req) { ++ error = unix_label_sock_perm(label, op, sk_req, sock); ++ if (!error) { ++ // update label ++ } ++ } ++ unix_state_unlock(sock->sk); ++ if (!peer_sk) ++ return error; ++ ++ unix_state_double_lock(sock->sk, peer_sk); ++ if (UNIX_FS(sock->sk)) { ++ error = unix_fs_perm(op, request, label, unix_sk(sock->sk), ++ PATH_SOCK_COND); ++ } else if (UNIX_FS(peer_sk)) { ++ error = unix_fs_perm(op, request, label, unix_sk(peer_sk), ++ PATH_SOCK_COND); ++ } else { ++ struct aa_sk_ctx *pctx = SK_CTX(peer_sk); ++ if (sk_req) ++ error = aa_unix_label_sk_perm(label, op, sk_req, ++ sock->sk); ++ last_error(error, ++ xcheck(aa_unix_peer_perm(label, op, ++ MAY_READ | MAY_WRITE, ++ sock->sk, peer_sk, NULL), ++ aa_unix_peer_perm(pctx->label, op, ++ MAY_READ | MAY_WRITE, ++ peer_sk, sock->sk, label))); ++ } ++ ++ unix_state_double_unlock(sock->sk, peer_sk); ++ sock_put(peer_sk); ++ ++ return error; ++} +diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c +index 125dad5c3fde..20cdb1c4b266 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -2187,6 +2187,11 @@ static struct aa_sfs_entry aa_sfs_entry_ns[] = { + { } + }; + ++static struct aa_sfs_entry aa_sfs_entry_dbus[] = { ++ AA_SFS_FILE_STRING("mask", "acquire send receive"), ++ { } ++}; ++ + static struct aa_sfs_entry aa_sfs_entry_query_label[] = { + AA_SFS_FILE_STRING("perms", "allow deny audit quiet"), + AA_SFS_FILE_BOOLEAN("data", 1), +@@ -2210,6 +2215,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { + AA_SFS_DIR("caps", aa_sfs_entry_caps), + AA_SFS_DIR("ptrace", aa_sfs_entry_ptrace), + AA_SFS_DIR("signal", aa_sfs_entry_signal), ++ AA_SFS_DIR("dbus", aa_sfs_entry_dbus), + AA_SFS_DIR("query", aa_sfs_entry_query), + { } + }; +diff --git a/security/apparmor/file.c b/security/apparmor/file.c +index db80221891c6..e62791106900 100644 +--- a/security/apparmor/file.c ++++ b/security/apparmor/file.c +@@ -16,6 +16,7 @@ + #include + #include + ++#include "include/af_unix.h" + #include "include/apparmor.h" + #include "include/audit.h" + #include "include/context.h" +@@ -289,7 +290,8 @@ int __aa_path_perm(const char *op, struct aa_profile *profile, const char *name, + { + int e = 0; + +- if (profile_unconfined(profile)) ++ if (profile_unconfined(profile) || ++ ((flags & PATH_SOCK_COND) && !PROFILE_MEDIATES_AF(profile, AF_UNIX))) + return 0; + aa_str_perms(profile->file.dfa, profile->file.start, name, cond, perms); + if (request & ~perms->allow) +diff --git a/security/apparmor/include/af_unix.h b/security/apparmor/include/af_unix.h +new file mode 100644 +index 000000000000..d1b7f2316be4 +--- /dev/null ++++ b/security/apparmor/include/af_unix.h +@@ -0,0 +1,114 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor af_unix fine grained mediation ++ * ++ * Copyright 2014 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_AF_UNIX_H ++ ++#include ++ ++#include "label.h" ++//#include "include/net.h" ++ ++#define unix_addr_len(L) ((L) - sizeof(sa_family_t)) ++#define unix_abstract_name_len(L) (unix_addr_len(L) - 1) ++#define unix_abstract_len(U) (unix_abstract_name_len((U)->addr->len)) ++#define addr_unix_abstract_name(B) ((B)[0] == 0) ++#define addr_unix_anonymous(U) (addr_unix_len(U) <= 0) ++#define addr_unix_abstract(U) (!addr_unix_anonymous(U) && addr_unix_abstract_name((U)->addr)) ++//#define unix_addr_fs(U) (!unix_addr_anonymous(U) && !unix_addr_abstract_name((U)->addr)) ++ ++#define unix_addr(A) ((struct sockaddr_un *)(A)) ++#define unix_addr_anon(A, L) ((A) && unix_addr_len(L) <= 0) ++#define unix_addr_fs(A, L) (!unix_addr_anon(A, L) && !addr_unix_abstract_name(unix_addr(A)->sun_path)) ++ ++#define UNIX_ANONYMOUS(U) (!unix_sk(U)->addr) ++/* from net/unix/af_unix.c */ ++#define UNIX_ABSTRACT(U) (!UNIX_ANONYMOUS(U) && \ ++ unix_sk(U)->addr->hash < UNIX_HASH_SIZE) ++#define UNIX_FS(U) (!UNIX_ANONYMOUS(U) && unix_sk(U)->addr->name->sun_path[0]) ++#define unix_peer(sk) (unix_sk(sk)->peer) ++#define unix_connected(S) ((S)->state == SS_CONNECTED) ++ ++static inline void print_unix_addr(struct sockaddr_un *A, int L) ++{ ++ char *buf = (A) ? (char *) &(A)->sun_path : NULL; ++ int len = unix_addr_len(L); ++ if (!buf || len <= 0) ++ printk(" "); ++ else if (buf[0]) ++ printk(" %s", buf); ++ else ++ /* abstract name len includes leading \0 */ ++ printk(" %d @%.*s", len - 1, len - 1, buf+1); ++}; ++ ++/* ++ printk("%s: %s: f %d, t %d, p %d", __FUNCTION__, \ ++ #SK , \ ++*/ ++#define print_unix_sk(SK) \ ++do { \ ++ struct unix_sock *u = unix_sk(SK); \ ++ printk("%s: f %d, t %d, p %d", #SK , \ ++ (SK)->sk_family, (SK)->sk_type, (SK)->sk_protocol); \ ++ if (u->addr) \ ++ print_unix_addr(u->addr->name, u->addr->len); \ ++ else \ ++ print_unix_addr(NULL, sizeof(sa_family_t)); \ ++ /* printk("\n");*/ \ ++} while (0) ++ ++#define print_sk(SK) \ ++do { \ ++ if (!(SK)) { \ ++ printk("%s: %s is null\n", __FUNCTION__, #SK); \ ++ } else if ((SK)->sk_family == PF_UNIX) { \ ++ print_unix_sk(SK); \ ++ printk("\n"); \ ++ } else { \ ++ printk("%s: %s: family %d\n", __FUNCTION__, #SK , \ ++ (SK)->sk_family); \ ++ } \ ++} while (0) ++ ++#define print_sock_addr(U) \ ++do { \ ++ printk("%s:\n", __FUNCTION__); \ ++ printk(" sock %s:", sock_ctx && sock_ctx->label ? aa_label_printk(sock_ctx->label, GFP_ATOMIC); : ""); print_sk(sock); \ ++ printk(" other %s:", other_ctx && other_ctx->label ? aa_label_printk(other_ctx->label, GFP_ATOMIC); : ""); print_sk(other); \ ++ printk(" new %s", new_ctx && new_ctx->label ? aa_label_printk(new_ctx->label, GFP_ATOMIC); : ""); print_sk(newsk); \ ++} while (0) ++ ++ ++ ++ ++int aa_unix_peer_perm(struct aa_label *label, const char *op, u32 request, ++ struct sock *sk, struct sock *peer_sk, ++ struct aa_label *peer_label); ++int aa_unix_label_sk_perm(struct aa_label *label, const char *op, u32 request, ++ struct sock *sk); ++int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock); ++int aa_unix_create_perm(struct aa_label *label, int family, int type, ++ int protocol); ++int aa_unix_bind_perm(struct socket *sock, struct sockaddr *address, ++ int addrlen); ++int aa_unix_connect_perm(struct socket *sock, struct sockaddr *address, ++ int addrlen); ++int aa_unix_listen_perm(struct socket *sock, int backlog); ++int aa_unix_accept_perm(struct socket *sock, struct socket *newsock); ++int aa_unix_msg_perm(const char *op, u32 request, struct socket *sock, ++ struct msghdr *msg, int size); ++int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock, int level, ++ int optname); ++int aa_unix_file_perm(struct aa_label *label, const char *op, u32 request, ++ struct socket *sock); ++ ++#endif /* __AA_AF_UNIX_H */ +diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h +index 140c8efcf364..0ae45240c352 100644 +--- a/security/apparmor/include/net.h ++++ b/security/apparmor/include/net.h +@@ -90,8 +90,6 @@ extern struct aa_sfs_entry aa_sfs_entry_network[]; + void audit_net_cb(struct audit_buffer *ab, void *va); + int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, + u32 request, u16 family, int type); +-int aa_af_perm(struct aa_label *label, const char *op, u32 request, u16 family, +- int type, int protocol); + static inline int aa_profile_af_sk_perm(struct aa_profile *profile, + struct common_audit_data *sa, + u32 request, +@@ -100,8 +98,20 @@ static inline int aa_profile_af_sk_perm(struct aa_profile *profile, + return aa_profile_af_perm(profile, sa, request, sk->sk_family, + sk->sk_type); + } +-int aa_sk_perm(const char *op, u32 request, struct sock *sk); + ++int aa_sock_perm(const char *op, u32 request, struct socket *sock); ++int aa_sock_create_perm(struct aa_label *label, int family, int type, ++ int protocol); ++int aa_sock_bind_perm(struct socket *sock, struct sockaddr *address, ++ int addrlen); ++int aa_sock_connect_perm(struct socket *sock, struct sockaddr *address, ++ int addrlen); ++int aa_sock_listen_perm(struct socket *sock, int backlog); ++int aa_sock_accept_perm(struct socket *sock, struct socket *newsock); ++int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, ++ struct msghdr *msg, int size); ++int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, int level, ++ int optname); + int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock); + +diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h +index 05fb3305671e..26762db2207d 100644 +--- a/security/apparmor/include/path.h ++++ b/security/apparmor/include/path.h +@@ -18,6 +18,7 @@ + + enum path_flags { + PATH_IS_DIR = 0x1, /* path is a directory */ ++ PATH_SOCK_COND = 0x2, + PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */ + PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */ + PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ +diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h +index 4364088a0b9e..26660a1a50b0 100644 +--- a/security/apparmor/include/policy.h ++++ b/security/apparmor/include/policy.h +@@ -226,7 +226,7 @@ static inline unsigned int PROFILE_MEDIATES_SAFE(struct aa_profile *profile, + static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile, + u16 AF) { + unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET); +- u16 be_af = cpu_to_be16(AF); ++ __be16 be_af = cpu_to_be16(AF); + + if (!state) + return 0; +diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c +index cc5ab23a2d84..0ede66d80a53 100644 +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -26,6 +26,7 @@ + #include + #include + ++#include "include/af_unix.h" + #include "include/apparmor.h" + #include "include/apparmorfs.h" + #include "include/audit.h" +@@ -782,16 +783,96 @@ static void apparmor_sk_clone_security(const struct sock *sk, + path_get(&new->path); + } + +-static int aa_sock_create_perm(struct aa_label *label, int family, int type, +- int protocol) ++static struct path *UNIX_FS_CONN_PATH(struct sock *sk, struct sock *newsk) + { +- AA_BUG(!label); +- AA_BUG(in_interrupt()); ++ if (sk->sk_family == PF_UNIX && UNIX_FS(sk)) ++ return &unix_sk(sk)->path; ++ else if (newsk->sk_family == PF_UNIX && UNIX_FS(newsk)) ++ return &unix_sk(newsk)->path; ++ return NULL; ++} ++ ++/** ++ * apparmor_unix_stream_connect - check perms before making unix domain conn ++ * ++ * peer is locked when this hook is called ++ */ ++static int apparmor_unix_stream_connect(struct sock *sk, struct sock *peer_sk, ++ struct sock *newsk) ++{ ++ struct aa_sk_ctx *sk_ctx = SK_CTX(sk); ++ struct aa_sk_ctx *peer_ctx = SK_CTX(peer_sk); ++ struct aa_sk_ctx *new_ctx = SK_CTX(newsk); ++ struct aa_label *label; ++ struct path *path; ++ int error; + +- return aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, type, +- protocol); ++ label = __begin_current_label_crit_section(); ++ error = aa_unix_peer_perm(label, OP_CONNECT, ++ (AA_MAY_CONNECT | AA_MAY_SEND | AA_MAY_RECEIVE), ++ sk, peer_sk, NULL); ++ if (!UNIX_FS(peer_sk)) { ++ last_error(error, ++ aa_unix_peer_perm(peer_ctx->label, OP_CONNECT, ++ (AA_MAY_ACCEPT | AA_MAY_SEND | AA_MAY_RECEIVE), ++ peer_sk, sk, label)); ++ } ++ __end_current_label_crit_section(label); ++ ++ if (error) ++ return error; ++ ++ /* label newsk if it wasn't labeled in post_create. Normally this ++ * would be done in sock_graft, but because we are directly looking ++ * at the peer_sk to obtain peer_labeling for unix socks this ++ * does not work ++ */ ++ if (!new_ctx->label) ++ new_ctx->label = aa_get_label(peer_ctx->label); ++ ++ /* Cross reference the peer labels for SO_PEERSEC */ ++ if (new_ctx->peer) ++ aa_put_label(new_ctx->peer); ++ ++ if (sk_ctx->peer) ++ aa_put_label(sk_ctx->peer); ++ ++ new_ctx->peer = aa_get_label(sk_ctx->label); ++ sk_ctx->peer = aa_get_label(peer_ctx->label); ++ ++ path = UNIX_FS_CONN_PATH(sk, peer_sk); ++ if (path) { ++ new_ctx->path = *path; ++ sk_ctx->path = *path; ++ path_get(path); ++ path_get(path); ++ } ++ return 0; + } + ++/** ++ * apparmor_unix_may_send - check perms before conn or sending unix dgrams ++ * ++ * other is locked when this hook is called ++ * ++ * dgram connect calls may_send, peer setup but path not copied????? ++ */ ++static int apparmor_unix_may_send(struct socket *sock, struct socket *peer) ++{ ++ struct aa_sk_ctx *peer_ctx = SK_CTX(peer->sk); ++ struct aa_label *label; ++ int error; ++ ++ label = __begin_current_label_crit_section(); ++ error = xcheck(aa_unix_peer_perm(label, OP_SENDMSG, AA_MAY_SEND, ++ sock->sk, peer->sk, NULL), ++ aa_unix_peer_perm(peer_ctx->label, OP_SENDMSG, ++ AA_MAY_RECEIVE, ++ peer->sk, sock->sk, label)); ++ __end_current_label_crit_section(label); ++ ++ return error; ++} + + /** + * apparmor_socket_create - check perms before creating a new socket +@@ -849,12 +930,7 @@ static int apparmor_socket_post_create(struct socket *sock, int family, + static int apparmor_socket_bind(struct socket *sock, + struct sockaddr *address, int addrlen) + { +- AA_BUG(!sock); +- AA_BUG(!sock->sk); +- AA_BUG(!address); +- AA_BUG(in_interrupt()); +- +- return aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk); ++ return aa_sock_bind_perm(sock, address, addrlen); + } + + /** +@@ -863,12 +939,7 @@ static int apparmor_socket_bind(struct socket *sock, + static int apparmor_socket_connect(struct socket *sock, + struct sockaddr *address, int addrlen) + { +- AA_BUG(!sock); +- AA_BUG(!sock->sk); +- AA_BUG(!address); +- AA_BUG(in_interrupt()); +- +- return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk); ++ return aa_sock_connect_perm(sock, address, addrlen); + } + + /** +@@ -876,11 +947,7 @@ static int apparmor_socket_connect(struct socket *sock, + */ + static int apparmor_socket_listen(struct socket *sock, int backlog) + { +- AA_BUG(!sock); +- AA_BUG(!sock->sk); +- AA_BUG(in_interrupt()); +- +- return aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk); ++ return aa_sock_listen_perm(sock, backlog); + } + + /** +@@ -891,23 +958,7 @@ static int apparmor_socket_listen(struct socket *sock, int backlog) + */ + static int apparmor_socket_accept(struct socket *sock, struct socket *newsock) + { +- AA_BUG(!sock); +- AA_BUG(!sock->sk); +- AA_BUG(!newsock); +- AA_BUG(in_interrupt()); +- +- return aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk); +-} +- +-static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, +- struct msghdr *msg, int size) +-{ +- AA_BUG(!sock); +- AA_BUG(!sock->sk); +- AA_BUG(!msg); +- AA_BUG(in_interrupt()); +- +- return aa_sk_perm(op, request, sock->sk); ++ return aa_sock_accept_perm(sock, newsock); + } + + /** +@@ -928,16 +979,6 @@ static int apparmor_socket_recvmsg(struct socket *sock, + return aa_sock_msg_perm(OP_RECVMSG, AA_MAY_RECEIVE, sock, msg, size); + } + +-/* revaliation, get/set attr, shutdown */ +-static int aa_sock_perm(const char *op, u32 request, struct socket *sock) +-{ +- AA_BUG(!sock); +- AA_BUG(!sock->sk); +- AA_BUG(in_interrupt()); +- +- return aa_sk_perm(op, request, sock->sk); +-} +- + /** + * apparmor_socket_getsockname - check perms before getting the local address + */ +@@ -954,17 +995,6 @@ static int apparmor_socket_getpeername(struct socket *sock) + return aa_sock_perm(OP_GETPEERNAME, AA_MAY_GETATTR, sock); + } + +-/* revaliation, get/set attr, opt */ +-static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, +- int level, int optname) +-{ +- AA_BUG(!sock); +- AA_BUG(!sock->sk); +- AA_BUG(in_interrupt()); +- +- return aa_sk_perm(op, request, sock->sk); +-} +- + /** + * apparmor_getsockopt - check perms before getting socket options + */ +@@ -1009,11 +1039,25 @@ static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) + + static struct aa_label *sk_peer_label(struct sock *sk) + { ++ struct sock *peer_sk; + struct aa_sk_ctx *ctx = SK_CTX(sk); + + if (ctx->peer) + return ctx->peer; + ++ if (sk->sk_family != PF_UNIX) ++ return ERR_PTR(-ENOPROTOOPT); ++ ++ /* check for sockpair peering which does not go through ++ * security_unix_stream_connect ++ */ ++ peer_sk = unix_peer(sk); ++ if (peer_sk) { ++ ctx = SK_CTX(peer_sk); ++ if (ctx->label) ++ return ctx->label; ++ } ++ + return ERR_PTR(-ENOPROTOOPT); + } + +@@ -1137,6 +1181,9 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { + LSM_HOOK_INIT(sk_free_security, apparmor_sk_free_security), + LSM_HOOK_INIT(sk_clone_security, apparmor_sk_clone_security), + ++ LSM_HOOK_INIT(unix_stream_connect, apparmor_unix_stream_connect), ++ LSM_HOOK_INIT(unix_may_send, apparmor_unix_may_send), ++ + LSM_HOOK_INIT(socket_create, apparmor_socket_create), + LSM_HOOK_INIT(socket_post_create, apparmor_socket_post_create), + LSM_HOOK_INIT(socket_bind, apparmor_socket_bind), +diff --git a/security/apparmor/net.c b/security/apparmor/net.c +index 33d54435f8d6..dd1953b08e58 100644 +--- a/security/apparmor/net.c ++++ b/security/apparmor/net.c +@@ -12,6 +12,7 @@ + * License. + */ + ++#include "include/af_unix.h" + #include "include/apparmor.h" + #include "include/audit.h" + #include "include/context.h" +@@ -24,6 +25,7 @@ + + struct aa_sfs_entry aa_sfs_entry_network[] = { + AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), ++ AA_SFS_FILE_BOOLEAN("af_unix", 1), + { } + }; + +@@ -69,6 +71,36 @@ static const char * const net_mask_names[] = { + "unknown", + }; + ++static void audit_unix_addr(struct audit_buffer *ab, const char *str, ++ struct sockaddr_un *addr, int addrlen) ++{ ++ int len = unix_addr_len(addrlen); ++ ++ if (!addr || len <= 0) { ++ audit_log_format(ab, " %s=none", str); ++ } else if (addr->sun_path[0]) { ++ audit_log_format(ab, " %s=", str); ++ audit_log_untrustedstring(ab, addr->sun_path); ++ } else { ++ audit_log_format(ab, " %s=\"@", str); ++ if (audit_string_contains_control(&addr->sun_path[1], len - 1)) ++ audit_log_n_hex(ab, &addr->sun_path[1], len - 1); ++ else ++ audit_log_format(ab, "%.*s", len - 1, ++ &addr->sun_path[1]); ++ audit_log_format(ab, "\""); ++ } ++} ++ ++static void audit_unix_sk_addr(struct audit_buffer *ab, const char *str, ++ struct sock *sk) ++{ ++ struct unix_sock *u = unix_sk(sk); ++ if (u && u->addr) ++ audit_unix_addr(ab, str, u->addr->name, u->addr->len); ++ else ++ audit_unix_addr(ab, str, NULL, 0); ++} + + /* audit callback for net specific fields */ + void audit_net_cb(struct audit_buffer *ab, void *va) +@@ -98,6 +130,23 @@ void audit_net_cb(struct audit_buffer *ab, void *va) + net_mask_names, NET_PERMS_MASK); + } + } ++ if (sa->u.net->family == AF_UNIX) { ++ if ((aad(sa)->request & ~NET_PEER_MASK) && aad(sa)->net.addr) ++ audit_unix_addr(ab, "addr", ++ unix_addr(aad(sa)->net.addr), ++ aad(sa)->net.addrlen); ++ else ++ audit_unix_sk_addr(ab, "addr", sa->u.net->sk); ++ if (aad(sa)->request & NET_PEER_MASK) { ++ if (aad(sa)->net.addr) ++ audit_unix_addr(ab, "peer_addr", ++ unix_addr(aad(sa)->net.addr), ++ aad(sa)->net.addrlen); ++ else ++ audit_unix_sk_addr(ab, "peer_addr", ++ aad(sa)->net.peer_sk); ++ } ++ } + if (aad(sa)->peer) { + audit_log_format(ab, " peer="); + aa_label_xaudit(ab, labels_ns(aad(sa)->label), aad(sa)->peer, +@@ -172,6 +221,127 @@ int aa_sk_perm(const char *op, u32 request, struct sock *sk) + return error; + } + ++#define af_select(FAMILY, FN, DEF_FN) \ ++({ \ ++ int __e; \ ++ switch ((FAMILY)) { \ ++ case AF_UNIX: \ ++ __e = aa_unix_ ## FN; \ ++ break; \ ++ default: \ ++ __e = DEF_FN; \ ++ } \ ++ __e; \ ++}) ++ ++/* TODO: push into lsm.c ???? */ ++ ++/* revaliation, get/set attr, shutdown */ ++int aa_sock_perm(const char *op, u32 request, struct socket *sock) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(in_interrupt()); ++ ++ return af_select(sock->sk->sk_family, ++ sock_perm(op, request, sock), ++ aa_sk_perm(op, request, sock->sk)); ++} ++ ++int aa_sock_create_perm(struct aa_label *label, int family, int type, ++ int protocol) ++{ ++ AA_BUG(!label); ++ /* TODO: .... */ ++ AA_BUG(in_interrupt()); ++ ++ return af_select(family, ++ create_perm(label, family, type, protocol), ++ aa_af_perm(label, OP_CREATE, AA_MAY_CREATE, family, ++ type, protocol)); ++} ++ ++int aa_sock_bind_perm(struct socket *sock, struct sockaddr *address, ++ int addrlen) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(!address); ++ /* TODO: .... */ ++ AA_BUG(in_interrupt()); ++ ++ return af_select(sock->sk->sk_family, ++ bind_perm(sock, address, addrlen), ++ aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk)); ++} ++ ++int aa_sock_connect_perm(struct socket *sock, struct sockaddr *address, ++ int addrlen) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(!address); ++ /* TODO: .... */ ++ AA_BUG(in_interrupt()); ++ ++ return af_select(sock->sk->sk_family, ++ connect_perm(sock, address, addrlen), ++ aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk)); ++} ++ ++int aa_sock_listen_perm(struct socket *sock, int backlog) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ /* TODO: .... */ ++ AA_BUG(in_interrupt()); ++ ++ return af_select(sock->sk->sk_family, ++ listen_perm(sock, backlog), ++ aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk)); ++} ++ ++/* ability of sock to connect, not peer address binding */ ++int aa_sock_accept_perm(struct socket *sock, struct socket *newsock) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(!newsock); ++ /* TODO: .... */ ++ AA_BUG(in_interrupt()); ++ ++ return af_select(sock->sk->sk_family, ++ accept_perm(sock, newsock), ++ aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk)); ++} ++ ++/* sendmsg, recvmsg */ ++int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock, ++ struct msghdr *msg, int size) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(!msg); ++ /* TODO: .... */ ++ AA_BUG(in_interrupt()); ++ ++ return af_select(sock->sk->sk_family, ++ msg_perm(op, request, sock, msg, size), ++ aa_sk_perm(op, request, sock->sk)); ++} ++ ++/* revaliation, get/set attr, opt */ ++int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock, int level, ++ int optname) ++{ ++ AA_BUG(!sock); ++ AA_BUG(!sock->sk); ++ AA_BUG(in_interrupt()); ++ ++ return af_select(sock->sk->sk_family, ++ opt_perm(op, request, sock, level, optname), ++ aa_sk_perm(op, request, sock->sk)); ++} + + int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, + struct socket *sock) +@@ -180,5 +350,7 @@ int aa_sock_file_perm(struct aa_label *label, const char *op, u32 request, + AA_BUG(!sock); + AA_BUG(!sock->sk); + +- return aa_label_sk_perm(label, op, request, sock->sk); ++ return af_select(sock->sk->sk_family, ++ file_perm(label, op, request, sock), ++ aa_label_sk_perm(label, op, request, sock->sk)); + } +-- +2.11.0 + diff --git a/kernel-patches/v4.14/README b/kernel-patches/v4.14/README new file mode 100644 index 000000000..41a77f864 --- /dev/null +++ b/kernel-patches/v4.14/README @@ -0,0 +1,4 @@ +This series is based on what is currently in linux-next scheduled for +inclusion in 4.14 + +af_unix-mediation is the last remaining patch that is out of tree