From f1e158f349218cd7ef1078df13ac8f155adf47c7 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Sun, 17 Jun 2018 03:56:25 -0700 Subject: [PATCH] UBUNTU: SAUCE: apparmor4.0.0 [44/90]: patch to provide compatibility with v2.x net rules BugLink: http://bugs.launchpad.net/bugs/2028253 The networking rules upstreamed in 4.17 have a deliberate abi break with the older 2.x network rules. This patch provides compatibility with the older rules for those still using an apparmor 2.x userspace and still want network rules to work on a newer kernel. Signed-off-by: John Johansen (cherry picked from https://gitlab.com/jjohansen/apparmor-kernel) Signed-off-by: Andrea Righi (cherry picked from commit fdb71a3190842ca72ca1a84db6b5d6fb6e73b1fc https://git.launchpad.net/~apparmor-dev/ubuntu-kernel-next) Signed-off-by: Paolo Pisati --- security/apparmor/apparmorfs.c | 1 + security/apparmor/include/apparmor.h | 2 +- security/apparmor/include/net.h | 11 ++++++ security/apparmor/include/policy.h | 2 ++ security/apparmor/net.c | 29 +++++++++++---- security/apparmor/policy.c | 1 + security/apparmor/policy_unpack.c | 53 ++++++++++++++++++++++++++++ 7 files changed, 91 insertions(+), 8 deletions(-) diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index bcfea073e3f2..a9e8eac0b44b 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -2401,6 +2401,7 @@ static struct aa_sfs_entry aa_sfs_entry_features[] = { AA_SFS_DIR("domain", aa_sfs_entry_domain), AA_SFS_DIR("file", aa_sfs_entry_file), AA_SFS_DIR("network_v8", aa_sfs_entry_network), + AA_SFS_DIR("network", aa_sfs_entry_network_compat), 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/include/apparmor.h b/security/apparmor/include/apparmor.h index f83934913b0f..7e341cffbc73 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -20,7 +20,7 @@ #define AA_CLASS_UNKNOWN 1 #define AA_CLASS_FILE 2 #define AA_CLASS_CAP 3 -#define AA_CLASS_DEPRECATED 4 +#define AA_CLASS_NET_COMPAT 4 #define AA_CLASS_RLIMITS 5 #define AA_CLASS_DOMAIN 6 #define AA_CLASS_MOUNT 7 diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h index 2e43e1e8303c..cf9e1c727ce3 100644 --- a/security/apparmor/include/net.h +++ b/security/apparmor/include/net.h @@ -77,6 +77,16 @@ static inline struct aa_sk_ctx *aa_sock(const struct sock *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_compat { + u16 allow[AF_MAX]; + u16 audit[AF_MAX]; + u16 quiet[AF_MAX]; +}; #define af_select(FAMILY, FN, DEF_FN) \ ({ \ @@ -96,6 +106,7 @@ struct aa_secmark { }; extern struct aa_sfs_entry aa_sfs_entry_network[]; +extern struct aa_sfs_entry aa_sfs_entry_network_compat[]; void audit_net_cb(struct audit_buffer *ab, void *va); int aa_profile_af_perm(struct aa_profile *profile, diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index 75088cc310b6..f7d1644a7423 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -204,6 +204,7 @@ struct aa_attachment { * @disconnected: what to prepend if attach_disconnected is specified * @attach: attachment rules for the profile * @rules: rules to be enforced + * @net_compat: v2 compat network controls for the profile * * @dents: dentries for the profiles file entries in apparmorfs * @dirname: name of the profile dir in apparmorfs @@ -235,6 +236,7 @@ struct aa_profile { struct aa_attachment attach; struct list_head rules; + struct aa_net_compat *net_compat; struct aa_loaddata *rawdata; unsigned char *hash; diff --git a/security/apparmor/net.c b/security/apparmor/net.c index 77413a519117..01dccc377b3a 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -24,6 +24,11 @@ struct aa_sfs_entry aa_sfs_entry_network[] = { { } }; +struct aa_sfs_entry aa_sfs_entry_network_compat[] = { + AA_SFS_FILE_STRING("af_mask", AA_SFS_AF_MASK), + { } +}; + static const char * const net_mask_names[] = { "unknown", "send", @@ -122,14 +127,24 @@ int aa_profile_af_perm(struct aa_profile *profile, if (profile_unconfined(profile)) return 0; state = RULE_MEDIATES(rules, AA_CLASS_NET); - if (!state) - return 0; + if (state) { + buffer[0] = cpu_to_be16(family); + buffer[1] = cpu_to_be16((u16) type); + state = aa_dfa_match_len(rules->policy->dfa, state, + (char *) &buffer, 4); + perms = *aa_lookup_perms(rules->policy, state); + } else if (profile->net_compat) { + /* 2.x socket mediation compat */ + perms.allow = (profile->net_compat->allow[family] & (1 << type)) ? + ALL_PERMS_MASK : 0; + perms.audit = (profile->net_compat->audit[family] & (1 << type)) ? + ALL_PERMS_MASK : 0; + perms.quiet = (profile->net_compat->quiet[family] & (1 << type)) ? + ALL_PERMS_MASK : 0; - buffer[0] = cpu_to_be16(family); - buffer[1] = cpu_to_be16((u16) type); - state = aa_dfa_match_len(rules->policy->dfa, state, (char *) &buffer, - 4); - perms = *aa_lookup_perms(rules->policy, state); + } else { + return 0; + } aa_apply_modes_to_perms(profile, &perms); return aa_check_perms(profile, &perms, request, ad, audit_net_cb); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 957654d253dd..398937fe39e6 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -295,6 +295,7 @@ void aa_free_profile(struct aa_profile *profile) kfree_sensitive(profile->disconnected); free_attachment(&profile->attach); + kfree_sensitive(profile->net_compat); /* * at this point there are no tasks that can have a reference diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 5e578ef0ddff..c41eb89a607f 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -270,6 +270,19 @@ fail: return false; } +VISIBLE_IF_KUNIT bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) +{ + if (aa_unpack_nameX(e, AA_U16, name)) { + if (!aa_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; +} + VISIBLE_IF_KUNIT bool aa_unpack_u32(struct aa_ext *e, u32 *data, const char *name) { void *pos = e->pos; @@ -812,6 +825,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"; + u16 size = 0; size_t ns_len; struct rhashtable_params params = { 0 }; char *key = NULL, *disconnected = NULL; @@ -984,6 +998,45 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; } + if (aa_unpack_array(e, "net_allowed_af", &size) || + VERSION_LT(e->version, v8)) { + u16 i; + + profile->net_compat = kzalloc(sizeof(struct aa_net_compat), + GFP_KERNEL); + if (!profile->net_compat) { + info = "out of memory"; + goto fail; + } + 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_compat->allow[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net_compat->audit[i], NULL)) + goto fail; + if (!unpack_u16(e, &profile->net_compat->quiet[i], NULL)) + goto fail; + } + if (size && !aa_unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + if (VERSION_LT(e->version, v7)) { + /* pre v7 policy always allowed these */ + profile->net_compat->allow[AF_UNIX] = 0xffff; + profile->net_compat->allow[AF_NETLINK] = 0xffff; + } + } + if (aa_unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ info = "failed to unpack policydb";