diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c index 949dd8a48164a..f3fc2ba1adf1a 100644 --- a/security/apparmor/apparmorfs.c +++ b/security/apparmor/apparmorfs.c @@ -2272,6 +2272,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 73d63b58d875b..17d89f3badc65 100644 --- a/security/apparmor/include/apparmor.h +++ b/security/apparmor/include/apparmor.h @@ -24,7 +24,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 ec7228e857a90..579b59a40ea4b 100644 --- a/security/apparmor/include/net.h +++ b/security/apparmor/include/net.h @@ -72,6 +72,16 @@ struct aa_sk_ctx { 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) \ ({ \ @@ -84,6 +94,7 @@ struct aa_sk_ctx { }) 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, struct common_audit_data *sa, diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h index ab64c6b5db5ac..cbdd1b3f3907e 100644 --- a/security/apparmor/include/policy.h +++ b/security/apparmor/include/policy.h @@ -112,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_compat: v2 compat network controls for the profile * @rlimits: rlimits for the profile * * @dents: dentries for the profiles file entries in apparmorfs @@ -149,6 +150,7 @@ struct aa_profile { struct aa_policydb policy; struct aa_file_rules file; struct aa_caps caps; + struct aa_net_compat *net_compat; int xattr_count; char **xattrs; diff --git a/security/apparmor/net.c b/security/apparmor/net.c index bb24cfa0a164c..bf6aaefc3a5f5 100644 --- a/security/apparmor/net.c +++ b/security/apparmor/net.c @@ -27,6 +27,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", @@ -119,14 +124,26 @@ int aa_profile_af_perm(struct aa_profile *profile, struct common_audit_data *sa, if (profile_unconfined(profile)) return 0; state = PROFILE_MEDIATES(profile, AA_CLASS_NET); - if (!state) + if (state) { + if (!state) + return 0; + buffer[0] = cpu_to_be16(family); + buffer[1] = cpu_to_be16((u16) type); + state = aa_dfa_match_len(profile->policy.dfa, state, + (char *) &buffer, 4); + aa_compute_perms(profile->policy.dfa, state, &perms); + } 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; + + } else { return 0; - - buffer[0] = cpu_to_be16(family); - buffer[1] = cpu_to_be16((u16) type); - state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &buffer, - 4); - 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); diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index 1590e2de4e841..1a8e0502fe604 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -227,6 +227,7 @@ void aa_free_profile(struct aa_profile *profile) aa_free_file_rules(&profile->file); aa_free_cap_rules(&profile->caps); aa_free_rlimit_rules(&profile->rlimits); + kzfree(profile->net_compat); for (i = 0; i < profile->xattr_count; i++) kzfree(profile->xattrs[i]); diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c index 0e566a01d217c..77f7b95b0bdaf 100644 --- a/security/apparmor/policy_unpack.c +++ b/security/apparmor/policy_unpack.c @@ -37,7 +37,7 @@ #define v5 5 /* base version */ #define v6 6 /* per entry policydb mediation check */ -#define v7 7 +#define v7 7 /* v2 compat networking */ #define v8 8 /* full network masking */ /* @@ -292,6 +292,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)) { @@ -621,7 +634,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; @@ -759,6 +772,43 @@ static struct aa_profile *unpack_profile(struct aa_ext *e, char **ns_name) goto fail; } + size = unpack_array(e, "net_allowed_af"); + if (size || VERSION_LT(e->version, v8)) { + 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 && !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 (unpack_nameX(e, AA_STRUCT, "policydb")) { /* generic policy dfa - optional and may be NULL */ info = "failed to unpack policydb";