diff --git a/README.md b/README.md index 774f6b6..04f45ea 100644 --- a/README.md +++ b/README.md @@ -44,19 +44,14 @@ Then, you can install the package as follows: apt install ./pve-kernel-VERSION_amd64.deb ``` -## AppArmor issues -When using these kernels, Proxmox's AppArmor profiles may fail to load since it -uses an older AppArmor feature set which is not supported by these kernels anymore. -This issue also appears when launching LXC containers. -To fix this, tell AppArmor to use the stock features file as opposed to -Proxmox's features file, which is done by updating `/etc/apparmor/parser.conf` as follows: - +## AppArmor intervention +Previously, these kernels required changing the AppArmor feature file to a non-default version. +This issue has been fixed since version 5.16. +If you have used the workaround, please update back to the default configuration in `/etc/apparmor/parser.conf` as follows: ``` ## Pin feature set (avoid regressions when policy is lagging behind ## the kernel) -# lxc-pve diverts to old feature file that is incompatible with kernel -# features-file=/usr/share/apparmor-features/features -features-file=/usr/share/apparmor-features/features.stock +features-file=/usr/share/apparmor-features/features ``` ## Building manually diff --git a/debian/changelog b/debian/changelog index 36488b3..72fb708 100644 --- a/debian/changelog +++ b/debian/changelog @@ -2,6 +2,7 @@ pve-kernel (5.16.0-1) edge; urgency=medium * Disable UBNSAN (see issue #164 and #200). * Update to ZFS 2.1.2 + * Fix AppArmor incompatibilities -- Fabian Mastenbroek Wed, 10 Nov 2021 10:15:00 +0000 diff --git a/debian/patches/pve/0006-apparmor-compatibility-v2.x-net-rules.patch b/debian/patches/pve/0006-apparmor-compatibility-v2.x-net-rules.patch new file mode 100644 index 0000000..2bc0164 --- /dev/null +++ b/debian/patches/pve/0006-apparmor-compatibility-v2.x-net-rules.patch @@ -0,0 +1,250 @@ +From f153f512ed7a81e9b92a04d49869cffebf714f52 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Sun, 17 Jun 2018 03:56:25 -0700 +Subject: UBUNTU: SAUCE: apparmor: patch to provide compatibility with v2.x net + rules + +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 +[ saf: resolve conflicts when rebasing to 4.20 ] +Signed-off-by: Seth Forshee +--- + 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 | 31 ++++++++++++++++----- + security/apparmor/policy.c | 1 + + security/apparmor/policy_unpack.c | 54 ++++++++++++++++++++++++++++++++++-- + 7 files changed, 92 insertions(+), 10 deletions(-) + +(limited to 'security/apparmor') + +diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c +index 2ee3b3d..0aef8e3 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -2362,6 +2362,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 1fbabdb..5870de2 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 aadb4b2..98a42ef 100644 +--- a/security/apparmor/include/net.h ++++ b/security/apparmor/include/net.h +@@ -68,6 +68,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) \ + ({ \ +@@ -87,6 +97,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, struct common_audit_data *sa, +diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h +index b5b4b81..f904105 100644 +--- a/security/apparmor/include/policy.h ++++ b/security/apparmor/include/policy.h +@@ -108,6 +108,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 +@@ -145,6 +146,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 e0c1b50..e693df8 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", +@@ -118,14 +123,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 4c010c9..a00e39b 100644 +--- a/security/apparmor/policy.c ++++ b/security/apparmor/policy.c +@@ -222,6 +222,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); ++ kfree_sensitive(profile->net_compat); + + for (i = 0; i < profile->xattr_count; i++) + kfree_sensitive(profile->xattrs[i]); +diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c +index 4e1f96b..aedfecc 100644 +--- a/security/apparmor/policy_unpack.c ++++ b/security/apparmor/policy_unpack.c +@@ -34,7 +34,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 */ + + /* +@@ -314,6 +314,19 @@ fail: + return false; + } + ++static bool unpack_u16(struct aa_ext *e, u16 *data, const char *name) ++{ ++ if (unpack_nameX(e, AA_U16, name)) { ++ if (!inbounds(e, sizeof(u16))) ++ return 0; ++ 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) + { + void *pos = e->pos; +@@ -676,7 +689,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; +@@ -823,6 +836,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"; diff --git a/debian/patches/pve/0007-apparmor-af_unix-mediation.patch b/debian/patches/pve/0007-apparmor-af_unix-mediation.patch new file mode 100644 index 0000000..6ac0aa9 --- /dev/null +++ b/debian/patches/pve/0007-apparmor-af_unix-mediation.patch @@ -0,0 +1,1168 @@ +From c59a2789cc146827c225f9f9035b9fc23a82fc45 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Tue, 18 Jul 2017 23:27:23 -0700 +Subject: UBUNTU: SAUCE: apparmor: af_unix mediation + +af_socket mediation did not make it into 4.17 so add remaining out +of tree patch + +Signed-off-by: John Johansen +Signed-off-by: Seth Forshee +--- + security/apparmor/Makefile | 3 +- + security/apparmor/af_unix.c | 652 ++++++++++++++++++++++++++++++++++++ + security/apparmor/apparmorfs.c | 6 + + security/apparmor/file.c | 4 +- + security/apparmor/include/af_unix.h | 114 +++++++ + security/apparmor/include/net.h | 4 + + security/apparmor/include/path.h | 1 + + security/apparmor/include/policy.h | 10 +- + security/apparmor/lsm.c | 112 +++++++ + security/apparmor/net.c | 53 ++- + 10 files changed, 953 insertions(+), 6 deletions(-) + create mode 100644 security/apparmor/af_unix.c + create mode 100644 security/apparmor/include/af_unix.h + +(limited to 'security/apparmor') + +diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile +index ff23fcf..fad407f 100644 +--- a/security/apparmor/Makefile ++++ b/security/apparmor/Makefile +@@ -5,7 +5,8 @@ obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o + + apparmor-y := apparmorfs.o audit.o capability.o task.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 0000000..54b3796 +--- /dev/null ++++ b/security/apparmor/af_unix.c +@@ -0,0 +1,652 @@ ++/* ++ * AppArmor security module ++ * ++ * This file contains AppArmor af_unix fine grained mediation ++ * ++ * Copyright 2018 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/audit.h" ++#include "include/af_unix.h" ++#include "include/apparmor.h" ++#include "include/file.h" ++#include "include/label.h" ++#include "include/path.h" ++#include "include/policy.h" ++#include "include/cred.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 0aef8e3..d581800 100644 +--- a/security/apparmor/apparmorfs.c ++++ b/security/apparmor/apparmorfs.c +@@ -2346,6 +2346,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), +@@ -2370,6 +2375,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 e1b7e936..866272c 100644 +--- a/security/apparmor/file.c ++++ b/security/apparmor/file.c +@@ -14,6 +14,7 @@ + #include + #include + ++#include "include/af_unix.h" + #include "include/apparmor.h" + #include "include/audit.h" + #include "include/cred.h" +@@ -271,7 +272,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 0000000..d1b7f23 +--- /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 98a42ef..b4044fa 100644 +--- a/security/apparmor/include/net.h ++++ b/security/apparmor/include/net.h +@@ -49,6 +49,7 @@ + struct aa_sk_ctx { + struct aa_label *label; + struct aa_label *peer; ++ struct path path; + }; + + #define SK_CTX(X) ((X)->sk_security) +@@ -83,6 +84,9 @@ struct aa_net_compat { + ({ \ + int __e; \ + switch ((FAMILY)) { \ ++ case AF_UNIX: \ ++ __e = aa_unix_ ## FN; \ ++ break; \ + default: \ + __e = DEF_FN; \ + } \ +diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h +index 44a7945..44592cd 100644 +--- a/security/apparmor/include/path.h ++++ b/security/apparmor/include/path.h +@@ -13,6 +13,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 f904105..f1c9cdc 100644 +--- a/security/apparmor/include/policy.h ++++ b/security/apparmor/include/policy.h +@@ -230,9 +230,13 @@ static inline unsigned int PROFILE_MEDIATES_AF(struct aa_profile *profile, + unsigned int state = PROFILE_MEDIATES(profile, AA_CLASS_NET); + __be16 be_af = cpu_to_be16(AF); + +- if (!state) +- return 0; +- return aa_dfa_match_len(profile->policy.dfa, state, (char *) &be_af, 2); ++ if (!state) { ++ state = PROFILE_MEDIATES(profile, AA_CLASS_NET_COMPAT); ++ if (!state) ++ return 0; ++ } ++ state = aa_dfa_match_len(profile->policy.dfa, state, (char *) &be_af, 2); ++ return state; + } + + /** +diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c +index f72406f..59a8ddd 100644 +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -25,6 +25,7 @@ + #include + #include + ++#include "include/af_unix.h" + #include "include/apparmor.h" + #include "include/apparmorfs.h" + #include "include/audit.h" +@@ -801,6 +802,7 @@ static void apparmor_sk_free_security(struct sock *sk) + SK_CTX(sk) = NULL; + aa_put_label(ctx->label); + aa_put_label(ctx->peer); ++ path_put(&ctx->path); + kfree(ctx); + } + +@@ -820,6 +822,99 @@ static void apparmor_sk_clone_security(const struct sock *sk, + if (new->peer) + aa_put_label(new->peer); + new->peer = aa_get_label(ctx->peer); ++ new->path = ctx->path; ++ path_get(&new->path); ++} ++ ++static struct path *UNIX_FS_CONN_PATH(struct sock *sk, struct sock *newsk) ++{ ++ 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; ++ ++ 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; + } + + /** +@@ -1065,11 +1160,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); + } + +@@ -1216,6 +1325,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 e693df8..e2e759b 100644 +--- a/security/apparmor/net.c ++++ b/security/apparmor/net.c +@@ -8,6 +8,7 @@ + * Copyright 2009-2017 Canonical Ltd. + */ + ++#include "include/af_unix.h" + #include "include/apparmor.h" + #include "include/audit.h" + #include "include/cred.h" +@@ -26,6 +27,7 @@ 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), ++ AA_SFS_FILE_BOOLEAN("af_unix", 1), + { } + }; + +@@ -71,6 +73,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) +@@ -102,6 +134,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, +@@ -202,7 +251,9 @@ 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)); + } + + #ifdef CONFIG_NETWORK_SECMARK diff --git a/debian/patches/pve/0008-apparmor-fix-apparmor-mediating-locking-non-fs-unix-sockets.patch b/debian/patches/pve/0008-apparmor-fix-apparmor-mediating-locking-non-fs-unix-sockets.patch new file mode 100644 index 0000000..c210027 --- /dev/null +++ b/debian/patches/pve/0008-apparmor-fix-apparmor-mediating-locking-non-fs-unix-sockets.patch @@ -0,0 +1,38 @@ +From d7410054784d8aa0e313f9eeb6110a791420f3d3 Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Mon, 30 Jul 2018 13:55:30 -0700 +Subject: UBUNTU SAUCE: apparmor: fix apparmor mediating locking non-fs, unix + sockets + +The apparmor policy language current does not allow expressing of the +locking permission for no-fs unix sockets. However the kernel is +enforcing mediation. + +Add the AA_MAY_LOCK perm to the computed perm mask which will grant +permission for all current abi profiles, but still allow specifying +auditing of the operation if needed. + +BugLink: http://bugs.launchpad.net/bugs/1780227 +Signed-off-by: John Johansen +Acked-by: Acked-by: Stefan Bader +Acked-by: Acked-by: Kleber Sacilotto de Souza +Signed-off-by: Seth Forshee +--- + security/apparmor/lib.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +(limited to 'security/apparmor') + +diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c +index fa49b81..bf72843 100644 +--- a/security/apparmor/lib.c ++++ b/security/apparmor/lib.c +@@ -334,7 +334,7 @@ void aa_compute_perms(struct aa_dfa *dfa, unsigned int state, + /* for v5 perm mapping in the policydb, the other set is used + * to extend the general perm set + */ +- perms->allow |= map_other(dfa_other_allow(dfa, state)); ++ perms->allow |= map_other(dfa_other_allow(dfa, state)) | AA_MAY_LOCK; + perms->audit |= map_other(dfa_other_audit(dfa, state)); + perms->quiet |= map_other(dfa_other_quiet(dfa, state)); + // perms->xindex = dfa_user_xindex(dfa, state); diff --git a/debian/patches/pve/0009-apparmor-fix-use-after-free-in-sk_peer_label.patch b/debian/patches/pve/0009-apparmor-fix-use-after-free-in-sk_peer_label.patch new file mode 100644 index 0000000..d31de83 --- /dev/null +++ b/debian/patches/pve/0009-apparmor-fix-use-after-free-in-sk_peer_label.patch @@ -0,0 +1,57 @@ +From e9243f6a285589f49161faf0f96f4cf15c1dafae Mon Sep 17 00:00:00 2001 +From: John Johansen +Date: Tue, 26 Jun 2018 20:19:19 -0700 +Subject: UBUNTU: SAUCE: apparmor: fix use after free in sk_peer_label + +BugLink: http://bugs.launchpad.net/bugs/1778646 +Signed-off-by: John Johansen +Signed-off-by: Seth Forshee +--- + security/apparmor/lsm.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +(limited to 'security/apparmor') + +diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c +index 59a8ddd..b1216ee 100644 +--- a/security/apparmor/lsm.c ++++ b/security/apparmor/lsm.c +@@ -1162,9 +1162,10 @@ static struct aa_label *sk_peer_label(struct sock *sk) + { + struct sock *peer_sk; + struct aa_sk_ctx *ctx = SK_CTX(sk); ++ struct aa_label *label = ERR_PTR(-ENOPROTOOPT); + + if (ctx->peer) +- return ctx->peer; ++ return aa_get_label(ctx->peer); + + if (sk->sk_family != PF_UNIX) + return ERR_PTR(-ENOPROTOOPT); +@@ -1172,14 +1173,15 @@ static struct aa_label *sk_peer_label(struct sock *sk) + /* check for sockpair peering which does not go through + * security_unix_stream_connect + */ +- peer_sk = unix_peer(sk); ++ peer_sk = unix_peer_get(sk); + if (peer_sk) { + ctx = SK_CTX(peer_sk); + if (ctx->label) +- return ctx->label; ++ label = aa_get_label(ctx->label); ++ sock_put(peer_sk); + } + +- return ERR_PTR(-ENOPROTOOPT); ++ return label; + } + + /** +@@ -1223,6 +1225,7 @@ out: + + } + ++ aa_put_label(peer); + done: + end_current_label_crit_section(label); + \ No newline at end of file diff --git a/debian/patches/series.linux b/debian/patches/series.linux index ff37420..b33879f 100644 --- a/debian/patches/series.linux +++ b/debian/patches/series.linux @@ -3,3 +3,7 @@ pve/0002-bridge-keep-MAC-of-first-assigned-port.patch pve/0003-pci-Enable-overrides-for-missing-ACS-capabilities-4..patch pve/0004-kvm-disable-default-dynamic-halt-polling-growth.patch pve/0005-net-core-downgrade-unregister_netdevice-refcount-lea.patch +pve/0006-apparmor-compatibility-v2.x-net-rules.patch +pve/0007-apparmor-af_unix-mediation.patch +pve/0008-apparmor-fix-apparmor-mediating-locking-non-fs-unix-sockets.patch +pve/0009-apparmor-fix-use-after-free-in-sk_peer_label.patch