summarylogtreecommitdiffstats
path: root/0202-apparmor-af_unix-mediation.patch
diff options
context:
space:
mode:
Diffstat (limited to '0202-apparmor-af_unix-mediation.patch')
-rw-r--r--0202-apparmor-af_unix-mediation.patch1141
1 files changed, 1141 insertions, 0 deletions
diff --git a/0202-apparmor-af_unix-mediation.patch b/0202-apparmor-af_unix-mediation.patch
new file mode 100644
index 00000000000..6840208b369
--- /dev/null
+++ b/0202-apparmor-af_unix-mediation.patch
@@ -0,0 +1,1141 @@
+diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
+index ff23fcfefe196da8d7e09f18c9eb8a4c4a5c2c08..fad407f6f62c82527e256866392508edd635d6d0 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 0000000000000000000000000000000000000000..54b3796f63d0d207f04397e9e3445db5c784d0bd
+--- /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 <net/tcp_states.h>
++
++#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 a4ed6163c03737d90e64e50b4bc4c24c031e7799..5ce4d95d48fba4feb60075134cd8e66f395b8a62 100644
+--- a/security/apparmor/apparmorfs.c
++++ b/security/apparmor/apparmorfs.c
+@@ -2249,6 +2249,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),
+@@ -2273,6 +2278,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 4c1b05eb130c32422c238089dd61c539037d30f8..8aa7af54b70af79c5cdd06d80de9cb9737348d29 100644
+--- a/security/apparmor/file.c
++++ b/security/apparmor/file.c
+@@ -12,6 +12,7 @@
+ #include <linux/fdtable.h>
+ #include <linux/file.h>
+
++#include "include/af_unix.h"
+ #include "include/apparmor.h"
+ #include "include/audit.h"
+ #include "include/cred.h"
+@@ -280,7 +281,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 0000000000000000000000000000000000000000..d1b7f2316be47d7631499ccbecc79e43c2a00c3b
+--- /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 <net/af_unix.h>
++
++#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(" <anonymous>");
++ 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); : "<null>"); print_sk(sock); \
++ printk(" other %s:", other_ctx && other_ctx->label ? aa_label_printk(other_ctx->label, GFP_ATOMIC); : "<null>"); print_sk(other); \
++ printk(" new %s", new_ctx && new_ctx->label ? aa_label_printk(new_ctx->label, GFP_ATOMIC); : "<null>"); 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 74768db9406656cb5ac080cd23d3a4faafd9e1ea..4ff96fa0b0b501eacc7ce48250394c48316e5080 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 35a8295e8f3a96867d3c49f50f0de2131fca5661..de800033195361415fad1f2c28e09ccdfbeece58 100644
+--- a/security/apparmor/include/path.h
++++ b/security/apparmor/include/path.h
+@@ -14,6 +14,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 6515a9174835c7d41279da92e6cafd72868ef181..a04d71803aeb53dacbb032d2a575db0a745153cb 100644
+--- a/security/apparmor/include/policy.h
++++ b/security/apparmor/include/policy.h
+@@ -221,9 +221,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 ec3a928af8299a1ccca676575c9b469c977ae3e5..5c54d4588ede7be8a7d14469dec9129f9dafc406 100644
+--- a/security/apparmor/lsm.c
++++ b/security/apparmor/lsm.c
+@@ -24,6 +24,7 @@
+ #include <net/sock.h>
+ #include <uapi/linux/mount.h>
+
++#include "include/af_unix.h"
+ #include "include/apparmor.h"
+ #include "include/apparmorfs.h"
+ #include "include/audit.h"
+@@ -779,6 +780,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);
+ }
+
+@@ -793,6 +795,99 @@ static void apparmor_sk_clone_security(const struct sock *sk,
+
+ new->label = aa_get_label(ctx->label);
+ 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;
+ }
+
+ /**
+@@ -1038,11 +1133,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);
+ }
+
+@@ -1189,6 +1298,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 1d8f5ff53cd4c19b73532387accb034589ef1122..297589c6dc95ec88b27b55ffac51a7ceb8ce07ff 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)
+@@ -100,6 +132,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,
+@@ -200,7 +249,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