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