diff options
Diffstat (limited to '0002-apparmor-af_unix-mediation.patch')
-rw-r--r-- | 0002-apparmor-af_unix-mediation.patch | 1141 |
1 files changed, 0 insertions, 1141 deletions
diff --git a/0002-apparmor-af_unix-mediation.patch b/0002-apparmor-af_unix-mediation.patch deleted file mode 100644 index 6840208b369d..000000000000 --- a/0002-apparmor-af_unix-mediation.patch +++ /dev/null @@ -1,1141 +0,0 @@ -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 |