summaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/apparmor/Kconfig14
-rw-r--r--security/apparmor/Makefile2
-rw-r--r--security/apparmor/af_inet.c565
-rw-r--r--security/apparmor/af_unix.c44
-rw-r--r--security/apparmor/apparmorfs.c281
-rw-r--r--security/apparmor/audit.c40
-rw-r--r--security/apparmor/capability.c2
-rw-r--r--security/apparmor/domain.c157
-rw-r--r--security/apparmor/file.c15
-rw-r--r--security/apparmor/include/af_inet.h36
-rw-r--r--security/apparmor/include/apparmor.h1
-rw-r--r--security/apparmor/include/apparmorfs.h3
-rw-r--r--security/apparmor/include/audit.h10
-rw-r--r--security/apparmor/include/capability.h3
-rw-r--r--security/apparmor/include/cred.h22
-rw-r--r--security/apparmor/include/file.h2
-rw-r--r--security/apparmor/include/label.h31
-rw-r--r--security/apparmor/include/lib.h6
-rw-r--r--security/apparmor/include/match.h18
-rw-r--r--security/apparmor/include/net.h9
-rw-r--r--security/apparmor/include/path.h3
-rw-r--r--security/apparmor/include/perms.h19
-rw-r--r--security/apparmor/include/policy.h17
-rw-r--r--security/apparmor/include/policy_unpack.h4
-rw-r--r--security/apparmor/include/procattr.h2
-rw-r--r--security/apparmor/include/task.h20
-rw-r--r--security/apparmor/label.c95
-rw-r--r--security/apparmor/lib.c57
-rw-r--r--security/apparmor/lsm.c282
-rw-r--r--security/apparmor/match.c35
-rw-r--r--security/apparmor/mount.c279
-rw-r--r--security/apparmor/net.c47
-rw-r--r--security/apparmor/policy.c30
-rw-r--r--security/apparmor/policy_compat.c14
-rw-r--r--security/apparmor/policy_unpack.c30
-rw-r--r--security/apparmor/task.c27
-rw-r--r--security/keys/trusted-keys/trusted_tpm1.c45
-rw-r--r--security/keys/trusted-keys/trusted_tpm2.c166
38 files changed, 1712 insertions, 721 deletions
diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig
index 1e3bd44643da..a1f3749bdfd0 100644
--- a/security/apparmor/Kconfig
+++ b/security/apparmor/Kconfig
@@ -11,7 +11,7 @@ config SECURITY_APPARMOR
This enables the AppArmor security module.
Required userspace tools (if they are not included in your
distribution) and further information may be found at
- http://apparmor.wiki.kernel.org
+ https://apparmor.wiki.kernel.org
If you are unsure how to answer this question, answer N.
@@ -93,6 +93,18 @@ config SECURITY_APPARMOR_EXPORT_BINARY
also increases policy load time. This option is required for
checkpoint and restore support, and debugging of loaded policy.
+config SECURITY_APPARMOR_COMPRESSED_POLICY
+ bool "Allow loading policy in a compressed format"
+ depends on SECURITY_APPARMOR
+ select ZSTD_DECOMPRESS
+ default y
+ help
+ This option allows loading policy from userspace in a
+ compressed format. This allows for userspace to not have to
+ decrompress caches before loading policy, and also allows
+ for less kernel memory to be used when "exporting the raw
+ binary policy" is enabled.
+
config SECURITY_APPARMOR_PARANOID_LOAD
bool "Perform full verification of loaded policy"
depends on SECURITY_APPARMOR
diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile
index 12fb419714c0..8aed0d7ed6f5 100644
--- a/security/apparmor/Makefile
+++ b/security/apparmor/Makefile
@@ -6,7 +6,7 @@ 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 \
- policy_compat.o af_unix.o
+ policy_compat.o af_unix.o af_inet.o
apparmor-$(CONFIG_SECURITY_APPARMOR_HASH) += crypto.o
obj-$(CONFIG_SECURITY_APPARMOR_KUNIT_TEST) += apparmor_policy_unpack_test.o
diff --git a/security/apparmor/af_inet.c b/security/apparmor/af_inet.c
new file mode 100644
index 000000000000..516c9016537c
--- /dev/null
+++ b/security/apparmor/af_inet.c
@@ -0,0 +1,565 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor inet fine grained mediation
+ *
+ * Copyright 2024 Canonical Ltd.
+ *
+ */
+
+#include <net/tcp_states.h>
+
+#include "include/audit.h"
+#include "include/af_inet.h"
+#include "include/apparmor.h"
+#include "include/file.h"
+#include "include/label.h"
+#include "include/net.h"
+#include "include/path.h"
+#include "include/policy.h"
+#include "include/cred.h"
+
+
+static inline aa_state_t RULE_MEDIATES_SK(struct aa_ruleset *rules,
+ const struct sock *sk)
+{
+ return RULE_MEDIATES_NET(rules);
+}
+
+
+enum addr_type {
+ ADDR_LOCAL = 0,
+ ADDR_LOCAL_PRIV = 1,
+ ADDR_REMOTE = 2,
+};
+
+struct match_addr {
+ const char *addrp;
+ enum addr_type addrtype;
+ int len;
+ __be16 port;
+};
+
+struct stored_match_addr {
+ union {
+ struct sockaddr addr;
+ struct sockaddr_in addr4;
+ struct sockaddr_in6 addr6;
+ };
+ int addrlen;
+ struct match_addr maddr;
+};
+
+static void set_ad_create(struct apparmor_audit_data *ad,
+ int family, int type, int protocol)
+{
+ ad->common.u.net->family = family;
+ ad->net.type = type;
+ ad->net.protocol = protocol;
+}
+
+static int set_ad_addr(struct apparmor_audit_data *ad,
+ u16 family, bool source, struct match_addr *maddr)
+{
+ ad->common.u.net->family = family;
+
+ if (source) {
+ ad->common.u.net->sport = maddr->port;
+ if (maddr->addrp) {
+ if (family == AF_INET)
+ /* ad.u.net->v4info.saddr = addr4->sin_addr.s_addr; */
+ ad->common.u.net->v4info.saddr = *(__be32 *)maddr->addrp;
+ else
+ /* ad.u.net->v4info.saddr = addr6->sin6_addr.s6_addr; */
+ ad->common.u.net->v6info.saddr = *(struct in6_addr *)maddr->addrp;
+ }
+ } else {
+ ad->common.u.net->dport = maddr->port;
+ if (maddr->addrp) {
+ if (family == AF_INET)
+ /* ad.u.net->v4info.saddr = addr4->sin_addr.s_addr; */
+ ad->common.u.net->v4info.daddr = *(__be32 *)maddr->addrp;
+ else
+ /* ad.u.net->v4info.saddr = addr6->sin6_addr.s6_addr; */
+ ad->common.u.net->v6info.daddr = *(struct in6_addr *)maddr->addrp;
+ }
+ }
+ return 0;
+}
+
+/* returns 0 on success
+* raw_port - if set raw_port (protocol) when SOCK_RAW */
+static int map_addr(struct sockaddr *addr, int addrlen, u16 raw_port,
+ enum addr_type addrtype, struct match_addr *maddr,
+ struct apparmor_audit_data *ad)
+{
+ struct sockaddr_in *addr4 = NULL;
+ struct sockaddr_in6 *addr6 = NULL;
+
+ AA_BUG(!addr);
+ AA_BUG(!maddr);
+
+ maddr->addrtype = addrtype;
+ if (!addr || addrlen < offsetofend(struct sockaddr, sa_family)) {
+ maddr->addrp = NULL;
+ maddr->port = 0;
+ maddr->len = 0;
+ return 0;
+ }
+
+ /*
+ * its possibly to have sk->sk_family == PF_INET6 and
+ * addr->sa_family == AF_INET. sk_family is used for socket
+ * mediation, sa_family for when we have address ...
+ */
+ switch (addr->sa_family) {
+ case AF_INET:
+ addr4 = (struct sockaddr_in *)addr;
+ if (addrlen < sizeof(struct sockaddr_in))
+ return -EINVAL;
+ maddr->port = addr4->sin_port;
+ maddr->addrp = (char *)&addr4->sin_addr.s_addr;
+ maddr->len = 4;
+ break;
+ case AF_INET6:
+ addr6 = (struct sockaddr_in6 *)addr;
+ if (addrlen < SIN6_LEN_RFC2133)
+ return -EINVAL;
+ maddr->port = addr6->sin6_port;
+ maddr->addrp = (char *)&addr6->sin6_addr.s6_addr;
+ maddr->len = 16;
+ break;
+ default:
+ return -EAFNOSUPPORT;
+ }
+ /* per ip spec, && sk->sk_type == SOCK_RAW*/
+ if (raw_port && addrtype != ADDR_REMOTE)
+ maddr->port = htons(raw_port);
+ if (ad)
+ set_ad_addr(ad, addr->sa_family, addrtype != ADDR_REMOTE, maddr);
+
+ return 0;
+}
+
+/* -ENOTCONN if not connected */
+static int map_sock_addr(struct socket *sock, enum addr_type addrtype,
+ struct stored_match_addr *maddr,
+ struct apparmor_audit_data *ad)
+{
+ /* do we need early bailout for !family ... */
+ maddr->addrlen = sock->ops->getname(sock, (struct sockaddr *) &maddr->addr, addrtype != ADDR_REMOTE ? 0 : 1);
+ if (maddr->addrlen == -ENOTCONN) {
+ maddr->addrlen = 0;
+ return map_addr(NULL, 0, 0, addrtype, &maddr->maddr, ad);
+ } else if (maddr->addrlen < 0)
+ return maddr->addrlen;
+ return map_addr(&maddr->addr, maddr->addrlen, 0, addrtype,
+ &maddr->maddr, ad);
+}
+
+/* TODO: combine with connect map addr */
+/* TODO: raw_port */
+static int bind_map_addr(const struct sock *sk, struct sockaddr *addr,
+ int addrlen,
+ struct match_addr *maddr,
+ struct apparmor_audit_data *ad)
+{
+ struct sockaddr_in *addr4 = NULL;
+ struct sockaddr_in6 *addr6 = NULL;
+ u16 family;
+
+ AA_BUG(!sk);
+ AA_BUG(!addr);
+ AA_BUG(!maddr);
+
+ if (addrlen < offsetofend(struct sockaddr, sa_family))
+ return -EINVAL;
+
+ maddr->addrtype = ADDR_LOCAL;
+ /*
+ * its possibly to have sk->sk_family == PF_INET6 and
+ * addr->sa_family == AF_INET. sk_family is used for socket
+ * mediation, sa_family for when we have address ...
+ */
+ family = addr->sa_family;
+ switch (addr->sa_family) {
+ case AF_UNSPEC:
+ if (sk->sk_family == PF_INET6) {
+ /* Length check from inet6_bind_sk() */
+ if (addrlen < SIN6_LEN_RFC2133)
+ return -EINVAL;
+ /* Family check from __inet6_bind() */
+ return -EAFNOSUPPORT;
+ }
+ /* see __inet_bind(), we only want to allow
+ * AF_UNSPEC if the address is INADDR_ANY
+ */
+ addr4 = (struct sockaddr_in *)addr;
+ if (addr4->sin_addr.s_addr != htonl(INADDR_ANY))
+ return -EAFNOSUPPORT;
+ family = AF_INET;
+ fallthrough;
+ case AF_INET:
+ addr4 = (struct sockaddr_in *)addr;
+ if (addrlen < sizeof(struct sockaddr_in))
+ return -EINVAL;
+ maddr->port = addr4->sin_port;
+ maddr->addrp = (char *)&addr4->sin_addr.s_addr;
+ maddr->len = 4;
+ break;
+ case AF_INET6:
+ addr6 = (struct sockaddr_in6 *)addr;
+ if (addrlen < SIN6_LEN_RFC2133)
+ return -EINVAL;
+ maddr->port = addr6->sin6_port;
+ maddr->addrp = (char *)&addr6->sin6_addr.s6_addr;
+ maddr->len = 16;
+ break;
+ default:
+ return -EAFNOSUPPORT;
+ }
+
+ if (ad)
+ set_ad_addr(ad, family, true, maddr);
+
+ return 0;
+}
+
+
+static inline int profile_sk_perm(struct aa_profile *profile, u32 request,
+ const struct sock *sk,
+ struct match_addr *maddr,
+ struct apparmor_audit_data *ad)
+{
+ AA_BUG(!profile);
+ AA_BUG(!sk);
+
+ return aa_profile_af_sk_perm(profile, ad, request, sk);
+}
+
+/* no kernel_t bailout */
+static int profile_create_perm(struct aa_profile *profile, int family,
+ int type, int protocol,
+ struct apparmor_audit_data *ad)
+{
+ AA_BUG(!profile);
+
+ return aa_profile_af_perm(profile, ad, AA_MAY_CREATE, family, type,
+ protocol);
+}
+
+
+/* sendmsg/rcvmsg/connect */
+static int profile_remote_perm(struct aa_profile *profile,
+ const struct sock *sk,
+ u32 request, struct match_addr *raddr,
+ struct match_addr *laddr,
+ struct apparmor_audit_data *ad)
+{
+ AA_BUG(!profile);
+ AA_BUG(!sk);
+ AA_BUG(!raddr);
+ AA_BUG(!laddr);
+ AA_BUG(sk->sk_family != PF_INET && sk->sk_family != PF_INET6,
+ "family=%d", sk->sk_family);
+
+ return aa_profile_af_sk_perm(profile, ad, request, sk);
+}
+
+static int profile_bind_perm(struct aa_profile *profile,
+ const struct sock *sk,
+ struct match_addr *maddr,
+ struct apparmor_audit_data *ad)
+{
+ return aa_profile_af_sk_perm(profile, ad, AA_MAY_BIND, sk);
+
+}
+
+static int profile_listen_perm(struct aa_profile *profile,
+ const struct sock *sk,
+ struct match_addr *maddr, int backlog,
+ struct apparmor_audit_data *ad)
+{
+ AA_BUG(!profile);
+ AA_BUG(!sk);
+ AA_BUG(!maddr);
+ AA_BUG(sk->sk_family != PF_INET && sk->sk_family != PF_INET6,
+ "family=%d", sk->sk_family);
+
+ return aa_profile_af_sk_perm(profile, ad, AA_MAY_LISTEN, sk);
+}
+
+static inline int profile_accept_perm(struct aa_profile *profile,
+ const struct sock *sk,
+ struct match_addr *maddr,
+ const struct sock *newsk,
+ struct apparmor_audit_data *ad)
+{
+ AA_BUG(!profile);
+ AA_BUG(!sk);
+ /* AA_BUG(!newsk); newsk can be null here, since not using atm ... */
+ AA_BUG(!maddr);
+ AA_BUG(sk->sk_family != PF_INET && sk->sk_family != PF_INET6,
+ "family=%d", sk->sk_family);
+
+ return aa_profile_af_sk_perm(profile, ad, AA_MAY_ACCEPT, sk);
+}
+
+/* getopt/setopt */
+static int profile_opt_perm(struct aa_profile *profile, u32 request,
+ const struct sock *sk, struct match_addr *maddr,
+ int level, int optname,
+ struct apparmor_audit_data *ad)
+{
+ AA_BUG(!profile);
+ AA_BUG(!sk);
+ AA_BUG(!maddr);
+ AA_BUG(sk->sk_family != PF_INET && sk->sk_family != PF_INET6,
+ "family=%d", sk->sk_family);
+
+ return aa_profile_af_sk_perm(profile, ad, request, sk);
+}
+
+/* ---------------------------------------------------------------------- */
+
+// TODO: cleanup init to use recursion, so we can have N init fns, in 1 macro
+// TODO: lift DEFINE_AUDIT out of macro into init fn???
+
+/* no kernel_t bailout */
+#define label_sk_has_perm2(CRED, LABEL, SOCKSK, OP, REQUEST, PROFILE, AAD, XXXX, YYYY, CALLBACKFN) \
+({ \
+ int __EERROR = 0; \
+ if (label_mediates(LABEL, AA_CLASS_NET)) { \
+ struct aa_profile *PROFILE; \
+ DEFINE_AUDIT_SK(AAD, OP, CRED, SOCKSK); \
+ (AAD).subj_cred = (CRED); \
+ (AAD).request = (REQUEST); \
+ __EERROR = (XXXX); \
+ if (__EERROR == 0) { \
+ __EERROR = (YYYY); \
+ if (__EERROR == 0) { \
+ __EERROR = fn_for_each(LABEL, PROFILE, \
+ (CALLBACKFN)); \
+ } \
+ } \
+ } \
+ __EERROR; \
+})
+
+/* no kernel_t bailout */
+#define label_sk_has_perm(CRED, LABEL, SOCKSK, OP, REQUEST, PROFILE, AAD, CALLBACKFN) \
+ label_sk_has_perm2(CRED, LABEL, SOCKSK, OP, REQUEST, PROFILE, AAD, \
+ 0, 0, CALLBACKFN)
+
+/* no kernel_t bailout */
+#define label_sk_has_perm1(CRED, LABEL, SOCKSK, OP, REQUEST, PROFILE, AAD, XXXX, CALLBACKFN) \
+ label_sk_has_perm2(CRED, LABEL, SOCKSK, OP, REQUEST, PROFILE, AAD, \
+ XXXX, 0, CALLBACKFN)
+
+
+/* Early bailout for kernel_t - 2 init args before callback */
+#define sk_has_perm2(SOCKSK, OP, REQUEST, PROFILE, AAD, XXXXY, YYYYX, CALLBACKFN) \
+({ \
+ struct aa_label *__label; \
+ struct aa_sk_ctx *__ctx = aa_sock(SOCKSK); \
+ int __ERROR = 0; \
+ bool __needput; \
+ if (rcu_access_pointer(__ctx->label) != kernel_t) { \
+ \
+ __label = begin_current_label_crit_section(&__needput); \
+ __ERROR = label_sk_has_perm2(current_cred(), __label, SOCKSK, OP, REQUEST, PROFILE, AAD, XXXXY, YYYYX, CALLBACKFN); \
+ end_current_label_crit_section(__label, __needput); \
+ } \
+ __ERROR; \
+})
+
+/* Early bailout for kernel_t - no init args before callback */
+#define sk_has_perm(SOCKSK, OP, REQUEST, PROFILE, AAD, CALLBACKFN) \
+ sk_has_perm2(SOCKSK, OP, REQUEST, PROFILE, AAD, 0, 0, CALLBACKFN)
+
+
+/* Early bailout for kernel_t - 1 init arg before callback */
+#define sk_has_perm1(SOCKSK, OP, REQUEST, PROFILE, AAD, XXXXY, CALLBACKFN) \
+ sk_has_perm2(SOCKSK, OP, REQUEST, PROFILE, AAD, XXXXY, 0, CALLBACKFN)
+
+
+
+/* no kernel_t early bailout */
+/* NOTE: already lifted label_mediates into lsm.c */
+int aa_inet_create_perm(struct aa_label *label, int family, int type,
+ int protocol)
+{
+ struct aa_profile *profile;
+ int error = 0;
+ DEFINE_AUDIT_NET(ad, OP_CREATE, current_cred(), NULL, family, type,
+ protocol);
+
+ ad.subj_cred = current_cred();
+ set_ad_create(&ad, family, type, protocol);
+ error = fn_for_each(label, profile,
+ profile_create_perm(profile, family, type,
+ protocol, &ad));
+
+ return error;
+}
+
+int aa_inet_bind_perm(struct socket *sock, struct sockaddr *addr,
+ int addrlen)
+{
+ struct match_addr maddr;
+
+ return sk_has_perm1(sock->sk, OP_BIND, AA_MAY_BIND, profile, ad,
+ bind_map_addr(sock->sk, addr, addrlen, &maddr,
+ &ad),
+ profile_bind_perm(profile, sock->sk, &maddr, &ad));
+}
+
+int aa_inet_connect_perm(struct socket *sock, struct sockaddr *addr,
+ int addrlen)
+{
+ struct stored_match_addr laddr;
+ struct match_addr raddr;
+
+ /* disconnect socket */
+ if (addrlen < offsetofend(struct sockaddr, sa_family))
+ return -EINVAL;
+ if (addr->sa_family == AF_UNSPEC)
+ return 0;
+
+ /* do we need early bailout for !family ... */
+ return sk_has_perm2(sock->sk, OP_CONNECT, AA_MAY_CONNECT, profile, ad,
+ map_sock_addr(sock, ADDR_LOCAL, &laddr, &ad),
+ map_addr(addr, addrlen, 0, ADDR_REMOTE, &raddr,
+ &ad),
+ profile_remote_perm(profile, sock->sk,
+ AA_MAY_CONNECT, &raddr,
+ &laddr.maddr, &ad));
+}
+
+int aa_inet_listen_perm(struct socket *sock, int backlog)
+{
+ struct stored_match_addr maddr;
+
+ /* do we need early bailout for !family ... */
+ return sk_has_perm1(sock->sk, OP_LISTEN, AA_MAY_LISTEN, profile, ad,
+ map_sock_addr(sock, ADDR_LOCAL, &maddr, &ad),
+ profile_listen_perm(profile, sock->sk, &maddr.maddr,
+ backlog, &ad));
+}
+
+/* ability of sock to connect, not peer address binding */
+int aa_inet_accept_perm(struct socket *sock, struct socket *newsock)
+{
+ struct stored_match_addr maddr;
+ int error;
+
+ error = sk_has_perm1(sock->sk, OP_ACCEPT, AA_MAY_ACCEPT, profile, ad,
+ map_sock_addr(sock, ADDR_LOCAL, &maddr, &ad),
+ profile_accept_perm(profile, sock->sk,
+ &maddr.maddr,
+ newsock->sk, &ad));
+
+ /* selinux updates inode - need to investigate this more */
+ return error;
+}
+
+/* sendmsg, recvmsg. */
+int aa_inet_msg_perm(const char *op, u32 request, struct socket *sock,
+ struct msghdr *msg, int size)
+{
+ struct stored_match_addr laddr;
+ struct match_addr raddr;
+
+ /* do we need early bailout for !family ... */
+ return sk_has_perm2(sock->sk, op, request, profile, ad,
+ map_sock_addr(sock, ADDR_LOCAL, &laddr, &ad),
+ map_addr(msg->msg_name, msg->msg_namelen, 0,
+ ADDR_REMOTE, &raddr, &ad),
+ profile_remote_perm(profile, sock->sk, request,
+ &raddr, &laddr.maddr, &ad));
+}
+
+/* getopt, setopt */
+int aa_inet_opt_perm(const char *op, u32 request, struct socket *sock,
+ int level, int optname)
+{
+ struct stored_match_addr maddr;
+
+ return sk_has_perm1(sock->sk, op, request, profile, ad,
+ map_sock_addr(sock, ADDR_LOCAL, &maddr, &ad),
+ profile_opt_perm(profile, request, sock->sk,
+ &maddr.maddr, level, optname, &ad));
+}
+
+static int inet_label_sock_perm(const struct cred *cred, struct aa_label *label,
+ const char *op, u32 request,
+ struct socket *sock)
+{
+ struct stored_match_addr maddr;
+
+ return label_sk_has_perm1(cred, label, sock->sk, op, request, profile,
+ ad,
+ map_sock_addr(sock, ADDR_LOCAL, &maddr, &ad),
+ profile_sk_perm(profile, request, sock->sk,
+ &maddr.maddr, &ad));
+}
+
+/* revalidation, get/set attr/getsockname/peername */
+int aa_inet_sock_perm(const char *op, u32 request, struct socket *sock)
+{
+ struct aa_sk_ctx *ctx = aa_sock(sock->sk);
+ struct aa_label *label;
+ bool needput;
+ int error;
+
+ if (rcu_access_pointer(ctx->label) == kernel_t)
+ return 0;
+
+ label = begin_current_label_crit_section(&needput);
+ error = inet_label_sock_perm(current_cred(), label, op, request, sock);
+ end_current_label_crit_section(label, needput);
+
+ return error;
+}
+
+int aa_inet_file_perm(const struct cred *subj_cred, struct aa_label *label,
+ const char *op, u32 request, struct socket *sock)
+{
+ u32 sk_req = request & ~NET_PEER_MASK;
+ struct stored_match_addr laddr;
+ const struct sock *sk = sock->sk;
+ int error = 0;
+
+ AA_BUG(!label);
+ AA_BUG(!sock);
+ AA_BUG(!sock->sk);
+ AA_BUG(sk->sk_family != PF_INET && sk->sk_family != PF_INET6,
+ "family=%d", sk->sk_family);
+
+ /* access to the local sock */
+ error = label_sk_has_perm1(subj_cred, label, sock->sk, op, request,
+ profile, ad,
+ map_sock_addr(sock, ADDR_LOCAL, &laddr, &ad),
+ profile_sk_perm(profile, sk_req, sock->sk, &laddr.maddr,
+ &ad));
+
+ if (!error) {
+ struct stored_match_addr raddr;
+
+ /* TODO: have ad here: instead of in CB so we do have to redo */
+ error = map_sock_addr(sock, ADDR_REMOTE, &raddr, NULL);
+ if (!error && raddr.maddr.addrp) {
+ error = label_sk_has_perm1(subj_cred, label, sock->sk,
+ op, request, profile, ad,
+ set_ad_addr(&ad, raddr.addr.sa_family,
+ false, &raddr.maddr),
+ profile_remote_perm(profile, sock->sk,
+ request,
+ &raddr.maddr,
+ &laddr.maddr, &ad));
+ }
+ }
+
+ return error;
+}
diff --git a/security/apparmor/af_unix.c b/security/apparmor/af_unix.c
index b9b22edae202..b908e744818c 100644
--- a/security/apparmor/af_unix.c
+++ b/security/apparmor/af_unix.c
@@ -20,6 +20,7 @@
#include "include/apparmor.h"
#include "include/file.h"
#include "include/label.h"
+#include "include/net.h"
#include "include/path.h"
#include "include/policy.h"
#include "include/cred.h"
@@ -66,7 +67,7 @@ static int unix_fs_perm(const char *op, u32 mask, const struct cred *subj_cred,
#define SHUTDOWN_ADDR "\x03" /* path addr is shutdown and cleared */
#define FS_ADDR "/" /* path addr in fs */
-static aa_state_t match_addr(struct aa_dfa *dfa, aa_state_t state,
+static aa_state_t match_addr(const struct aa_dfa *dfa, aa_state_t state,
struct sockaddr_un *addr, int addrlen)
{
if (addr)
@@ -172,7 +173,7 @@ static aa_state_t match_to_peer(struct aa_policydb *policy, aa_state_t state,
static aa_state_t match_label(struct aa_profile *profile,
struct aa_ruleset *rule, aa_state_t state,
u32 request, struct aa_profile *peer,
- struct aa_perms *p,
+ const struct aa_perms *p,
struct apparmor_audit_data *ad)
{
AA_BUG(!profile);
@@ -208,7 +209,7 @@ static int profile_create_perm(struct aa_profile *profile, int family,
AA_BUG(!profile);
AA_BUG(profile_unconfined(profile));
- state = RULE_MEDIATES_v9NET(rules);
+ state = RULE_MEDIATES_UNIX(rules);
if (state) {
state = aa_match_to_prot(rules->policy, state, AA_MAY_CREATE,
PF_UNIX, type, protocol, NULL,
@@ -234,7 +235,7 @@ static int profile_sk_perm(struct aa_profile *profile,
AA_BUG(!sk);
AA_BUG(profile_unconfined(profile));
- state = RULE_MEDIATES_v9NET(rules);
+ state = RULE_MEDIATES_UNIX(rules);
if (state) {
if (is_unix_fs(sk))
return unix_fs_perm(ad->op, request, ad->subj_cred,
@@ -263,7 +264,7 @@ static int profile_bind_perm(struct aa_profile *profile, struct sock *sk,
AA_BUG(!ad);
AA_BUG(profile_unconfined(profile));
- state = RULE_MEDIATES_v9NET(rules);
+ state = RULE_MEDIATES_UNIX(rules);
if (state) {
if (is_unix_addr_fs(ad->net.addr, ad->net.addrlen))
/* under v7-9 fs hook handles bind */
@@ -294,7 +295,7 @@ static int profile_listen_perm(struct aa_profile *profile, struct sock *sk,
AA_BUG(!ad);
AA_BUG(profile_unconfined(profile));
- state = RULE_MEDIATES_v9NET(rules);
+ state = RULE_MEDIATES_UNIX(rules);
if (state) {
__be16 b = cpu_to_be16(backlog);
@@ -331,7 +332,7 @@ static int profile_accept_perm(struct aa_profile *profile,
AA_BUG(!ad);
AA_BUG(profile_unconfined(profile));
- state = RULE_MEDIATES_v9NET(rules);
+ state = RULE_MEDIATES_UNIX(rules);
if (state) {
if (is_unix_fs(sk))
return unix_fs_perm(ad->op, AA_MAY_ACCEPT,
@@ -361,7 +362,7 @@ static int profile_opt_perm(struct aa_profile *profile, u32 request,
AA_BUG(!ad);
AA_BUG(profile_unconfined(profile));
- state = RULE_MEDIATES_v9NET(rules);
+ state = RULE_MEDIATES_UNIX(rules);
if (state) {
__be16 b = cpu_to_be16(optname);
if (is_unix_fs(sk))
@@ -402,7 +403,7 @@ static int profile_peer_perm(struct aa_profile *profile, u32 request,
AA_BUG(!peer_label);
AA_BUG(!ad);
- state = RULE_MEDIATES_v9NET(rules);
+ state = RULE_MEDIATES_UNIX(rules);
if (state) {
struct aa_profile *peerp;
@@ -462,13 +463,14 @@ static int aa_unix_label_sk_perm(const struct cred *subj_cred,
int aa_unix_sock_perm(const char *op, u32 request, struct socket *sock)
{
struct aa_label *label;
+ bool needput;
int error;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
error = aa_unix_label_sk_perm(current_cred(), label, op,
request, sock->sk,
is_unix_fs(sock->sk) ? &unix_sk(sock->sk)->path : NULL);
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return error;
}
@@ -489,13 +491,14 @@ int aa_unix_bind_perm(struct socket *sock, struct sockaddr *addr,
{
struct aa_profile *profile;
struct aa_label *label;
+ bool needput;
int error = 0;
error = valid_addr(addr, addrlen);
if (error)
return error;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
/* fs bind is handled by mknod */
if (!unconfined(label)) {
DEFINE_AUDIT_SK(ad, OP_BIND, current_cred(), sock->sk);
@@ -506,7 +509,7 @@ int aa_unix_bind_perm(struct socket *sock, struct sockaddr *addr,
error = fn_for_each_confined(label, profile,
profile_bind_perm(profile, sock->sk, &ad));
}
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return error;
}
@@ -528,9 +531,10 @@ int aa_unix_listen_perm(struct socket *sock, int backlog)
{
struct aa_profile *profile;
struct aa_label *label;
+ bool needput;
int error = 0;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
if (!unconfined(label)) {
DEFINE_AUDIT_SK(ad, OP_LISTEN, current_cred(), sock->sk);
@@ -538,7 +542,7 @@ int aa_unix_listen_perm(struct socket *sock, int backlog)
profile_listen_perm(profile, sock->sk,
backlog, &ad));
}
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return error;
}
@@ -549,16 +553,17 @@ int aa_unix_accept_perm(struct socket *sock, struct socket *newsock)
{
struct aa_profile *profile;
struct aa_label *label;
+ bool needput;
int error = 0;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
if (!unconfined(label)) {
DEFINE_AUDIT_SK(ad, OP_ACCEPT, current_cred(), sock->sk);
error = fn_for_each_confined(label, profile,
profile_accept_perm(profile, sock->sk, &ad));
}
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return error;
}
@@ -582,9 +587,10 @@ int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock,
{
struct aa_profile *profile;
struct aa_label *label;
+ bool needput;
int error = 0;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
if (!unconfined(label)) {
DEFINE_AUDIT_SK(ad, op, current_cred(), sock->sk);
@@ -592,7 +598,7 @@ int aa_unix_opt_perm(const char *op, u32 request, struct socket *sock,
profile_opt_perm(profile, request, sock->sk,
optname, &ad));
}
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return error;
}
diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 56155d7d5b2f..5b42140e12e8 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -23,6 +23,7 @@
#include <linux/fs_context.h>
#include <linux/poll.h>
#include <linux/zstd.h>
+#include <linux/string.h>
#include <uapi/linux/major.h>
#include <uapi/linux/magic.h>
@@ -33,6 +34,7 @@
#include "include/crypto.h"
#include "include/ipc.h"
#include "include/label.h"
+#include "include/net.h"
#include "include/lib.h"
#include "include/policy.h"
#include "include/policy_ns.h"
@@ -482,6 +484,126 @@ static struct aa_loaddata *aa_simple_write_to_buffer(const char __user *userbuf,
return data;
}
+#if defined(CONFIG_SECURITY_APPARMOR_COMPRESSED_POLICY) || \
+ defined(CONFIG_SECURITY_APPARMOR_EXPORT_BINARY)
+static int decompress_zstd(char *src, size_t slen, char *dst, size_t dlen)
+{
+ if (slen < dlen) {
+ const size_t wksp_len = zstd_dctx_workspace_bound();
+ zstd_dctx *ctx;
+ void *wksp;
+ size_t out_len;
+ int ret = 0;
+
+ wksp = kvzalloc(wksp_len, GFP_KERNEL);
+ if (!wksp) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+ ctx = zstd_init_dctx(wksp, wksp_len);
+ if (ctx == NULL) {
+ ret = -ENOMEM;
+ goto cleanup;
+ }
+ out_len = zstd_decompress_dctx(ctx, dst, dlen, src, slen);
+ if (zstd_is_error(out_len)) {
+ ret = -EINVAL;
+ goto cleanup;
+ }
+cleanup:
+ kvfree(wksp);
+ return ret;
+ }
+
+ if (dlen < slen)
+ return -EINVAL;
+ memcpy(dst, src, slen);
+ return 0;
+}
+#endif
+
+
+#ifdef CONFIG_SECURITY_APPARMOR_COMPRESSED_POLICY
+/**
+ * aa_get_data_from_compressed - common routine for getting compressed policy
+ * from user and get both compressed and uncompressed version.
+ * @userbuf: user buffer to copy data from (NOT NULL)
+ * @buffer_size: size of user buffer
+ * @pos: position write is at in the file (NOT NULL)
+ * @compressed_data: Ptr on compressed data. *compressed_data is allocated there
+ *
+ * Returns: kernel buffer containing copy of user buffer data or an
+ * ERR_PTR on failure.
+ */
+static struct aa_loaddata *aa_get_data_from_compressed(const char __user *userbuf,
+ size_t buffer_size,
+ loff_t *pos,
+ char **compressed_data)
+{
+ struct aa_loaddata *data;
+ zstd_frame_header header;
+ int error;
+
+ if (!userbuf || !pos)
+ return ERR_PTR(-EINVAL);
+ if (*pos)
+ return ERR_PTR(-ESPIPE);
+
+ *compressed_data = kvmalloc(buffer_size, GFP_KERNEL);
+ if (!*compressed_data)
+ return ERR_PTR(-ENOMEM);
+ if (copy_from_user(*compressed_data, userbuf, buffer_size)) {
+ error = -EFAULT;
+ goto fail;
+ }
+ error = zstd_get_frame_header(&header, *compressed_data, buffer_size);
+ if (error || header.frameContentSize == ZSTD_CONTENTSIZE_UNKNOWN ||
+ header.frameContentSize == ZSTD_CONTENTSIZE_ERROR) {
+ error = -EINVAL;
+ goto fail;
+ }
+
+ data = aa_loaddata_alloc(header.frameContentSize);
+ if (IS_ERR(data)) {
+ error = PTR_ERR(data);
+ goto fail;
+ }
+
+ // We then decompress the data
+ error = decompress_zstd(*compressed_data, buffer_size, data->data,
+ header.frameContentSize);
+ if (error)
+ goto fail_decompress;
+
+ data->size = header.frameContentSize;
+ return data;
+
+fail_decompress:
+ aa_put_i_loaddata(data);
+fail:
+ kvfree(*compressed_data);
+ return ERR_PTR(error);
+
+}
+#else
+static struct aa_loaddata *aa_get_data_from_compressed(const char __user *userbuf __always_unused,
+ size_t buffer_size __always_unused,
+ loff_t *pos __always_unused,
+ char **compressed_data __always_unused)
+{
+ return ERR_PTR(-EINVAL);
+}
+#endif /* CONFIG_SECURITY_APPARMOR_COMPRESSED_POLICY */
+
+struct aa_user_hdr {
+ uint8_t version;
+ uint8_t compress_level;
+ uint8_t padding[6]; /* force 8-byte alignment */
+} __packed __aligned(8);
+
+#define aa_hdr_magic "\x04\x08\x00\x76\x65\x72\x73\x69\x6f\x6e\x00\x02"
+#define aa_hdr_magic_size 12
+
static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
loff_t *pos, struct aa_ns *ns,
const struct cred *ocred)
@@ -489,8 +611,13 @@ static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
struct aa_loaddata *data;
struct aa_label *label;
ssize_t error;
+ char *compressed_data = NULL;
+ __le32 magic_le;
+ bool is_compressed;
+ u8 aahdr[aa_hdr_magic_size];
+ bool needput;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
/* high level check about policy management - fine grained in
* below after unpack
@@ -499,17 +626,59 @@ static ssize_t policy_update(u32 mask, const char __user *buf, size_t size,
if (error)
goto end_section;
- data = aa_simple_write_to_buffer(buf, size, size, pos);
- error = PTR_ERR(data);
+ /* If the policy is userspace compressed we start by decompressing it
+ * to make the required checks (computing hash, verifying profile, ...)
+ *
+ * Getting a userspace-compressed version then decompressing it in the
+ * kernel actually makes sense since zstd decompression is ~3.5x faster
+ * than compression. This also allow to increase the compression level.
+ */
+
+ if (size >= sizeof(__le32) &&
+ !copy_from_user(&magic_le, buf, sizeof(magic_le)) &&
+ le32_to_cpu(magic_le) == ZSTD_MAGICNUMBER) {
+ is_compressed = true;
+ } else if (size >= sizeof(struct aa_user_hdr) + sizeof(__le32) &&
+ !copy_from_user(&magic_le,
+ buf + sizeof(struct aa_user_hdr),
+ sizeof(magic_le)) &&
+ le32_to_cpu(magic_le) == ZSTD_MAGICNUMBER) {
+ is_compressed = true;
+ /* skip the userspace header if present */
+ buf += sizeof(struct aa_user_hdr);
+ size -= sizeof(struct aa_user_hdr);
+ } else if (size >= sizeof(struct aa_user_hdr) +
+ aa_hdr_magic_size &&
+ !copy_from_user(&aahdr,
+ buf + sizeof(struct aa_user_hdr),
+ aa_hdr_magic_size) &&
+ memcmp(&aahdr, aa_hdr_magic, aa_hdr_magic_size) == 0) {
+ /* uncompressed blob with user hdr */
+ buf += sizeof(struct aa_user_hdr);
+ size -= sizeof(struct aa_user_hdr);
+ is_compressed = false;
+ } else {
+ is_compressed = false;
+ }
+ if (is_compressed) {
+
+ data = aa_get_data_from_compressed(buf, size, pos, &compressed_data);
+ error = PTR_ERR(data);
+ } else {
+ data = aa_simple_write_to_buffer(buf, size, size, pos);
+ error = PTR_ERR(data);
+ }
+
if (!IS_ERR(data)) {
- error = aa_replace_profiles(ns, label, mask, data);
+ error = aa_replace_profiles(ns, label, mask, data,
+ compressed_data, size);
/* put pcount, which will put count and free if no
* profiles referencing it.
*/
aa_put_profile_loaddata(data);
}
end_section:
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return error;
}
@@ -549,6 +718,7 @@ static const struct file_operations aa_fs_profile_replace = {
.llseek = default_llseek,
};
+
/* .remove file hook fn to remove loaded policy */
static ssize_t profile_remove(struct file *f, const char __user *buf,
size_t size, loff_t *pos)
@@ -557,8 +727,9 @@ static ssize_t profile_remove(struct file *f, const char __user *buf,
struct aa_label *label;
ssize_t error;
struct aa_ns *ns = get_ns_common_ref(f->f_inode->i_private);
+ bool needput;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
/* high level check about policy management - fine grained in
* below after unpack
*/
@@ -580,7 +751,7 @@ static ssize_t profile_remove(struct file *f, const char __user *buf,
aa_put_profile_loaddata(data);
}
out:
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
aa_put_ns(ns);
return error;
}
@@ -685,7 +856,8 @@ static const struct file_operations aa_fs_ns_revision_fops = {
.release = ns_revision_release,
};
-static void profile_query_cb(struct aa_profile *profile, struct aa_perms *perms,
+static void profile_query_cb(const struct aa_profile *profile,
+ struct aa_perms *perms,
const char *match_str, size_t match_len)
{
struct aa_ruleset *rules = profile->label.rules[0];
@@ -755,6 +927,7 @@ static ssize_t query_data(char *buf, size_t buf_len,
struct aa_data *data;
u32 bytes, blocks;
__le32 outle32;
+ bool needput;
if (!query_len)
return -EINVAL; /* need a query */
@@ -768,9 +941,9 @@ static ssize_t query_data(char *buf, size_t buf_len,
if (buf_len < sizeof(bytes) + sizeof(blocks))
return -EINVAL; /* not enough space */
- curr = begin_current_label_crit_section();
+ curr = begin_current_label_crit_section(&needput);
label = aa_label_parse(curr, query, GFP_KERNEL, false, false);
- end_current_label_crit_section(curr);
+ end_current_label_crit_section(curr, needput);
if (IS_ERR(label))
return PTR_ERR(label);
@@ -846,6 +1019,7 @@ static ssize_t query_label(char *buf, size_t buf_len,
size_t label_name_len, match_len;
struct aa_perms perms;
struct label_it i;
+ bool needput;
if (!query_len)
return -EINVAL;
@@ -864,9 +1038,9 @@ static ssize_t query_label(char *buf, size_t buf_len,
match_str = label_name + label_name_len + 1;
match_len = query_len - label_name_len - 1;
- curr = begin_current_label_crit_section();
+ curr = begin_current_label_crit_section(&needput);
label = aa_label_parse(curr, label_name, GFP_KERNEL, false, false);
- end_current_label_crit_section(curr);
+ end_current_label_crit_section(curr, needput);
if (IS_ERR(label))
return PTR_ERR(label);
@@ -1234,10 +1408,11 @@ static const struct file_operations seq_ns_ ##NAME ##_fops = { \
static int seq_ns_stacked_show(struct seq_file *seq, void *v)
{
struct aa_label *label;
+ bool needput;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
seq_printf(seq, "%s\n", str_yes_no(label->size > 1));
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return 0;
}
@@ -1248,8 +1423,9 @@ static int seq_ns_nsstacked_show(struct seq_file *seq, void *v)
struct aa_profile *profile;
struct label_it it;
int count = 1;
+ bool needput;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
if (label->size > 1) {
label_for_each(it, label, profile)
@@ -1260,7 +1436,7 @@ static int seq_ns_nsstacked_show(struct seq_file *seq, void *v)
}
seq_printf(seq, "%s\n", str_yes_no(count > 1));
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return 0;
}
@@ -1268,23 +1444,32 @@ static int seq_ns_nsstacked_show(struct seq_file *seq, void *v)
static int seq_ns_level_show(struct seq_file *seq, void *v)
{
struct aa_label *label;
+ bool needput;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
seq_printf(seq, "%d\n", labels_ns(label)->level);
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return 0;
}
static int seq_ns_name_show(struct seq_file *seq, void *v)
{
- struct aa_label *label = begin_current_label_crit_section();
+ bool needput;
+ struct aa_label *label = begin_current_label_crit_section(&needput);
+
seq_printf(seq, "%s\n", labels_ns(label)->base.name);
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return 0;
}
+SEQ_NS_FOPS(stacked);
+SEQ_NS_FOPS(nsstacked);
+SEQ_NS_FOPS(level);
+SEQ_NS_FOPS(name);
+
+#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
static int seq_ns_compress_min_show(struct seq_file *seq, void *v)
{
seq_printf(seq, "%d\n", AA_MIN_CLEVEL);
@@ -1297,16 +1482,11 @@ static int seq_ns_compress_max_show(struct seq_file *seq, void *v)
return 0;
}
-SEQ_NS_FOPS(stacked);
-SEQ_NS_FOPS(nsstacked);
-SEQ_NS_FOPS(level);
-SEQ_NS_FOPS(name);
SEQ_NS_FOPS(compress_min);
SEQ_NS_FOPS(compress_max);
/* policy/raw_data/ * file ops */
-#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
#define SEQ_RAWDATA_FOPS(NAME) \
static int seq_rawdata_ ##NAME ##_open(struct inode *inode, struct file *file)\
{ \
@@ -1396,41 +1576,6 @@ SEQ_RAWDATA_FOPS(revision);
SEQ_RAWDATA_FOPS(hash);
SEQ_RAWDATA_FOPS(compressed_size);
-static int decompress_zstd(char *src, size_t slen, char *dst, size_t dlen)
-{
- if (slen < dlen) {
- const size_t wksp_len = zstd_dctx_workspace_bound();
- zstd_dctx *ctx;
- void *wksp;
- size_t out_len;
- int ret = 0;
-
- wksp = kvzalloc(wksp_len, GFP_KERNEL);
- if (!wksp) {
- ret = -ENOMEM;
- goto cleanup;
- }
- ctx = zstd_init_dctx(wksp, wksp_len);
- if (ctx == NULL) {
- ret = -ENOMEM;
- goto cleanup;
- }
- out_len = zstd_decompress_dctx(ctx, dst, dlen, src, slen);
- if (zstd_is_error(out_len)) {
- ret = -EINVAL;
- goto cleanup;
- }
-cleanup:
- kvfree(wksp);
- return ret;
- }
-
- if (dlen < slen)
- return -EINVAL;
- memcpy(dst, src, slen);
- return 0;
-}
-
static ssize_t rawdata_read(struct file *file, char __user *buf, size_t size,
loff_t *ppos)
{
@@ -1935,11 +2080,12 @@ static struct dentry *ns_mkdir_op(struct mnt_idmap *idmap, struct inode *dir,
/* TODO: improve permission check */
struct aa_label *label;
int error;
+ bool needput;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
error = aa_may_manage_policy(current_cred(), label, NULL, NULL,
AA_MAY_LOAD_POLICY);
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
if (error)
return ERR_PTR(error);
@@ -1984,12 +2130,13 @@ static int ns_rmdir_op(struct inode *dir, struct dentry *dentry)
struct aa_ns *ns, *parent;
/* TODO: improve permission check */
struct aa_label *label;
+ bool needput;
int error;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
error = aa_may_manage_policy(current_cred(), label, NULL, NULL,
AA_MAY_LOAD_POLICY);
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
if (error)
return error;
@@ -2483,6 +2630,10 @@ static struct aa_sfs_entry aa_sfs_entry_policy[] = {
AA_SFS_FILE_STRING("permstable32", PERMS32STR),
AA_SFS_FILE_U64("state32", 1),
AA_SFS_DIR("unconfined_restrictions", aa_sfs_entry_unconfined),
+#ifdef CONFIG_SECURITY_APPARMOR_COMPRESSED_POLICY
+ AA_SFS_FILE_BOOLEAN("compressed_load", 1),
+ AA_SFS_FILE_BOOLEAN("extended_policy_header", 1),
+#endif
{ }
};
@@ -2547,8 +2698,10 @@ static struct aa_sfs_entry aa_sfs_entry_apparmor[] = {
AA_SFS_FILE_FOPS(".ns_level", 0444, &seq_ns_level_fops),
AA_SFS_FILE_FOPS(".ns_name", 0444, &seq_ns_name_fops),
AA_SFS_FILE_FOPS("profiles", 0444, &aa_sfs_profiles_fops),
+#ifdef CONFIG_SECURITY_APPARMOR_EXPORT_BINARY
AA_SFS_FILE_FOPS("raw_data_compression_level_min", 0444, &seq_ns_compress_min_fops),
AA_SFS_FILE_FOPS("raw_data_compression_level_max", 0444, &seq_ns_compress_max_fops),
+#endif
AA_SFS_DIR("features", aa_sfs_entry_features),
{ }
};
diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c
index 4a60b6fda75f..5752bb93aaaa 100644
--- a/security/apparmor/audit.c
+++ b/security/apparmor/audit.c
@@ -20,6 +20,7 @@
const char *const audit_mode_names[] = {
"normal",
"quiet_denied",
+ "quiet.allowed",
"quiet",
"noquiet",
"all"
@@ -52,7 +53,7 @@ static const char *const aa_class_names[] = {
"unknown",
"unknown",
"net",
- "unknown",
+ "netv9",
"label",
"posix_mqueue",
"io_uring",
@@ -67,7 +68,7 @@ static const char *const aa_class_names[] = {
"unknown",
"unknown",
"unknown",
- "unknown",
+ "netv9_packet",
"X",
"dbus",
};
@@ -139,6 +140,17 @@ static void audit_pre(struct audit_buffer *ab, void *va)
}
}
+int aa_select_audit_type(u32 denied, const struct aa_perms *perms)
+{
+ if (likely(!denied))
+ return AUDIT_APPARMOR_AUDIT;
+ else if (denied & perms->kill)
+ return AUDIT_APPARMOR_KILL;
+ else if (denied == (denied & perms->complain))
+ return AUDIT_APPARMOR_ALLOWED;
+ return AUDIT_APPARMOR_DENIED;
+}
+
/**
* aa_audit_msg - Log a message to the audit subsystem
* @type: audit type for the message
@@ -152,6 +164,28 @@ void aa_audit_msg(int type, struct apparmor_audit_data *ad,
common_lsm_audit(&ad->common, audit_pre, cb);
}
+int aa_audit_perm_error(struct aa_label *label, u32 request, int error,
+ struct apparmor_audit_data *ad,
+ void (*cb)(struct audit_buffer *, void *))
+{
+ int type = aa_select_audit_type(request, &nullperms);
+
+ if (ad) {
+ struct aa_profile *profile;
+ struct label_it i;
+
+ ad->request = request;
+ ad->denied = request;
+ ad->error = error;
+ label_for_each_confined(i, label, profile) {
+ ad->subj_label = &profile->label;
+ aa_audit_msg(type, ad, cb);
+ }
+ }
+
+ return error;
+}
+
/**
* aa_audit - Log a profile based audit event to the audit subsystem
* @type: audit type for the message
@@ -192,7 +226,7 @@ int aa_audit(int type, struct aa_profile *profile,
aa_audit_msg(type, ad, cb);
if (ad->type == AUDIT_APPARMOR_KILL)
- (void)send_sig_info(profile->signal, NULL,
+ send_sig_info(profile->signal, SEND_SIG_NOINFO,
ad->common.type == LSM_AUDIT_DATA_TASK &&
ad->common.u.tsk ? ad->common.u.tsk : current);
diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c
index b9ea6bc45c1a..2f3c6c84358d 100644
--- a/security/apparmor/capability.c
+++ b/security/apparmor/capability.c
@@ -191,7 +191,7 @@ int aa_capable(const struct cred *subj_cred, struct aa_label *label,
return error;
}
-kernel_cap_t aa_profile_capget(struct aa_profile *profile)
+kernel_cap_t aa_profile_capget(const struct aa_profile *profile)
{
struct aa_ruleset *rules = profile->label.rules[0];
aa_state_t state;
diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c
index d6958eb00e30..af9e8431e3ea 100644
--- a/security/apparmor/domain.c
+++ b/security/apparmor/domain.c
@@ -24,6 +24,7 @@
#include "include/domain.h"
#include "include/file.h"
#include "include/ipc.h"
+#include "include/lib.h"
#include "include/match.h"
#include "include/path.h"
#include "include/policy.h"
@@ -90,8 +91,8 @@ out:
* If a subns profile is not to be matched should be prescreened with
* visibility test.
*/
-static inline aa_state_t match_component(struct aa_profile *profile,
- struct aa_profile *tp,
+static inline aa_state_t match_component(const struct aa_profile *profile,
+ const struct aa_profile *tp,
bool stack, aa_state_t state)
{
struct aa_ruleset *rules = profile->label.rules[0];
@@ -126,7 +127,7 @@ static inline aa_state_t match_component(struct aa_profile *profile,
* @perms should be preinitialized with allperms OR a previous permission
* check to be stacked.
*/
-static int label_compound_match(struct aa_profile *profile,
+static int label_compound_match(const struct aa_profile *profile,
struct aa_label *label, bool stack,
aa_state_t state, bool inview, u32 request,
struct aa_perms *perms)
@@ -188,7 +189,7 @@ fail:
* @perms should be preinitialized with allperms OR a previous permission
* check to be stacked.
*/
-static int label_components_match(struct aa_profile *profile,
+static int label_components_match(const struct aa_profile *profile,
struct aa_label *label, bool stack,
aa_state_t start, bool inview, u32 request,
struct aa_perms *perms)
@@ -252,7 +253,7 @@ fail:
*
* Returns: the state the match finished in, may be the none matching state
*/
-static int label_match(struct aa_profile *profile, struct aa_label *label,
+static int label_match(const struct aa_profile *profile, struct aa_label *label,
bool stack, aa_state_t state, bool inview, u32 request,
struct aa_perms *perms)
{
@@ -286,7 +287,7 @@ static int label_match(struct aa_profile *profile, struct aa_label *label,
* currently only matches full label A//&B//&C or individual components A, B, C
* not arbitrary combinations. Eg. A//&B, C
*/
-static int change_profile_perms(struct aa_profile *profile,
+static int change_profile_perms(const struct aa_profile *profile,
struct aa_label *target, bool stack,
u32 request, aa_state_t start,
struct aa_perms *perms)
@@ -303,28 +304,31 @@ static int change_profile_perms(struct aa_profile *profile,
/**
* aa_xattrs_match - check whether a file matches the xattrs defined in profile
- * @bprm: binprm struct for the process to validate
+ * @path: path for file being matched (NOT NULL)
* @profile: profile to match against (NOT NULL)
* @state: state to start match in
*
* Returns: number of extended attributes that matched, or < 0 on error
*/
-static int aa_xattrs_match(const struct linux_binprm *bprm,
- struct aa_profile *profile, aa_state_t state)
+static int aa_xattrs_match(const struct path *path,
+ const struct aa_profile *profile, aa_state_t state)
{
+ AA_BUG(!path);
+ AA_BUG(!profile);
+
int i;
struct dentry *d;
char *value = NULL;
- struct aa_attachment *attach = &profile->attach;
+ const struct aa_attachment *attach = &profile->attach;
int size, value_size = 0, ret = attach->xattr_count;
- if (!bprm || !attach->xattr_count)
+ if (!attach->xattr_count)
return 0;
might_sleep();
/* transition from exec match to xattr set */
state = aa_dfa_outofband_transition(attach->xmatch->dfa, state);
- d = bprm->file->f_path.dentry;
+ d = path->dentry;
for (i = 0; i < attach->xattr_count; i++) {
size = vfs_getxattr_alloc(&nop_mnt_idmap, d, attach->xattrs[i],
@@ -372,7 +376,7 @@ out:
/**
* find_attach - do attachment search for unconfined processes
- * @bprm: binprm structure of transitioning task
+ * @path: path of file in question (NOT NULL)
* @ns: the current namespace (NOT NULL)
* @head: profile list to walk (NOT NULL)
* @name: to match against (NOT NULL)
@@ -387,7 +391,7 @@ out:
*
* Returns: label or NULL if no match found
*/
-static struct aa_label *find_attach(const struct linux_binprm *bprm,
+static struct aa_label *find_attach(const struct path *path,
struct aa_ns *ns, struct list_head *head,
const char *name, const char **info)
{
@@ -395,6 +399,7 @@ static struct aa_label *find_attach(const struct linux_binprm *bprm,
bool conflict = false;
struct aa_profile *profile, *candidate = NULL;
+ AA_BUG(!path);
AA_BUG(!name);
AA_BUG(!head);
@@ -434,13 +439,13 @@ restart:
if (count < candidate_len)
continue;
- if (bprm && attach->xattr_count) {
+ if (attach->xattr_count) {
long rev = READ_ONCE(ns->revision);
if (!aa_get_profile_not0(profile))
goto restart;
rcu_read_unlock();
- ret = aa_xattrs_match(bprm, profile,
+ ret = aa_xattrs_match(path, profile,
state);
rcu_read_lock();
aa_put_profile(profile);
@@ -556,7 +561,7 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
/**
* x_to_label - get target label for a given xindex
* @profile: current profile (NOT NULL)
- * @bprm: binprm structure of transitioning task
+ * @path: path of file in question
* @name: name to lookup (NOT NULL)
* @xindex: index into x transition table
* @lookupname: returns: name used in lookup if one was specified (NOT NULL)
@@ -567,7 +572,7 @@ struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
* Returns: refcounted label or NULL if not found available
*/
static struct aa_label *x_to_label(struct aa_profile *profile,
- const struct linux_binprm *bprm,
+ const struct path *path,
const char *name, u32 xindex,
const char **lookupname,
const char **info)
@@ -598,11 +603,11 @@ static struct aa_label *x_to_label(struct aa_profile *profile,
case AA_X_NAME:
if (xindex & AA_X_CHILD)
/* released by caller */
- new = find_attach(bprm, ns, &profile->base.profiles,
+ new = find_attach(path, ns, &profile->base.profiles,
name, info);
else
/* released by caller */
- new = find_attach(bprm, ns, &ns->base.profiles,
+ new = find_attach(path, ns, &ns->base.profiles,
name, info);
*lookupname = name;
break;
@@ -690,7 +695,7 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
}
if (profile_unconfined(profile)) {
- new = find_attach(bprm, profile->ns,
+ new = find_attach(&bprm->file->f_path, profile->ns,
&profile->ns->base.profiles, name, &info);
/* info set -> something unusual that we should report
* Currently this is only conflicting attachments, but other
@@ -720,8 +725,8 @@ static struct aa_label *profile_transition(const struct cred *subj_cred,
state = aa_str_perms(rules->file, state, name, cond, &perms);
if (perms.allow & MAY_EXEC) {
/* exec permission determine how to transition */
- new = x_to_label(profile, bprm, name, perms.xindex, &target,
- &info);
+ new = x_to_label(profile, &bprm->file->f_path, name,
+ perms.xindex, &target, &info);
if (new && new->proxy == profile->label.proxy && info) {
/* Force audit on conflicting attachment fallback
* Because perms is never used again after this audit
@@ -874,6 +879,52 @@ static struct aa_label *label_merge_wrap(struct aa_label *a, struct aa_label *b,
return label;
}
+static bool is_profile_priv_restricted_to_stack(const struct cred *subj_cred,
+ struct aa_profile *profile)
+{
+ if (profile_unconfined(profile) && profile == profile->ns->unconfined &&
+ aa_unprivileged_unconfined_restricted &&
+ /* cap_capable returns false (0) if true, hence true here means
+ * doesn't have capability and the stack will be restricted
+ */
+ cap_capable(current_cred(), &init_user_ns, CAP_MAC_OVERRIDE,
+ CAP_OPT_NOAUDIT))
+ return true;
+ return false;
+}
+
+static const char *stack_msg = "change_profile unprivileged unconfined converted to stacking";
+
+static struct aa_label *priv_restricted_transition(const struct cred *subj_cred,
+ struct aa_profile *profile,
+ const char *op, u32 request,
+ const char *name,
+ struct aa_label *transition,
+ gfp_t gfp)
+{
+ if (!is_profile_priv_restricted_to_stack(subj_cred, profile))
+ return aa_get_newest_label(transition);
+
+ /* transition allowed but only via stack */
+ struct aa_label *target = label_merge_wrap(&profile->label,
+ transition, gfp);
+ if (IS_ERR_OR_NULL(target))
+ return target;
+
+ /* doing this here is less than optimal but good enough until the
+ * fs mediation rework lands
+ */
+ struct aa_perms perms = {
+ .allow = request,
+ .audit = request,
+ };
+ aa_audit_file(subj_cred, profile, &perms, op,
+ request, name, NULL, target,
+ subj_cred->euid, stack_msg, 0);
+
+ return target;
+}
+
static struct aa_label *handle_onexec(const struct cred *subj_cred,
struct aa_label *label,
struct aa_label *onexec, bool stack,
@@ -902,7 +953,10 @@ static struct aa_label *handle_onexec(const struct cred *subj_cred,
new = fn_label_build_in_scope(label, profile, GFP_KERNEL,
stack ? label_merge_wrap(&profile->label, onexec,
GFP_KERNEL)
- : aa_get_newest_label(onexec),
+ : priv_restricted_transition(subj_cred, profile,
+ OP_CHANGE_ONEXEC, AA_MAY_ONEXEC,
+ bprm->filename, onexec,
+ GFP_KERNEL),
profile_transition(subj_cred, profile, bprm,
buffer, cond, unsafe));
AA_BUG(!new);
@@ -1084,8 +1138,8 @@ static struct aa_label *build_change_hat(const struct cred *subj_cred,
if (!hat) {
error = -ENOENT;
if (COMPLAIN_MODE(profile)) {
- hat = aa_new_learning_profile(profile, true, name,
- GFP_KERNEL);
+ hat = __aa_new_learning_profile(profile, true, name,
+ GFP_KERNEL);
if (!hat) {
info = "failed null profile create";
error = -ENOMEM;
@@ -1123,6 +1177,7 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
bool sibling = false;
const char *name, *info = NULL;
int i, error;
+ bool needput = false;
AA_BUG(!label);
AA_BUG(!hats);
@@ -1135,7 +1190,6 @@ static struct aa_label *change_hat(const struct cred *subj_cred,
* the profiles and label, we can rely on the namespaces being live
* and avoid incrementing their refcounts while grabbing the lock.
*/
- label = aa_get_label(label);
ns = labels_ns(label);
retry:
@@ -1143,15 +1197,19 @@ retry:
if (label_is_stale(label)) {
new = aa_get_newest_label(label);
new_ns = labels_ns(new);
+
+ if (needput)
+ /* aa_put_label() is safe to call when under lock */
+ aa_put_label(label);
+ label = new;
+ needput = true;
+ /* check if replaced with label in parent ns, and lock there */
if (new_ns != ns) {
- aa_put_label(new);
mutex_unlock(&ns->lock);
ns = new_ns;
- label = new;
+ /* retry will bottom out at the root of the tree */
goto retry;
}
- aa_put_label(label);
- label = new;
}
if (PROFILE_IS_HAT(labels_profile(label)))
@@ -1162,7 +1220,8 @@ retry:
name = hats[i];
label_for_each_in_scope(it, labels_ns(label), label, profile) {
if (sibling && PROFILE_IS_HAT(profile)) {
- root = aa_get_profile(profile->parent);
+ root = aa_get_profile(rcu_dereference_protected(profile->parent,
+ mutex_is_locked(&ns->lock)));
} else if (!sibling && !PROFILE_IS_HAT(profile)) {
root = aa_get_profile(profile);
} else { /* conflicting change type */
@@ -1223,6 +1282,8 @@ fail:
}
}
mutex_unlock(&ns->lock);
+ if (needput)
+ aa_put_label(label);
return ERR_PTR(error);
build:
@@ -1233,7 +1294,8 @@ build:
mutex_unlock(&ns->lock);
AA_BUG(!new);
/* return new label or error ptr */
-
+ if (needput)
+ aa_put_label(label);
return new;
}
@@ -1406,8 +1468,6 @@ static int change_profile_perms_wrapper(const char *op, const char *name,
return error;
}
-static const char *stack_msg = "change_profile unprivileged unconfined converted to stacking";
-
/**
* aa_change_profile - perform a one-way profile transition
* @fqname: name of profile may include namespace (NOT NULL)
@@ -1467,28 +1527,6 @@ int aa_change_profile(const char *fqname, int flags)
op = OP_CHANGE_PROFILE;
}
- /* This should move to a per profile test. Requires pushing build
- * into callback
- */
- if (!stack && unconfined(label) &&
- label == &labels_ns(label)->unconfined->label &&
- aa_unprivileged_unconfined_restricted &&
- /* TODO: refactor so this check is a fn */
- cap_capable(current_cred(), &init_user_ns, CAP_MAC_OVERRIDE,
- CAP_OPT_NOAUDIT)) {
- /* regardless of the request in this case apparmor
- * stacks against unconfined so admin set policy can't be
- * by-passed
- */
- stack = true;
- perms.audit = request;
- (void) fn_for_each_in_scope(label, profile,
- aa_audit_file(subj_cred, profile, &perms, op,
- request, auditname, NULL, target,
- GLOBAL_ROOT_UID, stack_msg, 0));
- perms.audit = 0;
- }
-
if (*fqname == '&') {
stack = true;
/* don't have label_parse() do stacking */
@@ -1559,7 +1597,10 @@ check:
/* stacking is always a subset, so only check the nonstack case */
if (!stack) {
new = fn_label_build_in_scope(label, profile, GFP_KERNEL,
- aa_get_label(target),
+ priv_restricted_transition(subj_cred, profile,
+ op, request,
+ auditname, target,
+ GFP_KERNEL),
aa_get_label(&profile->label));
AA_BUG(!new);
if (IS_ERR(new))
diff --git a/security/apparmor/file.c b/security/apparmor/file.c
index c9d55fe1086f..3e74b613db32 100644
--- a/security/apparmor/file.c
+++ b/security/apparmor/file.c
@@ -93,11 +93,13 @@ static void file_audit_cb(struct audit_buffer *ab, void *va)
* Returns: %0 or error on failure
*/
int aa_audit_file(const struct cred *subj_cred,
- struct aa_profile *profile, struct aa_perms *perms,
+ struct aa_profile *profile, const struct aa_perms *perms,
const char *op, u32 request, const char *name,
const char *target, struct aa_label *tlabel,
kuid_t ouid, const char *info, int error)
{
+ u32 quiet = perms->quiet;
+ u32 complain = perms->complain;
int type = AUDIT_APPARMOR_AUTO;
DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_TASK, AA_CLASS_FILE, op);
@@ -112,6 +114,8 @@ int aa_audit_file(const struct cred *subj_cred,
ad.error = error;
ad.common.u.tsk = NULL;
+ if (COMPLAIN_MODE(profile))
+ complain |= ~(perms->allow | perms->deny);
if (likely(!ad.error)) {
u32 mask = perms->audit;
@@ -132,11 +136,14 @@ int aa_audit_file(const struct cred *subj_cred,
if (ad.request & perms->kill)
type = AUDIT_APPARMOR_KILL;
+ if (AUDIT_MODE(profile) == AUDIT_QUIET_ALLOWED)
+ quiet |= complain | perms->allow;
+
/* quiet known rejects, assumes quiet and kill do not overlap */
- if ((ad.request & perms->quiet) &&
+ if ((ad.request & quiet) &&
AUDIT_MODE(profile) != AUDIT_NOQUIET &&
AUDIT_MODE(profile) != AUDIT_ALL)
- ad.request &= ~perms->quiet;
+ ad.request &= ~quiet;
if (!ad.request)
return ad.error;
@@ -232,7 +239,7 @@ int __aa_path_perm(const char *op, const struct cred *subj_cred,
int e = 0;
if (profile_unconfined(profile) ||
- ((flags & PATH_SOCK_COND) && !RULE_MEDIATES_v9NET(rules)))
+ ((flags & PATH_SOCK_COND) && !RULE_MEDIATES_UNIX(rules)))
return 0;
aa_str_perms(rules->file, rules->file->start[AA_CLASS_FILE],
name, cond, perms);
diff --git a/security/apparmor/include/af_inet.h b/security/apparmor/include/af_inet.h
new file mode 100644
index 000000000000..601ab44bdfe1
--- /dev/null
+++ b/security/apparmor/include/af_inet.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * AppArmor security module
+ *
+ * This file contains AppArmor inet/inet6 fine grained mediation
+ *
+ * Copyright 2024 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_INET_H
+#define __AA_AF_INET_H
+
+#include "label.h"
+
+int aa_inet_sock_perm(const char *op, u32 request, struct socket *sock);
+int aa_inet_create_perm(struct aa_label *label, int family, int type,
+ int protocol);
+int aa_inet_bind_perm(struct socket *sock, struct sockaddr *address,
+ int addrlen);
+int aa_inet_connect_perm(struct socket *sock, struct sockaddr *address,
+ int addrlen);
+int aa_inet_listen_perm(struct socket *sock, int backlog);
+int aa_inet_accept_perm(struct socket *sock, struct socket *newsock);
+int aa_inet_msg_perm(const char *op, u32 request, struct socket *sock,
+ struct msghdr *msg, int size);
+int aa_inet_opt_perm(const char *op, u32 request, struct socket *sock, int level,
+ int optname);
+int aa_inet_file_perm(const struct cred *subj_cred,
+ struct aa_label *label, const char *op, u32 request,
+ struct socket *sock);
+
+#endif /* __AA_AF_INET_H */
diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h
index cc6e3df1bc62..cb06b2f55d49 100644
--- a/security/apparmor/include/apparmor.h
+++ b/security/apparmor/include/apparmor.h
@@ -36,6 +36,7 @@
#define AA_CLASS_NS 21
#define AA_CLASS_IO_URING 22
+#define AA_CLASS_NETV9_SKB 30
#define AA_CLASS_X 31
#define AA_CLASS_DBUS 32
diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h
index 33243d11fd10..c6c8fcde728f 100644
--- a/security/apparmor/include/apparmorfs.h
+++ b/security/apparmor/include/apparmorfs.h
@@ -11,6 +11,9 @@
#ifndef __AA_APPARMORFS_H
#define __AA_APPARMORFS_H
+#include <linux/init.h>
+#include <linux/types.h>
+
extern struct path aa_null;
enum aa_sfs_type {
diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h
index aa00b34404f9..987ed4441fd3 100644
--- a/security/apparmor/include/audit.h
+++ b/security/apparmor/include/audit.h
@@ -21,13 +21,14 @@
#include "label.h"
extern const char *const audit_mode_names[];
-#define AUDIT_MAX_INDEX 5
enum audit_mode {
AUDIT_NORMAL, /* follow normal auditing of accesses */
AUDIT_QUIET_DENIED, /* quiet all denied access messages */
+ AUDIT_QUIET_ALLOWED, /* quiet all allowed access messages */
AUDIT_QUIET, /* quiet all messages */
AUDIT_NOQUIET, /* do not quiet audit messages */
- AUDIT_ALL /* audit all accesses */
+ AUDIT_ALL, /* audit all accesses */
+ AUDIT_MODE_NAMES_COUNT /* Must be last entry */
};
enum audit_type {
@@ -183,6 +184,8 @@ struct apparmor_audit_data {
.common.apparmor_audit_data = &NAME, \
};
+int aa_select_audit_type(u32 denied, const struct aa_perms *perms);
+
void aa_audit_msg(int type, struct apparmor_audit_data *ad,
void (*cb) (struct audit_buffer *, void *));
int aa_audit(int type, struct aa_profile *profile,
@@ -196,6 +199,9 @@ int aa_audit(int type, struct aa_profile *profile,
(AD)->error; \
})
+int aa_audit_perm_error(struct aa_label *label, u32 request, int error,
+ struct apparmor_audit_data *ad,
+ void (*cb)(struct audit_buffer *, void *));
static inline int complain_error(int error)
{
diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h
index 1ddcec2d1160..89a9c75d8f44 100644
--- a/security/apparmor/include/capability.h
+++ b/security/apparmor/include/capability.h
@@ -11,6 +11,7 @@
#ifndef __AA_CAPABILITY_H
#define __AA_CAPABILITY_H
+#include <linux/capability.h>
#include <linux/sched.h>
#include "apparmorfs.h"
@@ -36,7 +37,7 @@ struct aa_caps {
extern struct aa_sfs_entry aa_sfs_entry_caps[];
-kernel_cap_t aa_profile_capget(struct aa_profile *profile);
+kernel_cap_t aa_profile_capget(const struct aa_profile *profile);
int aa_capable(const struct cred *subj_cred, struct aa_label *label,
int cap, unsigned int opts);
diff --git a/security/apparmor/include/cred.h b/security/apparmor/include/cred.h
index 2b6098149b15..056e031b7b8a 100644
--- a/security/apparmor/include/cred.h
+++ b/security/apparmor/include/cred.h
@@ -177,12 +177,14 @@ static inline void __end_current_label_crit_section(struct aa_label *label,
/**
* end_current_label_crit_section - put a reference found with begin_current_label..
* @label: label reference to put
+ * @needput: output: bool set by __begin_current_label_crit_section
*
* Should only be used with a reference obtained with
* begin_current_label_crit_section and never used in situations where the
* task cred may be updated
*/
-static inline void end_current_label_crit_section(struct aa_label *label)
+static inline void end_current_label_crit_section(struct aa_label *label,
+ bool needput)
{
if (label != aa_current_raw_label())
aa_put_label(label);
@@ -208,28 +210,22 @@ static inline struct aa_label *__begin_current_label_crit_section(bool *needput)
/**
* begin_current_label_crit_section - current's confining label and update it
+ * @needput: store whether the label needs to be put when ending crit section
*
* Returns: up to date confining label or the ns unconfined label (NOT NULL)
*
- * Not safe to call inside locks
- *
* The returned reference must be put with end_current_label_crit_section()
- * This must NOT be used if the task cred could be updated within the
+ * This should NOT be used if the task cred could be updated within the
* critical section between begin_current_label_crit_section() ..
* end_current_label_crit_section()
*/
-static inline struct aa_label *begin_current_label_crit_section(void)
+static inline struct aa_label *begin_current_label_crit_section(bool *needput)
{
struct aa_label *label = aa_current_raw_label();
- might_sleep();
-
- if (label_is_stale(label)) {
- label = aa_get_newest_label(label);
- if (aa_replace_current_label(label) == 0)
- /* task cred will keep the reference */
- aa_put_label(label);
- }
+ label = __begin_current_label_crit_section(needput);
+ if (*needput)
+ aa_schedule_stale_label_replacement();
return label;
}
diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h
index ef60f99bc5ae..1614c07fc53e 100644
--- a/security/apparmor/include/file.h
+++ b/security/apparmor/include/file.h
@@ -72,7 +72,7 @@ struct path_cond {
#define COMBINED_PERM_MASK(X) ((X).allow | (X).audit | (X).quiet | (X).kill)
int aa_audit_file(const struct cred *cred,
- struct aa_profile *profile, struct aa_perms *perms,
+ struct aa_profile *profile, const struct aa_perms *perms,
const char *op, u32 request, const char *name,
const char *target, struct aa_label *tlabel, kuid_t ouid,
const char *info, int error);
diff --git a/security/apparmor/include/label.h b/security/apparmor/include/label.h
index b5a722a47fd2..2c869b7dec97 100644
--- a/security/apparmor/include/label.h
+++ b/security/apparmor/include/label.h
@@ -23,7 +23,7 @@ struct aa_ruleset;
#define LOCAL_VEC_ENTRIES 8
#define DEFINE_VEC(T, V) \
- struct aa_ ## T *(_ ## V ## _localtmp)[LOCAL_VEC_ENTRIES]; \
+ struct aa_ ## T *(_ ## V ## _localtmp)[LOCAL_VEC_ENTRIES + 1]; \
struct aa_ ## T **(V)
#define vec_setup(T, V, N, GFP) \
@@ -31,10 +31,10 @@ struct aa_ruleset;
if ((N) <= LOCAL_VEC_ENTRIES) { \
typeof(N) i; \
(V) = (_ ## V ## _localtmp); \
- for (i = 0; i < (N); i++) \
+ for (i = 0; i <= (N); i++) \
(V)[i] = NULL; \
} else \
- (V) = kzalloc(sizeof(struct aa_ ## T *) * (N), (GFP)); \
+ (V) = kzalloc_objs(struct aa_ ## T *, (N) + 1, (GFP)); \
(V) ? 0 : -ENOMEM; \
})
@@ -165,7 +165,7 @@ do { \
#define labels_profile(X) ((X)->vec[(X)->size - 1])
-int aa_label_next_confined(struct aa_label *l, int i);
+int aa_label_next_confined(const struct aa_label *l, int i);
/* for each profile in a label */
#define label_for_each(I, L, P) \
@@ -246,12 +246,12 @@ int aa_label_next_confined(struct aa_label *l, int i);
#define fn_for_each_not_in_set(L1, L2, P, FN) \
fn_for_each2_XXX((L1), (L2), P, FN, _not_in_set)
-static inline bool label_mediates(struct aa_label *L, unsigned char C)
+static inline bool label_mediates(const struct aa_label *L, unsigned char C)
{
return (L)->mediates & (((u64) 1) << (C));
}
-static inline bool label_mediates_safe(struct aa_label *L, unsigned char C)
+static inline bool label_mediates_safe(const struct aa_label *L, unsigned char C)
{
if (C > AA_CLASS_LAST)
return false;
@@ -268,11 +268,12 @@ void aa_label_kref(struct kref *kref);
bool aa_label_init(struct aa_label *label, int size, gfp_t gfp);
struct aa_label *aa_label_alloc(int size, struct aa_proxy *proxy, gfp_t gfp);
-bool aa_label_is_subset(struct aa_label *set, struct aa_label *sub);
-bool aa_label_is_unconfined_subset(struct aa_label *set, struct aa_label *sub);
+bool aa_label_is_subset(const struct aa_label *set, const struct aa_label *sub);
+bool aa_label_is_unconfined_subset(const struct aa_label *set,
+ const struct aa_label *sub);
struct aa_profile *__aa_label_next_not_in_set(struct label_it *I,
- struct aa_label *set,
- struct aa_label *sub);
+ const struct aa_label *set,
+ const struct aa_label *sub);
bool aa_label_remove(struct aa_label *label);
struct aa_label *aa_label_insert(struct aa_labelset *ls, struct aa_label *l);
bool aa_label_replace(struct aa_label *old, struct aa_label *new);
@@ -280,8 +281,8 @@ bool aa_label_make_newest(struct aa_labelset *ls, struct aa_label *old,
struct aa_label *new);
struct aa_profile *aa_label_next_in_merge(struct label_it *I,
- struct aa_label *a,
- struct aa_label *b);
+ const struct aa_label *a,
+ const struct aa_label *b);
struct aa_label *aa_label_find_merge(struct aa_label *a, struct aa_label *b);
struct aa_label *aa_label_merge(struct aa_label *a, struct aa_label *b,
gfp_t gfp);
@@ -342,7 +343,7 @@ static inline const char *aa_label_str_split(const char *str)
struct aa_perms;
struct aa_ruleset;
-int aa_label_match(struct aa_profile *profile, struct aa_ruleset *rules,
+int aa_label_match(const struct aa_profile *profile, struct aa_ruleset *rules,
struct aa_label *label, aa_state_t state, bool subns,
u32 request, struct aa_perms *perms);
@@ -462,8 +463,8 @@ static inline void aa_put_label(struct aa_label *l)
}
/* wrapper fn to indicate semantics of the check */
-static inline bool __aa_subj_label_is_cached(struct aa_label *subj_label,
- struct aa_label *obj_label)
+static inline bool __aa_subj_label_is_cached(const struct aa_label *subj_label,
+ const struct aa_label *obj_label)
{
return aa_label_is_subset(obj_label, subj_label);
}
diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h
index e3c8cb044a90..475dd71fbe40 100644
--- a/security/apparmor/include/lib.h
+++ b/security/apparmor/include/lib.h
@@ -136,7 +136,7 @@ static inline bool aa_strneq(const char *str, const char *sub, int len)
* character which is not used in standard matching and is only
* used to separate pairs.
*/
-static inline aa_state_t aa_dfa_null_transition(struct aa_dfa *dfa,
+static inline aa_state_t aa_dfa_null_transition(const struct aa_dfa *dfa,
aa_state_t start)
{
/* the null transition only needs the string's null terminator byte */
@@ -338,8 +338,8 @@ __do_cleanup: \
__new_ = (FN); \
} \
__done: \
- if (PTR_ERR(__new_)) \
- AA_DEBUG(DEBUG_LABEL, "label build failed\n"); \
+ if (IS_ERR(__new_)) \
+ AA_DEBUG(DEBUG_LABEL, "label build failed %ld\n", PTR_ERR(__new_)); \
(__new_); \
})
diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h
index 7accb1c39849..f7bd7855f1bd 100644
--- a/security/apparmor/include/match.h
+++ b/security/apparmor/include/match.h
@@ -125,16 +125,18 @@ static inline size_t table_size(size_t len, size_t el_size)
#define aa_state_t unsigned int
-struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags);
-aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start,
+struct aa_dfa *aa_dfa_unpack(const void *blob, size_t size, int flags);
+aa_state_t aa_dfa_match_len(const struct aa_dfa *dfa, aa_state_t start,
const char *str, int len);
-aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start,
+aa_state_t aa_dfa_match(const struct aa_dfa *dfa, aa_state_t start,
const char *str);
-aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c);
-aa_state_t aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t state);
-aa_state_t aa_dfa_match_until(struct aa_dfa *dfa, aa_state_t start,
+aa_state_t aa_dfa_next(const struct aa_dfa *dfa, aa_state_t state,
+ const char c);
+aa_state_t aa_dfa_outofband_transition(const struct aa_dfa *dfa,
+ aa_state_t state);
+aa_state_t aa_dfa_match_until(const struct aa_dfa *dfa, aa_state_t start,
const char *str, const char **retpos);
-aa_state_t aa_dfa_matchn_until(struct aa_dfa *dfa, aa_state_t start,
+aa_state_t aa_dfa_matchn_until(const struct aa_dfa *dfa, aa_state_t start,
const char *str, int n, const char **retpos);
void aa_dfa_free_kref(struct kref *kref);
@@ -152,7 +154,7 @@ struct match_workbuf N = { \
.len = 0, \
}
-aa_state_t aa_dfa_leftmatch(struct aa_dfa *dfa, aa_state_t start,
+aa_state_t aa_dfa_leftmatch(const struct aa_dfa *dfa, aa_state_t start,
const char *str, unsigned int *count);
/**
diff --git a/security/apparmor/include/net.h b/security/apparmor/include/net.h
index 0d0b0ce42723..375341929cb6 100644
--- a/security/apparmor/include/net.h
+++ b/security/apparmor/include/net.h
@@ -86,7 +86,7 @@ extern struct aa_sfs_entry aa_sfs_entry_network[];
extern struct aa_sfs_entry aa_sfs_entry_networkv9[];
int aa_do_perms(struct aa_profile *profile, struct aa_policydb *policy,
- aa_state_t state, u32 request, struct aa_perms *p,
+ aa_state_t state, u32 request, const struct aa_perms *p,
struct apparmor_audit_data *ad);
/* passing in state returned by XXX_mediates_AF() */
aa_state_t aa_match_to_prot(struct aa_policydb *policy, aa_state_t state,
@@ -102,13 +102,14 @@ int aa_af_perm(const struct cred *subj_cred, struct aa_label *label,
static inline int aa_profile_af_sk_perm(struct aa_profile *profile,
struct apparmor_audit_data *ad,
u32 request,
- struct sock *sk)
+ const struct sock *sk)
{
return aa_profile_af_perm(profile, ad, request, sk->sk_family,
sk->sk_type, sk->sk_protocol);
}
-int aa_sk_perm(const char *op, u32 request, struct sock *sk);
-
+int aa_sk_perm(const char *op, u32 request, const struct sock *sk);
+int aa_label_sk_perm(const struct cred *subj_cred, struct aa_label *label,
+ const char *op, u32 request, const struct sock *sk);
int aa_sock_file_perm(const struct cred *subj_cred, struct aa_label *label,
const char *op, u32 request,
struct file *file);
diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h
index 8bb915d48dc7..250812a77e11 100644
--- a/security/apparmor/include/path.h
+++ b/security/apparmor/include/path.h
@@ -11,6 +11,9 @@
#ifndef __AA_PATH_H
#define __AA_PATH_H
+#include <linux/path.h>
+#include <linux/types.h>
+
enum path_flags {
PATH_IS_DIR = 0x1, /* path is a directory */
PATH_SOCK_COND = 0x2,
diff --git a/security/apparmor/include/perms.h b/security/apparmor/include/perms.h
index 37a3781b99a0..ee25eb8e78e4 100644
--- a/security/apparmor/include/perms.h
+++ b/security/apparmor/include/perms.h
@@ -96,8 +96,8 @@ struct aa_perms {
#define AA_INDEX_NONE 0
#define ALL_PERMS_MASK 0xffffffff
-extern struct aa_perms nullperms;
-extern struct aa_perms allperms;
+extern const struct aa_perms nullperms;
+extern const struct aa_perms allperms;
/**
* aa_perms_accum_raw - accumulate perms with out masking off overlapping perms
@@ -105,7 +105,7 @@ extern struct aa_perms allperms;
* @addend: perms struct to add to @accum
*/
static inline void aa_perms_accum_raw(struct aa_perms *accum,
- struct aa_perms *addend)
+ const struct aa_perms *addend)
{
accum->deny |= addend->deny;
accum->allow &= addend->allow & ~addend->deny;
@@ -132,7 +132,7 @@ static inline void aa_perms_accum_raw(struct aa_perms *accum,
* @addend: perms struct to add to @accum
*/
static inline void aa_perms_accum(struct aa_perms *accum,
- struct aa_perms *addend)
+ const struct aa_perms *addend)
{
accum->deny |= addend->deny;
accum->allow &= addend->allow & ~accum->deny;
@@ -206,14 +206,15 @@ void aa_audit_perm_names(struct audit_buffer *ab, const char * const *names,
u32 mask);
void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
u32 chrsmask, const char * const *names, u32 namesmask);
-void aa_apply_modes_to_perms(struct aa_profile *profile,
+void aa_apply_modes_to_perms(const struct aa_profile *profile,
struct aa_perms *perms);
-void aa_perms_accum(struct aa_perms *accum, struct aa_perms *addend);
-void aa_perms_accum_raw(struct aa_perms *accum, struct aa_perms *addend);
-void aa_profile_match_label(struct aa_profile *profile,
+void aa_perms_accum(struct aa_perms *accum, const struct aa_perms *addend);
+void aa_perms_accum_raw(struct aa_perms *accum, const struct aa_perms *addend);
+void aa_profile_match_label(const struct aa_profile *profile,
struct aa_ruleset *rules, struct aa_label *label,
int type, u32 request, struct aa_perms *perms);
-int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
+int aa_check_perms(struct aa_profile *profile, const struct aa_perms *perms,
u32 request, struct apparmor_audit_data *ad,
void (*cb)(struct audit_buffer *, void *));
#endif /* __AA_PERM_H */
+
diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h
index 3895f8774a3f..0dcbf8cf1029 100644
--- a/security/apparmor/include/policy.h
+++ b/security/apparmor/include/policy.h
@@ -26,7 +26,6 @@
#include "file.h"
#include "lib.h"
#include "label.h"
-#include "net.h"
#include "perms.h"
#include "resource.h"
@@ -37,7 +36,6 @@ extern int unprivileged_userns_apparmor_policy;
extern int aa_unprivileged_unconfined_restricted;
extern const char *const aa_profile_mode_names[];
-#define APPARMOR_MODE_NAMES_MAX_INDEX 4
#define PROFILE_MODE(_profile, _mode) \
((aa_g_profile_mode == (_mode)) || \
@@ -76,6 +74,7 @@ enum profile_mode {
APPARMOR_KILL, /* kill task on access violation */
APPARMOR_UNCONFINED, /* profile set to unconfined */
APPARMOR_USER, /* modified complain mode to userspace */
+ PROFILE_MODE_NAMES_COUNT /* Must be last entry */
};
@@ -295,6 +294,9 @@ struct aa_profile *aa_alloc_profile(const char *name, struct aa_proxy *proxy,
gfp_t gfp);
struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name,
gfp_t gfp);
+struct aa_profile *__aa_new_learning_profile(struct aa_profile *parent,
+ bool hat, const char *base,
+ gfp_t gfp);
struct aa_profile *aa_new_learning_profile(struct aa_profile *parent, bool hat,
const char *base, gfp_t gfp);
void aa_free_profile(struct aa_profile *profile);
@@ -305,7 +307,8 @@ struct aa_profile *aa_fqlookupn_profile(struct aa_label *base,
const char *fqname, size_t n);
ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_label *label,
- u32 mask, struct aa_loaddata *udata);
+ u32 mask, struct aa_loaddata *udata,
+ char *compressed_profile, size_t compressed_size);
ssize_t aa_remove_profiles(struct aa_ns *view, struct aa_label *label,
char *name, size_t size);
void __aa_profile_list_release(struct list_head *head);
@@ -351,10 +354,14 @@ static inline aa_state_t RULE_MEDIATES_NET(struct aa_ruleset *rules)
/* fallback and check v7/8 if v9 is NOT mediated */
if (!state)
state = RULE_MEDIATES(rules, AA_CLASS_NET);
-
return state;
}
+static inline aa_state_t RULE_MEDIATES_UNIX(struct aa_ruleset *rules)
+{
+ return RULE_MEDIATES_v9NET(rules);
+}
+
void aa_compute_profile_mediates(struct aa_profile *profile);
static inline bool profile_mediates(struct aa_profile *profile,
@@ -429,7 +436,7 @@ static inline void aa_put_profile(struct aa_profile *p)
kref_put(&p->label.count.count, aa_label_kref);
}
-static inline int AUDIT_MODE(struct aa_profile *profile)
+static inline int AUDIT_MODE(const struct aa_profile *profile)
{
if (aa_g_audit != AUDIT_NORMAL)
return aa_g_audit;
diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h
index 4ea9b6479a3e..c01f6885dbe3 100644
--- a/security/apparmor/include/policy_unpack.h
+++ b/security/apparmor/include/policy_unpack.h
@@ -16,6 +16,7 @@
#include <linux/dcache.h>
#include <linux/workqueue.h>
+#include "lib.h"
struct aa_load_ent {
struct list_head list;
@@ -128,7 +129,8 @@ struct aa_loaddata {
char *data;
};
-int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns);
+int aa_unpack(struct aa_loaddata *udata, struct list_head *lh, const char **ns,
+ char *compressed_data, size_t compressed_size);
/**
* aa_get_i_loaddata - get a reference count from a counted data reference
diff --git a/security/apparmor/include/procattr.h b/security/apparmor/include/procattr.h
index 03dbfdb2f2c0..56acd1bdb634 100644
--- a/security/apparmor/include/procattr.h
+++ b/security/apparmor/include/procattr.h
@@ -11,6 +11,8 @@
#ifndef __AA_PROCATTR_H
#define __AA_PROCATTR_H
+#include "label.h"
+
int aa_getprocattr(struct aa_label *label, char **string, bool newline);
int aa_setprocattr_changehat(char *args, size_t size, int flags);
diff --git a/security/apparmor/include/task.h b/security/apparmor/include/task.h
index b1aaaf60fa8b..a8030ed78ff2 100644
--- a/security/apparmor/include/task.h
+++ b/security/apparmor/include/task.h
@@ -10,6 +10,11 @@
#ifndef __AA_TASK_H
#define __AA_TASK_H
+#include <linux/sched.h>
+
+#include "audit.h"
+#include "label.h"
+
static inline struct aa_task_ctx *task_ctx(struct task_struct *task)
{
return task->security + apparmor_blob_sizes.lbs_task;
@@ -21,15 +26,22 @@ static inline struct aa_task_ctx *task_ctx(struct task_struct *task)
* @onexec: profile to transition to on next exec (MAY BE NULL)
* @previous: profile the task may return to (MAY BE NULL)
* @token: magic value the task must know for returning to @previous_profile
+ * @label_replacement_tw: for aa_schedule_stale_label_replacement()
+ * @label_replacement_pending: is @label_replacement_tw pending?
+ *
+ * When changing this, check if aa_dup_task_ctx() needs to be updated.
*/
struct aa_task_ctx {
struct aa_label *nnp;
struct aa_label *onexec;
struct aa_label *previous;
u64 token;
+ struct callback_head label_replacement_tw;
+ bool label_replacement_pending;
};
int aa_replace_current_label(struct aa_label *label);
+void aa_schedule_stale_label_replacement(void);
void aa_set_current_onexec(struct aa_label *label, bool stack);
int aa_set_current_hat(struct aa_label *label, u64 token);
int aa_restore_previous_label(u64 cookie);
@@ -56,10 +68,10 @@ static inline void aa_free_task_ctx(struct aa_task_ctx *ctx)
static inline void aa_dup_task_ctx(struct aa_task_ctx *new,
const struct aa_task_ctx *old)
{
- *new = *old;
- aa_get_label(new->nnp);
- aa_get_label(new->previous);
- aa_get_label(new->onexec);
+ new->nnp = aa_get_label(old->nnp);
+ new->onexec = aa_get_label(old->onexec);
+ new->previous = aa_get_label(old->previous);
+ new->token = old->token;
}
/**
diff --git a/security/apparmor/label.c b/security/apparmor/label.c
index 3fd384d8c41a..82742a471055 100644
--- a/security/apparmor/label.c
+++ b/security/apparmor/label.c
@@ -165,7 +165,8 @@ static int profile_cmp(struct aa_profile *a, struct aa_profile *b)
* ==0 if @a == @b
* >0 if @a > @b
*/
-static int vec_cmp(struct aa_profile **a, int an, struct aa_profile **b, int bn)
+static int vec_cmp(struct aa_profile * const *a, int an,
+ struct aa_profile * const *b, int bn)
{
int i;
@@ -473,7 +474,7 @@ fail:
* ==0 if a == b
* >0 if a > b
*/
-static int label_cmp(struct aa_label *a, struct aa_label *b)
+static int label_cmp(const struct aa_label *a, const struct aa_label *b)
{
AA_BUG(!b);
@@ -484,7 +485,7 @@ static int label_cmp(struct aa_label *a, struct aa_label *b)
}
/* helper fn for label_for_each_confined */
-int aa_label_next_confined(struct aa_label *label, int i)
+int aa_label_next_confined(const struct aa_label *label, int i)
{
AA_BUG(!label);
AA_BUG(i < 0);
@@ -507,8 +508,8 @@ int aa_label_next_confined(struct aa_label *label, int i)
* else NULL if @sub is a subset of @set
*/
struct aa_profile *__aa_label_next_not_in_set(struct label_it *I,
- struct aa_label *set,
- struct aa_label *sub)
+ const struct aa_label *set,
+ const struct aa_label *sub)
{
AA_BUG(!set);
AA_BUG(!I);
@@ -544,7 +545,7 @@ struct aa_profile *__aa_label_next_not_in_set(struct label_it *I,
* Returns: true if @sub is subset of @set
* else false
*/
-bool aa_label_is_subset(struct aa_label *set, struct aa_label *sub)
+bool aa_label_is_subset(const struct aa_label *set, const struct aa_label *sub)
{
struct label_it i = { };
@@ -571,7 +572,8 @@ bool aa_label_is_subset(struct aa_label *set, struct aa_label *sub)
* Returns: true if @sub is special_subset of @set
* else false
*/
-bool aa_label_is_unconfined_subset(struct aa_label *set, struct aa_label *sub)
+bool aa_label_is_unconfined_subset(const struct aa_label *set,
+ const struct aa_label *sub)
{
struct label_it i = { };
struct aa_profile *p;
@@ -796,6 +798,44 @@ bool aa_label_remove(struct aa_label *label)
return res;
}
+enum ls_lock_class {
+ AA_LS_LOCK_FIRST,
+ AA_LS_LOCK_SECOND,
+};
+
+#define write_lock_irqsave_nested(L, F, SC) write_lock_irqsave(L, F)
+
+static void ns_ls_double_lock(struct aa_ns *ns1, struct aa_ns *ns2,
+ unsigned long *flags)
+{
+ if (likely(ns1 == ns2)) {
+ write_lock_irqsave(&ns1->labels.lock, *flags);
+ return;
+ }
+
+ /* ordered by namespace hierarchy (walked in nesting order in
+ * labels_update. If at the same level by address order
+ */
+ if ((ns1->level > ns2->level) ||
+ (ns1->level == ns2->level && ns1 > ns2))
+ swap(ns1, ns2);
+
+ write_lock_irqsave_nested(&ns1->labels.lock, *flags, AA_LS_LOCK_FIRST);
+ write_lock_nested(&ns2->labels.lock, AA_LS_LOCK_SECOND);
+}
+
+static void ns_ls_double_unlock(struct aa_ns *ns1, struct aa_ns *ns2,
+ unsigned long flags)
+{
+ if (likely(ns1 == ns2)) {
+ write_unlock_irqrestore(&ns1->labels.lock, flags);
+ return;
+ }
+ /* order doesn't matter on unlock, except flags restore must be last */
+ write_unlock(&ns2->labels.lock);
+ write_unlock_irqrestore(&ns1->labels.lock, flags);
+}
+
/**
* aa_label_replace - replace a label @old with a new version @new
* @old: label to replace
@@ -803,36 +843,34 @@ bool aa_label_remove(struct aa_label *label)
*
* Returns: true if @old was in tree and replaced
* else @old was not in tree, and @new was not inserted
+ *
+ * replacement can involve two different labelsets so has to be
+ * handled very careful, as a double lock may be required.
*/
bool aa_label_replace(struct aa_label *old, struct aa_label *new)
{
+ struct aa_ns *ons = labels_ns(old);
+ struct aa_ns *nns = labels_ns(new);
unsigned long flags;
bool res;
- if (name_is_shared(old, new) && labels_ns(old) == labels_ns(new)) {
- write_lock_irqsave(&labels_set(old)->lock, flags);
+ ns_ls_double_lock(ons, nns, &flags);
+ if (ons == nns && name_is_shared(old, new)) {
if (old->proxy != new->proxy)
__proxy_share(old, new);
else
__aa_proxy_redirect(old, new);
res = __label_replace(old, new);
- write_unlock_irqrestore(&labels_set(old)->lock, flags);
} else {
struct aa_label *l;
- struct aa_labelset *ls = labels_set(old);
- write_lock_irqsave(&ls->lock, flags);
+ /* will redirect old proxy to new */
res = __label_remove(old, new);
- if (labels_ns(old) != labels_ns(new)) {
- write_unlock_irqrestore(&ls->lock, flags);
- ls = labels_set(new);
- write_lock_irqsave(&ls->lock, flags);
- }
- l = __label_insert(ls, new, true);
+ l = __label_insert(&nns->labels, new, true);
res = (l == new);
- write_unlock_irqrestore(&ls->lock, flags);
aa_put_label(l);
}
+ ns_ls_double_unlock(ons, nns, flags);
return res;
}
@@ -955,8 +993,8 @@ struct aa_label *aa_label_insert(struct aa_labelset *ls, struct aa_label *label)
* else null if no more profiles
*/
struct aa_profile *aa_label_next_in_merge(struct label_it *I,
- struct aa_label *a,
- struct aa_label *b)
+ const struct aa_label *a,
+ const struct aa_label *b)
{
AA_BUG(!a);
AA_BUG(!b);
@@ -1254,9 +1292,9 @@ out:
* If a subns profile is not to be matched should be prescreened with
* visibility test.
*/
-static inline aa_state_t match_component(struct aa_profile *profile,
+static inline aa_state_t match_component(const struct aa_profile *profile,
struct aa_ruleset *rules,
- struct aa_profile *tp,
+ const struct aa_profile *tp,
aa_state_t state)
{
const char *ns_name;
@@ -1288,7 +1326,7 @@ static inline aa_state_t match_component(struct aa_profile *profile,
* @perms should be preinitialized with allperms OR a previous permission
* check to be stacked.
*/
-static int label_compound_match(struct aa_profile *profile,
+static int label_compound_match(const struct aa_profile *profile,
struct aa_ruleset *rules,
struct aa_label *label,
aa_state_t state, bool inview, u32 request,
@@ -1344,7 +1382,7 @@ fail:
* @perms should be preinitialized with allperms OR a previous permission
* check to be stacked.
*/
-static int label_components_match(struct aa_profile *profile,
+static int label_components_match(const struct aa_profile *profile,
struct aa_ruleset *rules,
struct aa_label *label, aa_state_t start,
bool inview, u32 request,
@@ -1403,7 +1441,7 @@ fail:
*
* Returns: the state the match finished in, may be the none matching state
*/
-int aa_label_match(struct aa_profile *profile, struct aa_ruleset *rules,
+int aa_label_match(const struct aa_profile *profile, struct aa_ruleset *rules,
struct aa_label *label, aa_state_t state, bool inview,
u32 request, struct aa_perms *perms)
{
@@ -1743,10 +1781,7 @@ void aa_label_xaudit(struct audit_buffer *ab, struct aa_ns *ns,
str = (char *) label->hname;
len = strlen(str);
}
- if (audit_string_contains_control(str, len))
- audit_log_n_hex(ab, str, len);
- else
- audit_log_n_string(ab, str, len);
+ audit_log_n_untrustedstring(ab, str, len);
kfree(name);
}
diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c
index e41ff57798b2..73b88bde40bb 100644
--- a/security/apparmor/lib.c
+++ b/security/apparmor/lib.c
@@ -20,8 +20,8 @@
#include "include/perms.h"
#include "include/policy.h"
-struct aa_perms nullperms;
-struct aa_perms allperms = { .allow = ALL_PERMS_MASK,
+const struct aa_perms nullperms;
+const struct aa_perms allperms = { .allow = ALL_PERMS_MASK,
.quiet = ALL_PERMS_MASK,
.hide = ALL_PERMS_MASK };
@@ -30,7 +30,7 @@ struct val_table_ent {
int value;
};
-static struct val_table_ent debug_values_table[] = {
+static const struct val_table_ent debug_values_table[] = {
{ "N", DEBUG_NONE },
{ "none", DEBUG_NONE },
{ "n", DEBUG_NONE },
@@ -49,10 +49,11 @@ static struct val_table_ent debug_values_table[] = {
{ NULL, 0 }
};
-static struct val_table_ent *val_table_find_ent(struct val_table_ent *table,
- const char *name, size_t len)
+static const struct val_table_ent *
+val_table_find_ent(const struct val_table_ent *table,
+ const char *name, size_t len)
{
- struct val_table_ent *entry;
+ const struct val_table_ent *entry;
for (entry = table; entry->str != NULL; entry++) {
if (strncmp(entry->str, name, len) == 0 &&
@@ -64,7 +65,7 @@ static struct val_table_ent *val_table_find_ent(struct val_table_ent *table,
int aa_parse_debug_params(const char *str)
{
- struct val_table_ent *ent;
+ const struct val_table_ent *ent;
const char *next;
int val = 0;
@@ -360,8 +361,16 @@ void aa_audit_perm_mask(struct audit_buffer *ab, u32 mask, const char *chrs,
*
* TODO: split into profile and ns based flags for when accumulating perms
*/
-void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
+void aa_apply_modes_to_perms(const struct aa_profile *profile,
+ struct aa_perms *perms)
{
+ if (KILL_MODE(profile))
+ perms->kill = ~perms->allow;
+ else if (COMPLAIN_MODE(profile))
+ perms->complain |= ~(perms->allow | perms->deny);
+ else if (USER_MODE(profile))
+ perms->prompt |= ~(perms->allow | perms->deny);
+
switch (AUDIT_MODE(profile)) {
case AUDIT_ALL:
perms->audit = ALL_PERMS_MASK;
@@ -373,19 +382,15 @@ void aa_apply_modes_to_perms(struct aa_profile *profile, struct aa_perms *perms)
perms->audit = 0;
fallthrough;
case AUDIT_QUIET_DENIED:
- perms->quiet = ALL_PERMS_MASK;
+ perms->quiet |= ~perms->allow;
+ break;
+ case AUDIT_QUIET_ALLOWED:
+ perms->quiet |= perms->complain | perms->allow;
break;
}
-
- if (KILL_MODE(profile))
- perms->kill = ALL_PERMS_MASK;
- else if (COMPLAIN_MODE(profile))
- perms->complain = ALL_PERMS_MASK;
- else if (USER_MODE(profile))
- perms->prompt = ALL_PERMS_MASK;
}
-void aa_profile_match_label(struct aa_profile *profile,
+void aa_profile_match_label(const struct aa_profile *profile,
struct aa_ruleset *rules,
struct aa_label *label,
int type, u32 request, struct aa_perms *perms)
@@ -417,11 +422,11 @@ void aa_profile_match_label(struct aa_profile *profile,
* error code will indicate whether there was an explicit deny
* with a positive value.
*/
-int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
+int aa_check_perms(struct aa_profile *profile, const struct aa_perms *perms,
u32 request, struct apparmor_audit_data *ad,
void (*cb)(struct audit_buffer *, void *))
{
- int type, error;
+ int error;
u32 denied = request & (~perms->allow | perms->deny);
if (likely(!denied)) {
@@ -430,18 +435,10 @@ int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
if (!request || !ad)
return 0;
- type = AUDIT_APPARMOR_AUDIT;
error = 0;
} else {
error = -EACCES;
- if (denied & perms->kill)
- type = AUDIT_APPARMOR_KILL;
- else if (denied == (denied & perms->complain))
- type = AUDIT_APPARMOR_ALLOWED;
- else
- type = AUDIT_APPARMOR_DENIED;
-
if (denied == (denied & perms->hide))
error = -ENOENT;
@@ -450,6 +447,8 @@ int aa_check_perms(struct aa_profile *profile, struct aa_perms *perms,
return error;
}
+ int type = aa_select_audit_type(denied, perms);
+
if (ad) {
ad->subj_label = &profile->label;
ad->request = request;
@@ -482,6 +481,8 @@ bool aa_policy_init(struct aa_policy *policy, const char *prefix,
char *hname;
size_t hname_sz;
+ INIT_LIST_HEAD(&policy->list);
+ INIT_LIST_HEAD(&policy->profiles);
hname_sz = (prefix ? strlen(prefix) + 2 : 0) + strlen(name) + 1;
/* freed by policy_free */
hname = aa_str_alloc(hname_sz, gfp);
@@ -494,8 +495,6 @@ bool aa_policy_init(struct aa_policy *policy, const char *prefix,
policy->hname = hname;
/* base.name is a substring of fqname */
policy->name = basename(policy->hname);
- INIT_LIST_HEAD(&policy->list);
- INIT_LIST_HEAD(&policy->profiles);
return true;
}
diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c
index 88d12e89d115..d502ad0ac26f 100644
--- a/security/apparmor/lsm.c
+++ b/security/apparmor/lsm.c
@@ -28,6 +28,7 @@
#include <uapi/linux/lsm.h>
#include "include/af_unix.h"
+#include "include/af_inet.h"
#include "include/apparmor.h"
#include "include/apparmorfs.h"
#include "include/audit.h"
@@ -367,15 +368,16 @@ static int apparmor_path_link(struct dentry *old_dentry, const struct path *new_
{
struct aa_label *label;
int error = 0;
+ bool needput;
if (!path_mediated_fs(old_dentry))
return 0;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
if (!unconfined(label))
error = aa_path_link(current_cred(), label, old_dentry, new_dir,
new_dentry);
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return error;
}
@@ -386,13 +388,14 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d
{
struct aa_label *label;
int error = 0;
+ bool needput;
if (!path_mediated_fs(old_dentry))
return 0;
if ((flags & RENAME_EXCHANGE) && !path_mediated_fs(new_dentry))
return 0;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
if (!unconfined(label)) {
struct mnt_idmap *idmap = mnt_idmap(old_dir->mnt);
vfsuid_t vfsuid;
@@ -438,7 +441,7 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d
AA_MAY_CREATE, &cond);
}
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return error;
}
@@ -505,11 +508,12 @@ static int apparmor_file_open(struct file *file)
static int apparmor_file_alloc_security(struct file *file)
{
struct aa_file_ctx *ctx = file_ctx(file);
- struct aa_label *label = begin_current_label_crit_section();
+ bool needput;
+ struct aa_label *label = begin_current_label_crit_section(&needput);
spin_lock_init(&ctx->lock);
rcu_assign_pointer(ctx->label, aa_get_label(label));
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return 0;
}
@@ -524,11 +528,12 @@ static void apparmor_file_free_security(struct file *file)
static int common_file_perm(const char *op, struct file *file, u32 mask)
{
struct aa_label *label;
+ bool needput;
int error = 0;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
error = aa_file_perm(op, current_cred(), label, file, mask, false);
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return error;
}
@@ -849,6 +854,7 @@ static int do_setattr(u64 attr, void *value, size_t size)
char *command, *largs = NULL, *args = value;
size_t arg_size;
int error;
+ bool needput;
DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_NONE,
OP_SETPROCATTR);
@@ -906,7 +912,7 @@ out:
return error;
fail:
- ad.subj_label = begin_current_label_crit_section();
+ ad.subj_label = begin_current_label_crit_section(&needput);
if (attr == LSM_ATTR_CURRENT)
ad.info = "current";
else if (attr == LSM_ATTR_EXEC)
@@ -915,7 +921,7 @@ fail:
ad.info = "invalid";
ad.error = error = -EINVAL;
aa_audit_msg(AUDIT_APPARMOR_DENIED, &ad, NULL);
- end_current_label_crit_section(ad.subj_label);
+ end_current_label_crit_section(ad.subj_label, needput);
goto out;
}
@@ -1046,18 +1052,19 @@ static int apparmor_userns_create(const struct cred *cred)
struct aa_label *label;
struct aa_profile *profile;
int error = 0;
+ bool needput;
DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_TASK, AA_CLASS_NS,
OP_USERNS_CREATE);
ad.subj_cred = current_cred();
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
if (!unconfined(label)) {
error = fn_for_each(label, profile,
aa_profile_ns_perm(profile, &ad,
AA_USERNS_CREATE));
}
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return error;
}
@@ -1251,23 +1258,32 @@ static int apparmor_socket_create(int family, int type, int protocol, int kern)
{
struct aa_label *label;
int error = 0;
+ bool needput;
AA_BUG(in_interrupt());
if (kern)
return 0;
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
if (!unconfined(label)) {
- if (family == PF_UNIX)
+ switch (family) {
+ case PF_UNIX:
error = aa_unix_create_perm(label, family, type,
protocol);
- else
+ break;
+ case PF_INET:
+ case PF_INET6:
+ error = aa_inet_create_perm(label, family, type,
+ protocol);
+ break;
+ default:
error = aa_af_perm(current_cred(), label, OP_CREATE,
AA_MAY_CREATE, family, type,
protocol);
+ }
}
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return error;
}
@@ -1314,9 +1330,10 @@ static int apparmor_socket_socketpair(struct socket *socka,
struct aa_sk_ctx *a_ctx = aa_sock(socka->sk);
struct aa_sk_ctx *b_ctx = aa_sock(sockb->sk);
struct aa_label *label;
+ bool needput;
/* socks not live yet - initial values set in sk_alloc */
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
if (rcu_access_pointer(a_ctx->label) != label) {
AA_BUG("a_ctx != label");
aa_put_label(rcu_dereference_protected(a_ctx->label, true));
@@ -1332,7 +1349,7 @@ static int apparmor_socket_socketpair(struct socket *socka,
/* unix socket pairs by-pass unix_stream_connect */
unix_connect_peers(a_ctx, b_ctx);
}
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return 0;
}
@@ -1359,8 +1376,13 @@ static int apparmor_socket_bind(struct socket *sock,
AA_BUG(!address);
AA_BUG(in_interrupt());
- if (sock->sk->sk_family == PF_UNIX)
+ switch (sock->sk->sk_family) {
+ case PF_UNIX:
return aa_unix_bind_perm(sock, address, addrlen);
+ case PF_INET:
+ case PF_INET6:
+ return aa_inet_bind_perm(sock, address, addrlen);
+ }
return aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk);
}
@@ -1373,8 +1395,13 @@ static int apparmor_socket_connect(struct socket *sock,
AA_BUG(in_interrupt());
/* PF_UNIX goes through unix_stream_connect && unix_may_send */
- if (sock->sk->sk_family == PF_UNIX)
+ switch (sock->sk->sk_family) {
+ case PF_UNIX:
return 0;
+ case PF_INET:
+ case PF_INET6:
+ return aa_inet_connect_perm(sock, address, addrlen);
+ }
return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk);
}
@@ -1384,8 +1411,13 @@ static int apparmor_socket_listen(struct socket *sock, int backlog)
AA_BUG(!sock->sk);
AA_BUG(in_interrupt());
- if (sock->sk->sk_family == PF_UNIX)
+ switch (sock->sk->sk_family) {
+ case PF_UNIX:
return aa_unix_listen_perm(sock, backlog);
+ case PF_INET:
+ case PF_INET6:
+ return aa_inet_listen_perm(sock, backlog);
+ }
return aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk);
}
@@ -1400,8 +1432,13 @@ static int apparmor_socket_accept(struct socket *sock, struct socket *newsock)
AA_BUG(!newsock);
AA_BUG(in_interrupt());
- if (sock->sk->sk_family == PF_UNIX)
+ switch (sock->sk->sk_family) {
+ case PF_UNIX:
return aa_unix_accept_perm(sock, newsock);
+ case PF_INET:
+ case PF_INET6:
+ return aa_inet_accept_perm(sock, newsock);
+ }
return aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk);
}
@@ -1414,8 +1451,14 @@ static int aa_sock_msg_perm(const char *op, u32 request, struct socket *sock,
AA_BUG(in_interrupt());
/* PF_UNIX goes through unix_may_send */
- if (sock->sk->sk_family == PF_UNIX)
+ switch (sock->sk->sk_family) {
+ case PF_UNIX:
return 0;
+ case PF_INET:
+ case PF_INET6:
+ return aa_inet_msg_perm(op, request, sock, msg, size);
+ }
+
return aa_sk_perm(op, request, sock->sk);
}
@@ -1434,7 +1477,8 @@ static int apparmor_socket_sendmsg(struct socket *sock,
(sk_is_tcp(sock->sk) ||
(sk_is_inet(sock->sk) && sock->sk->sk_type == SOCK_STREAM &&
sock->sk->sk_protocol == IPPROTO_MPTCP)))
- error = aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk);
+ error = aa_sock_msg_perm(OP_CONNECT, AA_MAY_CONNECT, sock,
+ msg, size);
return error;
}
@@ -1452,8 +1496,13 @@ static int aa_sock_perm(const char *op, u32 request, struct socket *sock)
AA_BUG(!sock->sk);
AA_BUG(in_interrupt());
- if (sock->sk->sk_family == PF_UNIX)
+ switch (sock->sk->sk_family) {
+ case PF_UNIX:
return aa_unix_sock_perm(op, request, sock);
+ case PF_INET:
+ case PF_INET6:
+ return aa_inet_sock_perm(op, request, sock);
+ }
return aa_sk_perm(op, request, sock->sk);
}
@@ -1475,8 +1524,13 @@ static int aa_sock_opt_perm(const char *op, u32 request, struct socket *sock,
AA_BUG(!sock->sk);
AA_BUG(in_interrupt());
- if (sock->sk->sk_family == PF_UNIX)
+ switch (sock->sk->sk_family) {
+ case PF_UNIX:
return aa_unix_opt_perm(op, request, sock, level, optname);
+ case PF_INET:
+ case PF_INET6:
+ return aa_inet_opt_perm(op, request, sock, level, optname);
+ }
return aa_sk_perm(op, request, sock->sk);
}
@@ -1499,42 +1553,6 @@ static int apparmor_socket_shutdown(struct socket *sock, int how)
return aa_sock_perm(OP_SHUTDOWN, AA_MAY_SHUTDOWN, sock);
}
-#ifdef CONFIG_NETWORK_SECMARK
-/**
- * apparmor_socket_sock_rcv_skb - check perms before associating skb to sk
- * @sk: sk to associate @skb with
- * @skb: skb to check for perms
- *
- * Note: can not sleep may be called with locks held
- *
- * don't want protocol specific in __skb_recv_datagram()
- * to deny an incoming connection socket_sock_rcv_skb()
- */
-static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
-{
- struct aa_sk_ctx *ctx = aa_sock(sk);
- int error;
-
- if (!skb->secmark)
- return 0;
-
- /*
- * If reach here before socket_post_create hook is called, in which
- * case label is null, drop the packet.
- */
- if (!rcu_access_pointer(ctx->label))
- return -EACCES;
-
- rcu_read_lock();
- error = apparmor_secmark_check(rcu_dereference(ctx->label), OP_RECVMSG,
- AA_MAY_RECEIVE, skb->secmark, sk);
- rcu_read_unlock();
-
- return error;
-}
-#endif
-
-
static struct aa_label *sk_peer_get_label(struct sock *sk)
{
struct aa_sk_ctx *ctx = aa_sock(sk);
@@ -1563,13 +1581,14 @@ static int apparmor_socket_getpeersec_stream(struct socket *sock,
int slen, error = 0;
struct aa_label *label;
struct aa_label *peer;
+ bool needput;
peer = sk_peer_get_label(sock->sk);
if (IS_ERR(peer)) {
error = PTR_ERR(peer);
goto done;
}
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
slen = aa_label_asxprint(&name, labels_ns(label), peer,
FLAG_SHOW_MODE | FLAG_VIEW_SUBNS |
FLAG_HIDDEN_UNCONFINED, GFP_KERNEL);
@@ -1590,7 +1609,7 @@ done_len:
error = -EFAULT;
done_put:
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
aa_put_label(peer);
done:
kfree(name);
@@ -1634,6 +1653,39 @@ static void apparmor_sock_graft(struct sock *sk, struct socket *parent)
}
#ifdef CONFIG_NETWORK_SECMARK
+/**
+ * apparmor_socket_sock_rcv_skb - check perms before associating skb to sk
+ * @sk: sk to associate @skb with
+ * @skb: skb to check for perms
+ *
+ * Note: can not sleep may be called with locks held
+ *
+ * don't want protocol specific in __skb_recv_datagram()
+ * to deny an incoming connection socket_sock_rcv_skb()
+ */
+static int apparmor_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ struct aa_sk_ctx *ctx = aa_sock(sk);
+ int error;
+
+ if (!skb->secmark)
+ return 0;
+
+ /*
+ * If reach here before socket_post_create hook is called, in which
+ * case label is null, drop the packet.
+ */
+ if (!rcu_access_pointer(ctx->label))
+ return -EACCES;
+
+ rcu_read_lock();
+ error = apparmor_secmark_check(rcu_dereference(ctx->label), OP_RECVMSG,
+ AA_MAY_RECEIVE, skb->secmark, sk);
+ rcu_read_unlock();
+
+ return error;
+}
+
static int apparmor_inet_conn_request(const struct sock *sk, struct sk_buff *skb,
struct request_sock *req)
{
@@ -1652,6 +1704,51 @@ static int apparmor_inet_conn_request(const struct sock *sk, struct sk_buff *skb
}
#endif
+#if defined(CONFIG_NETFILTER) && defined(CONFIG_NETWORK_SECMARK)
+static unsigned int apparmor_ip_postroute(void *priv,
+ struct sk_buff *skb,
+ const struct nf_hook_state *state)
+{
+ struct aa_sk_ctx *ctx;
+ struct sock *sk;
+ int error;
+
+ if (!skb->secmark)
+ return NF_ACCEPT;
+
+ sk = skb_to_full_sk(skb);
+ if (sk == NULL)
+ return NF_ACCEPT;
+
+ ctx = aa_sock(sk);
+ rcu_read_lock();
+ error = apparmor_secmark_check(rcu_dereference(ctx->label), OP_SENDMSG,
+ AA_MAY_SEND, skb->secmark, sk);
+ rcu_read_unlock();
+ if (!error)
+ return NF_ACCEPT;
+
+ return NF_DROP_ERR(-ECONNREFUSED);
+}
+
+static const struct nf_hook_ops apparmor_nf_ops[] = {
+ {
+ .hook = apparmor_ip_postroute,
+ .pf = NFPROTO_IPV4,
+ .hooknum = NF_INET_POST_ROUTING,
+ .priority = NF_IP_PRI_SELINUX_FIRST,
+ },
+#if IS_ENABLED(CONFIG_IPV6)
+ {
+ .hook = apparmor_ip_postroute,
+ .pf = NFPROTO_IPV6,
+ .hooknum = NF_INET_POST_ROUTING,
+ .priority = NF_IP6_PRI_SELINUX_FIRST,
+ },
+#endif
+};
+#endif
+
/*
* The cred blob is a pointer to, not an instance of, an aa_label.
*/
@@ -2093,7 +2190,7 @@ static int param_set_audit(const char *val, const struct kernel_param *kp)
if (apparmor_initialized && !aa_current_policy_admin_capable(NULL))
return -EPERM;
- i = match_string(audit_mode_names, AUDIT_MAX_INDEX, val);
+ i = match_string(audit_mode_names, AUDIT_MODE_NAMES_COUNT, val);
if (i < 0)
return -EINVAL;
@@ -2121,7 +2218,7 @@ static int param_set_mode(const char *val, const struct kernel_param *kp)
if (apparmor_initialized && !aa_current_policy_admin_capable(NULL))
return -EPERM;
- i = match_string(aa_profile_mode_names, APPARMOR_MODE_NAMES_MAX_INDEX,
+ i = match_string(aa_profile_mode_names, PROFILE_MODE_NAMES_COUNT,
val);
if (i < 0)
return -EINVAL;
@@ -2369,51 +2466,8 @@ static inline int apparmor_init_sysctl(void)
}
#endif /* CONFIG_SYSCTL */
-#if defined(CONFIG_NETFILTER) && defined(CONFIG_NETWORK_SECMARK)
-static unsigned int apparmor_ip_postroute(void *priv,
- struct sk_buff *skb,
- const struct nf_hook_state *state)
-{
- struct aa_sk_ctx *ctx;
- struct sock *sk;
- int error;
-
- if (!skb->secmark)
- return NF_ACCEPT;
-
- sk = skb_to_full_sk(skb);
- if (sk == NULL)
- return NF_ACCEPT;
-
- ctx = aa_sock(sk);
- rcu_read_lock();
- error = apparmor_secmark_check(rcu_dereference(ctx->label), OP_SENDMSG,
- AA_MAY_SEND, skb->secmark, sk);
- rcu_read_unlock();
- if (!error)
- return NF_ACCEPT;
-
- return NF_DROP_ERR(-ECONNREFUSED);
-
-}
-
-static const struct nf_hook_ops apparmor_nf_ops[] = {
- {
- .hook = apparmor_ip_postroute,
- .pf = NFPROTO_IPV4,
- .hooknum = NF_INET_POST_ROUTING,
- .priority = NF_IP_PRI_SELINUX_FIRST,
- },
-#if IS_ENABLED(CONFIG_IPV6)
- {
- .hook = apparmor_ip_postroute,
- .pf = NFPROTO_IPV6,
- .hooknum = NF_INET_POST_ROUTING,
- .priority = NF_IP6_PRI_SELINUX_FIRST,
- },
-#endif
-};
+#if defined(CONFIG_NETFILTER) && defined(CONFIG_NETWORK_SECMARK)
static int __net_init apparmor_nf_register(struct net *net)
{
return nf_register_net_hooks(net, apparmor_nf_ops,
@@ -2446,16 +2500,16 @@ static int __init apparmor_nf_ip_init(void)
}
#endif
-static char nulldfa_src[] __aligned(8) = {
+static const char nulldfa_src[] __aligned(8) = {
#include "nulldfa.in"
};
-static struct aa_dfa *nulldfa;
+static struct aa_dfa *nulldfa __ro_after_init;
static char stacksplitdfa_src[] __aligned(8) = {
#include "stacksplitdfa.in"
};
-struct aa_dfa *stacksplitdfa;
-struct aa_policydb *nullpdb;
+struct aa_dfa *stacksplitdfa __ro_after_init;
+struct aa_policydb *nullpdb __ro_after_init;
static int __init aa_setup_dfa_engine(void)
{
diff --git a/security/apparmor/match.c b/security/apparmor/match.c
index d43ff34d705c..7713484f6a36 100644
--- a/security/apparmor/match.c
+++ b/security/apparmor/match.c
@@ -31,7 +31,7 @@
*
* NOTE: must be freed by kvfree (not kfree)
*/
-static struct table_header *unpack_table(char *blob, size_t bsize)
+static struct table_header *unpack_table(const char *blob, size_t bsize)
{
struct table_header *table = ERR_PTR(-EPROTO);
struct table_header th;
@@ -151,7 +151,7 @@ out:
*
* Returns: %0 else error code on failure to verify
*/
-static int verify_dfa(struct aa_dfa *dfa)
+static int verify_dfa(const struct aa_dfa *dfa)
{
size_t i, state_count, trans_count;
int error = -EPROTO;
@@ -312,11 +312,11 @@ static struct table_header *remap_data16_to_data32(struct table_header *old)
*
* Returns: an unpacked dfa ready for matching or ERR_PTR on failure
*/
-struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags)
+struct aa_dfa *aa_dfa_unpack(const void *blob, size_t size, int flags)
{
int hsize;
int error = -ENOMEM;
- char *data = blob;
+ const char *data = blob;
struct table_header *table = NULL;
struct aa_dfa *dfa = kzalloc_obj(struct aa_dfa);
if (!dfa)
@@ -467,7 +467,7 @@ do { \
*
* Returns: final state reached after input is consumed
*/
-aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start,
+aa_state_t aa_dfa_match_len(const struct aa_dfa *dfa, aa_state_t start,
const char *str, int len)
{
u32 *def = DEFAULT_TABLE(dfa);
@@ -512,7 +512,8 @@ aa_state_t aa_dfa_match_len(struct aa_dfa *dfa, aa_state_t start,
*
* Returns: final state reached after input is consumed
*/
-aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start, const char *str)
+aa_state_t aa_dfa_match(const struct aa_dfa *dfa, aa_state_t start,
+ const char *str)
{
u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
@@ -555,7 +556,8 @@ aa_state_t aa_dfa_match(struct aa_dfa *dfa, aa_state_t start, const char *str)
*
* Returns: state reach after input @c
*/
-aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c)
+aa_state_t aa_dfa_next(const struct aa_dfa *dfa, aa_state_t state,
+ const char c)
{
u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
@@ -573,7 +575,8 @@ aa_state_t aa_dfa_next(struct aa_dfa *dfa, aa_state_t state, const char c)
return state;
}
-aa_state_t aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t state)
+aa_state_t aa_dfa_outofband_transition(const struct aa_dfa *dfa,
+ aa_state_t state)
{
u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
@@ -603,8 +606,8 @@ aa_state_t aa_dfa_outofband_transition(struct aa_dfa *dfa, aa_state_t state)
*
* Returns: final state reached after input is consumed
*/
-aa_state_t aa_dfa_match_until(struct aa_dfa *dfa, aa_state_t start,
- const char *str, const char **retpos)
+aa_state_t aa_dfa_match_until(const struct aa_dfa *dfa, aa_state_t start,
+ const char *str, const char **retpos)
{
u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
@@ -664,8 +667,8 @@ aa_state_t aa_dfa_match_until(struct aa_dfa *dfa, aa_state_t start,
*
* Returns: final state reached after input is consumed
*/
-aa_state_t aa_dfa_matchn_until(struct aa_dfa *dfa, aa_state_t start,
- const char *str, int n, const char **retpos)
+aa_state_t aa_dfa_matchn_until(const struct aa_dfa *dfa, aa_state_t start,
+ const char *str, int n, const char **retpos)
{
u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
@@ -740,9 +743,9 @@ static bool is_loop(struct match_workbuf *wb, aa_state_t state,
return false;
}
-static aa_state_t leftmatch_fb(struct aa_dfa *dfa, aa_state_t start,
- const char *str, struct match_workbuf *wb,
- unsigned int *count)
+static aa_state_t leftmatch_fb(const struct aa_dfa *dfa, aa_state_t start,
+ const char *str, struct match_workbuf *wb,
+ unsigned int *count)
{
u32 *def = DEFAULT_TABLE(dfa);
u32 *base = BASE_TABLE(dfa);
@@ -821,7 +824,7 @@ out:
*
* Returns: final state reached after input is consumed
*/
-aa_state_t aa_dfa_leftmatch(struct aa_dfa *dfa, aa_state_t start,
+aa_state_t aa_dfa_leftmatch(const struct aa_dfa *dfa, aa_state_t start,
const char *str, unsigned int *count)
{
DEFINE_MATCH_WB(wb);
diff --git a/security/apparmor/mount.c b/security/apparmor/mount.c
index 2f5d918832c1..4ed7b9136beb 100644
--- a/security/apparmor/mount.c
+++ b/security/apparmor/mount.c
@@ -24,6 +24,10 @@
#include "include/policy.h"
+#define DEFINE_AUDIT_MOUNT(NAME, OP, CRED) \
+ DEFINE_AUDIT_DATA(NAME, LSM_AUDIT_DATA_NONE, AA_CLASS_MOUNT, OP);\
+ NAME.subj_cred = (CRED)
+
static void audit_mnt_flags(struct audit_buffer *ab, unsigned long flags)
{
if (flags & MS_RDONLY)
@@ -114,77 +118,6 @@ static void audit_cb(struct audit_buffer *ab, void *va)
}
/**
- * audit_mount - handle the auditing of mount operations
- * @subj_cred: cred of the subject
- * @profile: the profile being enforced (NOT NULL)
- * @op: operation being mediated (NOT NULL)
- * @name: name of object being mediated (MAYBE NULL)
- * @src_name: src_name of object being mediated (MAYBE_NULL)
- * @type: type of filesystem (MAYBE_NULL)
- * @trans: name of trans (MAYBE NULL)
- * @flags: filesystem independent mount flags
- * @data: filesystem mount flags
- * @request: permissions requested
- * @perms: the permissions computed for the request (NOT NULL)
- * @info: extra information message (MAYBE NULL)
- * @error: 0 if operation allowed else failure error code
- *
- * Returns: %0 or error on failure
- */
-static int audit_mount(const struct cred *subj_cred,
- struct aa_profile *profile, const char *op,
- const char *name, const char *src_name,
- const char *type, const char *trans,
- unsigned long flags, const void *data, u32 request,
- struct aa_perms *perms, const char *info, int error)
-{
- int audit_type = AUDIT_APPARMOR_AUTO;
- DEFINE_AUDIT_DATA(ad, LSM_AUDIT_DATA_NONE, AA_CLASS_MOUNT, op);
-
- if (likely(!error)) {
- u32 mask = perms->audit;
-
- if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL))
- mask = 0xffff;
-
- /* mask off perms that are not being force audited */
- request &= mask;
-
- if (likely(!request))
- return 0;
- audit_type = AUDIT_APPARMOR_AUDIT;
- } else {
- /* only report permissions that were denied */
- request = request & ~perms->allow;
-
- if (request & perms->kill)
- audit_type = AUDIT_APPARMOR_KILL;
-
- /* quiet known rejects, assumes quiet and kill do not overlap */
- if ((request & perms->quiet) &&
- AUDIT_MODE(profile) != AUDIT_NOQUIET &&
- AUDIT_MODE(profile) != AUDIT_ALL)
- request &= ~perms->quiet;
-
- if (!request)
- return error;
- }
-
- ad.subj_cred = subj_cred;
- ad.name = name;
- ad.mnt.src_name = src_name;
- ad.mnt.type = type;
- ad.mnt.trans = trans;
- ad.mnt.flags = flags;
- if (data && (perms->audit & AA_AUDIT_DATA))
- ad.mnt.data = data;
- ad.info = info;
- ad.error = error;
-
- return aa_audit(audit_type, profile, &ad, audit_cb);
-}
-
-/**
* match_mnt_flags - Do an ordered match on mount flags
* @dfa: dfa to match against
* @state: state to start in
@@ -196,7 +129,7 @@ static int audit_mount(const struct cred *subj_cred,
*
* Returns: next state after flags match
*/
-static aa_state_t match_mnt_flags(struct aa_dfa *dfa, aa_state_t state,
+static aa_state_t match_mnt_flags(const struct aa_dfa *dfa, aa_state_t state,
unsigned long flags)
{
unsigned int i;
@@ -278,7 +211,7 @@ static int do_match_mnt(struct aa_policydb *policy, aa_state_t start,
}
-static int path_flags(struct aa_profile *profile, const struct path *path)
+static int path_flags(const struct aa_profile *profile, const struct path *path)
{
AA_BUG(!profile);
AA_BUG(!path);
@@ -289,7 +222,6 @@ static int path_flags(struct aa_profile *profile, const struct path *path)
/**
* match_mnt_path_str - handle path matching for mount
- * @subj_cred: cred of confined subject
* @profile: the confining profile
* @mntpath: for the mntpnt (NOT NULL)
* @buffer: buffer to be used to lookup mntpath
@@ -299,18 +231,19 @@ static int path_flags(struct aa_profile *profile, const struct path *path)
* @data: fs mount data (MAYBE NULL)
* @binary: whether @data is binary
* @devinfo: error str if (IS_ERR(@devname))
+ * @ad: apparmor audit data structure
*
* Returns: 0 on success else error
*/
-static int match_mnt_path_str(const struct cred *subj_cred,
- struct aa_profile *profile,
+static int match_mnt_path_str(struct aa_profile *profile,
const struct path *mntpath, char *buffer,
const char *devname, const char *type,
unsigned long flags, void *data, bool binary,
- const char *devinfo)
+ const char *devinfo,
+ struct apparmor_audit_data *ad)
{
struct aa_perms perms = { };
- const char *mntpnt = NULL, *info = NULL;
+ const char *mntpnt = NULL;
struct aa_ruleset *rules = profile->label.rules[0];
int pos, error;
@@ -321,36 +254,37 @@ static int match_mnt_path_str(const struct cred *subj_cred,
if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
return 0;
+ ad->mnt.type = type;
+
error = aa_path_name(mntpath, path_flags(profile, mntpath), buffer,
- &mntpnt, &info, profile->disconnected);
+ &mntpnt, &ad->info, profile->disconnected);
if (error)
- goto audit;
+ return aa_audit_perm_error(&profile->label, AA_MAY_MOUNT,
+ error, ad, audit_cb);
+ ad->name = mntpnt;
+
if (IS_ERR(devname)) {
error = PTR_ERR(devname);
- devname = NULL;
- info = devinfo;
- goto audit;
+ ad->info = devinfo;
+ return aa_audit_perm_error(&profile->label, AA_MAY_MOUNT,
+ error, ad, audit_cb);
}
+ ad->mnt.src_name = devname;
- error = -EACCES;
pos = do_match_mnt(rules->policy,
rules->policy->start[AA_CLASS_MOUNT],
mntpnt, devname, type, flags, data, binary, &perms);
- if (pos) {
- info = mnt_info_table[pos];
- goto audit;
- }
- error = 0;
+ if (pos)
+ ad->info = mnt_info_table[pos];
-audit:
- return audit_mount(subj_cred, profile, OP_MOUNT, mntpnt, devname,
- type, NULL,
- flags, data, AA_MAY_MOUNT, &perms, info, error);
+ aa_apply_modes_to_perms(profile, &perms);
+ if (data && !binary && (perms.audit & AA_AUDIT_DATA))
+ ad->mnt.data = data;
+ return aa_check_perms(profile, &perms, AA_MAY_MOUNT, ad, audit_cb);
}
/**
* match_mnt - handle path matching for mount
- * @subj_cred: cred of the subject
* @profile: the confining profile
* @path: for the mntpnt (NOT NULL)
* @buffer: buffer to be used to lookup mntpath
@@ -360,14 +294,14 @@ audit:
* @flags: mount flags to match
* @data: fs mount data (MAYBE NULL)
* @binary: whether @data is binary
+ * @ad: apparmor audit data structure
*
* Returns: 0 on success else error
*/
-static int match_mnt(const struct cred *subj_cred,
- struct aa_profile *profile, const struct path *path,
+static int match_mnt(struct aa_profile *profile, const struct path *path,
char *buffer, const struct path *devpath, char *devbuffer,
const char *type, unsigned long flags, void *data,
- bool binary)
+ bool binary, struct apparmor_audit_data *ad)
{
const char *devname = NULL, *info = NULL;
struct aa_ruleset *rules = profile->label.rules[0];
@@ -387,8 +321,8 @@ static int match_mnt(const struct cred *subj_cred,
devname = ERR_PTR(error);
}
- return match_mnt_path_str(subj_cred, profile, path, buffer, devname,
- type, flags, data, binary, info);
+ return match_mnt_path_str(profile, path, buffer, devname,
+ type, flags, data, binary, info, ad);
}
int aa_remount(const struct cred *subj_cred,
@@ -399,6 +333,8 @@ int aa_remount(const struct cred *subj_cred,
char *buffer = NULL;
bool binary;
int error;
+ DEFINE_AUDIT_MOUNT(ad, OP_MOUNT, subj_cred);
+ ad.mnt.flags = flags;
AA_BUG(!label);
AA_BUG(!path);
@@ -408,10 +344,9 @@ int aa_remount(const struct cred *subj_cred,
buffer = aa_get_buffer(false);
if (!buffer)
return -ENOMEM;
- error = fn_for_each_confined(label, profile,
- match_mnt(subj_cred, profile, path, buffer, NULL,
- NULL, NULL,
- flags, data, binary));
+ error = fn_for_each(label, profile,
+ match_mnt(profile, path, buffer, NULL, NULL, NULL,
+ flags, data, binary, &ad));
aa_put_buffer(buffer);
return error;
@@ -425,6 +360,7 @@ int aa_bind_mount(const struct cred *subj_cred,
char *buffer = NULL, *old_buffer = NULL;
struct path old_path;
int error;
+ DEFINE_AUDIT_MOUNT(ad, OP_MOUNT, subj_cred);
AA_BUG(!label);
AA_BUG(!path);
@@ -433,10 +369,12 @@ int aa_bind_mount(const struct cred *subj_cred,
return -EINVAL;
flags &= MS_REC | MS_BIND;
+ ad.mnt.flags = flags;
error = kern_path(dev_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path);
if (error)
- return error;
+ return aa_audit_perm_error(label, AA_MAY_MOUNT, error, &ad,
+ audit_cb);
buffer = aa_get_buffer(false);
old_buffer = aa_get_buffer(false);
@@ -444,9 +382,9 @@ int aa_bind_mount(const struct cred *subj_cred,
if (!buffer || !old_buffer)
goto out;
- error = fn_for_each_confined(label, profile,
- match_mnt(subj_cred, profile, path, buffer, &old_path,
- old_buffer, NULL, flags, NULL, false));
+ error = fn_for_each(label, profile,
+ match_mnt(profile, path, buffer, &old_path,
+ old_buffer, NULL, flags, NULL, false, &ad));
out:
aa_put_buffer(buffer);
aa_put_buffer(old_buffer);
@@ -462,6 +400,7 @@ int aa_mount_change_type(const struct cred *subj_cred,
struct aa_profile *profile;
char *buffer = NULL;
int error;
+ DEFINE_AUDIT_MOUNT(ad, OP_MOUNT, subj_cred);
AA_BUG(!label);
AA_BUG(!path);
@@ -469,14 +408,14 @@ int aa_mount_change_type(const struct cred *subj_cred,
/* These are the flags allowed by do_change_type() */
flags &= (MS_REC | MS_SILENT | MS_SHARED | MS_PRIVATE | MS_SLAVE |
MS_UNBINDABLE);
+ ad.mnt.flags = flags;
buffer = aa_get_buffer(false);
if (!buffer)
return -ENOMEM;
- error = fn_for_each_confined(label, profile,
- match_mnt(subj_cred, profile, path, buffer, NULL,
- NULL, NULL,
- flags, NULL, false));
+ error = fn_for_each(label, profile,
+ match_mnt(profile, path, buffer, NULL, NULL, NULL,
+ flags, NULL, false, &ad));
aa_put_buffer(buffer);
return error;
@@ -489,6 +428,8 @@ int aa_move_mount(const struct cred *subj_cred,
struct aa_profile *profile;
char *to_buffer = NULL, *from_buffer = NULL;
int error;
+ DEFINE_AUDIT_MOUNT(ad, OP_MOUNT, subj_cred);
+ ad.mnt.flags = MS_MOVE;
AA_BUG(!label);
AA_BUG(!from_path);
@@ -503,10 +444,10 @@ int aa_move_mount(const struct cred *subj_cred,
if (!our_mnt(from_path->mnt))
/* moving a mount detached from the namespace */
from_path = NULL;
- error = fn_for_each_confined(label, profile,
- match_mnt(subj_cred, profile, to_path, to_buffer,
- from_path, from_buffer,
- NULL, MS_MOVE, NULL, false));
+ error = fn_for_each(label, profile,
+ match_mnt(profile, to_path, to_buffer, from_path,
+ from_buffer, NULL, MS_MOVE, NULL, false,
+ &ad));
out:
aa_put_buffer(to_buffer);
aa_put_buffer(from_buffer);
@@ -542,6 +483,8 @@ int aa_new_mount(const struct cred *subj_cred, struct aa_label *label,
int error;
int requires_dev = 0;
struct path tmp_path, *dev_path = NULL;
+ DEFINE_AUDIT_MOUNT(ad, OP_MOUNT, subj_cred);
+ ad.mnt.flags = flags;
AA_BUG(!label);
AA_BUG(!path);
@@ -578,15 +521,15 @@ int aa_new_mount(const struct cred *subj_cred, struct aa_label *label,
error = -ENOMEM;
goto out;
}
- error = fn_for_each_confined(label, profile,
- match_mnt(subj_cred, profile, path, buffer,
- dev_path, dev_buffer,
- type, flags, data, binary));
+ error = fn_for_each(label, profile,
+ match_mnt(profile, path, buffer, dev_path,
+ dev_buffer, type, flags, data,
+ binary, &ad));
} else {
- error = fn_for_each_confined(label, profile,
- match_mnt_path_str(subj_cred, profile, path,
- buffer, dev_name,
- type, flags, data, binary, NULL));
+ error = fn_for_each(label, profile,
+ match_mnt_path_str(profile, path, buffer,
+ dev_name, type, flags, data,
+ binary, NULL, &ad));
}
out:
@@ -598,13 +541,12 @@ out:
return error;
}
-static int profile_umount(const struct cred *subj_cred,
- struct aa_profile *profile, const struct path *path,
- char *buffer)
+static int profile_umount(struct aa_profile *profile, const struct path *path,
+ char *buffer, struct apparmor_audit_data *ad)
{
struct aa_ruleset *rules = profile->label.rules[0];
struct aa_perms perms = { };
- const char *name = NULL, *info = NULL;
+ const char *name = NULL;
aa_state_t state;
int error;
@@ -614,22 +556,23 @@ static int profile_umount(const struct cred *subj_cred,
if (!RULE_MEDIATES(rules, AA_CLASS_MOUNT))
return 0;
+ /* TODO: lift path_name, need to separate profile path_flags from
+ * the lookup
+ */
error = aa_path_name(path, path_flags(profile, path), buffer, &name,
- &info, profile->disconnected);
+ &ad->info, profile->disconnected);
if (error)
- goto audit;
+ return aa_audit_perm_error(&profile->label, AA_MAY_UMOUNT,
+ error, ad, audit_cb);
+ ad->name = name;
state = aa_dfa_match(rules->policy->dfa,
rules->policy->start[AA_CLASS_MOUNT],
name);
perms = *aa_lookup_perms(rules->policy, state);
- if (AA_MAY_UMOUNT & ~perms.allow)
- error = -EACCES;
-audit:
- return audit_mount(subj_cred, profile, OP_UMOUNT, name, NULL, NULL,
- NULL, 0, NULL,
- AA_MAY_UMOUNT, &perms, info, error);
+ aa_apply_modes_to_perms(profile, &perms);
+ return aa_check_perms(profile, &perms, AA_MAY_UMOUNT, ad, audit_cb);
}
int aa_umount(const struct cred *subj_cred, struct aa_label *label,
@@ -639,6 +582,7 @@ int aa_umount(const struct cred *subj_cred, struct aa_label *label,
char *buffer = NULL;
int error;
struct path path = { .mnt = mnt, .dentry = mnt->mnt_root };
+ DEFINE_AUDIT_MOUNT(ad, OP_UMOUNT, subj_cred);
AA_BUG(!label);
AA_BUG(!mnt);
@@ -647,8 +591,8 @@ int aa_umount(const struct cred *subj_cred, struct aa_label *label,
if (!buffer)
return -ENOMEM;
- error = fn_for_each_confined(label, profile,
- profile_umount(subj_cred, profile, &path, buffer));
+ error = fn_for_each(label, profile,
+ profile_umount(profile, &path, buffer, &ad));
aa_put_buffer(buffer);
return error;
@@ -658,16 +602,15 @@ int aa_umount(const struct cred *subj_cred, struct aa_label *label,
*
* Returns: label for transition or ERR_PTR. Does not return NULL
*/
-static struct aa_label *build_pivotroot(const struct cred *subj_cred,
- struct aa_profile *profile,
+static struct aa_label *build_pivotroot(struct aa_profile *profile,
const struct path *new_path,
char *new_buffer,
const struct path *old_path,
- char *old_buffer)
+ char *old_buffer,
+ struct apparmor_audit_data *ad)
{
struct aa_ruleset *rules = profile->label.rules[0];
- const char *old_name, *new_name = NULL, *info = NULL;
- const char *trans_name = NULL;
+ const char *old_name, *new_name = NULL;
struct aa_perms perms = { };
aa_state_t state;
int error;
@@ -681,36 +624,40 @@ static struct aa_label *build_pivotroot(const struct cred *subj_cred,
return aa_get_newest_label(&profile->label);
error = aa_path_name(old_path, path_flags(profile, old_path),
- old_buffer, &old_name, &info,
+ old_buffer, &old_name, &ad->info,
profile->disconnected);
if (error)
- goto audit;
+ goto err;
+ ad->mnt.src_name = old_name;
error = aa_path_name(new_path, path_flags(profile, new_path),
- new_buffer, &new_name, &info,
+ new_buffer, &new_name, &ad->info,
profile->disconnected);
if (error)
- goto audit;
+ goto err;
+ ad->name = new_name;
- error = -EACCES;
state = aa_dfa_match(rules->policy->dfa,
rules->policy->start[AA_CLASS_MOUNT],
new_name);
state = aa_dfa_null_transition(rules->policy->dfa, state);
state = aa_dfa_match(rules->policy->dfa, state, old_name);
perms = *aa_lookup_perms(rules->policy, state);
+ /* todo: allow pivotroot to specify a transition other than profile */
+ ad->mnt.trans = profile->label.hname;
- if (AA_MAY_PIVOTROOT & perms.allow)
- error = 0;
+ aa_apply_modes_to_perms(profile, &perms);
+ error = aa_check_perms(profile, &perms, AA_MAY_PIVOTROOT, ad, audit_cb);
-audit:
- error = audit_mount(subj_cred, profile, OP_PIVOTROOT, new_name,
- old_name,
- NULL, trans_name, 0, NULL, AA_MAY_PIVOTROOT,
- &perms, info, error);
+out:
if (error)
return ERR_PTR(error);
return aa_get_newest_label(&profile->label);
+
+err:
+ error = aa_audit_perm_error(&profile->label, AA_MAY_PIVOTROOT, error,
+ ad, audit_cb);
+ goto out;
}
int aa_pivotroot(const struct cred *subj_cred, struct aa_label *label,
@@ -719,8 +666,9 @@ int aa_pivotroot(const struct cred *subj_cred, struct aa_label *label,
{
struct aa_profile *profile;
struct aa_label *target = NULL;
- char *old_buffer = NULL, *new_buffer = NULL, *info = NULL;
+ char *old_buffer = NULL, *new_buffer = NULL;
int error;
+ DEFINE_AUDIT_MOUNT(ad, OP_PIVOTROOT, subj_cred);
AA_BUG(!label);
AA_BUG(!old_path);
@@ -732,9 +680,8 @@ int aa_pivotroot(const struct cred *subj_cred, struct aa_label *label,
if (!old_buffer || !new_buffer)
goto out;
target = fn_label_build(label, profile, GFP_KERNEL,
- build_pivotroot(subj_cred, profile, new_path,
- new_buffer,
- old_path, old_buffer));
+ build_pivotroot(profile, new_path, new_buffer,
+ old_path, old_buffer, &ad));
AA_BUG(!target);
if (!IS_ERR(target)) {
error = aa_replace_current_label(target);
@@ -742,7 +689,7 @@ int aa_pivotroot(const struct cred *subj_cred, struct aa_label *label,
goto fail;
aa_put_label(target);
} else
- /* already audited error */
+ /* already audited error in build_pivotroot */
error = PTR_ERR(target);
out:
aa_put_buffer(old_buffer);
@@ -751,14 +698,12 @@ out:
return error;
fail:
- /* TODO: add back in auditing of new_name and old_name */
- error = fn_for_each(label, profile,
- audit_mount(subj_cred, profile, OP_PIVOTROOT,
- NULL /*new_name */,
- NULL /* old_name */,
- NULL, NULL,
- 0, target->hname, AA_MAY_PIVOTROOT, &nullperms, info,
- error));
+ /* TODO: add back in auditing of new_name and old_name,
+ * needs lifting of name lookup out of profile cb
+ */
+ ad.mnt.trans = target->hname;
+ error = aa_audit_perm_error(label, AA_MAY_PIVOTROOT, error, &ad,
+ audit_cb);
aa_put_label(target);
goto out;
}
diff --git a/security/apparmor/net.c b/security/apparmor/net.c
index cf590dd08540..a333e6aff926 100644
--- a/security/apparmor/net.c
+++ b/security/apparmor/net.c
@@ -9,6 +9,7 @@
*/
#include "include/af_unix.h"
+#include "include/af_inet.h"
#include "include/apparmor.h"
#include "include/audit.h"
#include "include/cred.h"
@@ -133,12 +134,12 @@ void audit_net_cb(struct audit_buffer *ab, void *va)
audit_log_format(ab, " protocol=%d", ad->net.protocol);
if (ad->request & NET_PERMS_MASK) {
- audit_log_format(ab, " requested_mask=");
+ audit_log_format(ab, " requested=");
aa_audit_perm_mask(ab, ad->request, NULL, 0,
net_mask_names, NET_PERMS_MASK);
if (ad->denied & NET_PERMS_MASK) {
- audit_log_format(ab, " denied_mask=");
+ audit_log_format(ab, " denied=");
aa_audit_perm_mask(ab, ad->denied, NULL, 0,
net_mask_names, NET_PERMS_MASK);
}
@@ -166,7 +167,7 @@ void audit_net_cb(struct audit_buffer *ab, void *va)
/* standard permission lookup pattern - supports early bailout */
int aa_do_perms(struct aa_profile *profile, struct aa_policydb *policy,
aa_state_t state, u32 request,
- struct aa_perms *p, struct apparmor_audit_data *ad)
+ const struct aa_perms *p, struct apparmor_audit_data *ad)
{
struct aa_perms perms;
@@ -198,7 +199,7 @@ static struct aa_perms *early_match(struct aa_policydb *policy,
return p;
}
-static aa_state_t aa_dfa_match_be16(struct aa_dfa *dfa, aa_state_t state,
+static aa_state_t aa_dfa_match_be16(const struct aa_dfa *dfa, aa_state_t state,
u16 data)
{
__be16 buffer = cpu_to_be16(data);
@@ -261,14 +262,15 @@ int aa_profile_af_perm(struct aa_profile *profile,
AA_BUG(type < 0 || type >= SOCK_MAX);
AA_BUG(profile_unconfined(profile));
- if (profile_unconfined(profile))
- return 0;
state = RULE_MEDIATES_NET(rules);
- if (!state)
- return 0;
- state = aa_match_to_prot(rules->policy, state, request, family, type,
- protocol, &p, &ad->info);
- return aa_do_perms(profile, rules->policy, state, request, p, ad);
+ if (state) {
+ state = aa_match_to_prot(rules->policy, state, request, family,
+ type, protocol, &p, &ad->info);
+ return aa_do_perms(profile, rules->policy, state, request, p,
+ ad);
+ } /* else */
+
+ return 0;
}
int aa_af_perm(const struct cred *subj_cred, struct aa_label *label,
@@ -282,10 +284,8 @@ int aa_af_perm(const struct cred *subj_cred, struct aa_label *label,
type, protocol));
}
-static int aa_label_sk_perm(const struct cred *subj_cred,
- struct aa_label *label,
- const char *op, u32 request,
- struct sock *sk)
+int aa_label_sk_perm(const struct cred *subj_cred, struct aa_label *label,
+ const char *op, u32 request, const struct sock *sk)
{
struct aa_sk_ctx *ctx = aa_sock(sk);
int error = 0;
@@ -299,24 +299,26 @@ static int aa_label_sk_perm(const struct cred *subj_cred,
ad.subj_cred = subj_cred;
error = fn_for_each_confined(label, profile,
- aa_profile_af_sk_perm(profile, &ad, request, sk));
+ aa_profile_af_perm(profile, &ad, request, sk->sk_family,
+ sk->sk_type, sk->sk_protocol));
}
return error;
}
-int aa_sk_perm(const char *op, u32 request, struct sock *sk)
+int aa_sk_perm(const char *op, u32 request, const struct sock *sk)
{
struct aa_label *label;
+ bool needput;
int error;
AA_BUG(!sk);
AA_BUG(in_interrupt());
/* TODO: switch to begin_current_label ???? */
- label = begin_current_label_crit_section();
+ label = begin_current_label_crit_section(&needput);
error = aa_label_sk_perm(current_cred(), label, op, request, sk);
- end_current_label_crit_section(label);
+ end_current_label_crit_section(label, needput);
return error;
}
@@ -333,8 +335,13 @@ int aa_sock_file_perm(const struct cred *subj_cred, struct aa_label *label,
if (!sock || !sock->sk)
return 0;
- if (sock->sk->sk_family == PF_UNIX)
+ switch (sock->sk->sk_family) {
+ case PF_UNIX:
return aa_unix_file_perm(subj_cred, label, op, request, file);
+ case PF_INET:
+ case PF_INET6:
+ return aa_inet_file_perm(subj_cred, label, op, request, sock);
+ }
return aa_label_sk_perm(subj_cred, label, op, request, sock->sk);
}
diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c
index 94b4a7e727cc..397f9ff78aeb 100644
--- a/security/apparmor/policy.c
+++ b/security/apparmor/policy.c
@@ -81,6 +81,7 @@
#include "include/file.h"
#include "include/ipc.h"
#include "include/match.h"
+#include "include/net.h"
#include "include/path.h"
#include "include/policy.h"
#include "include/policy_ns.h"
@@ -719,7 +720,7 @@ struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name,
}
/**
- * aa_new_learning_profile - create or find a null-X learning profile
+ * __aa_new_learning_profile - create or find a null-X learning profile
* @parent: profile that caused this profile to be created (NOT NULL)
* @hat: true if the null- learning profile is a hat
* @base: name to base the null profile off of
@@ -736,8 +737,9 @@ struct aa_profile *aa_alloc_null(struct aa_profile *parent, const char *name,
*
* Returns: new refcounted profile else NULL on failure
*/
-struct aa_profile *aa_new_learning_profile(struct aa_profile *parent, bool hat,
- const char *base, gfp_t gfp)
+struct aa_profile *__aa_new_learning_profile(struct aa_profile *parent,
+ bool hat, const char *base,
+ gfp_t gfp)
{
struct aa_profile *p, *profile;
const char *bname;
@@ -745,6 +747,7 @@ struct aa_profile *aa_new_learning_profile(struct aa_profile *parent, bool hat,
size_t name_sz;
AA_BUG(!parent);
+ AA_BUG(!mutex_is_locked(&parent->ns->lock));
if (base) {
name_sz = strlen(parent->base.hname) + 8 + strlen(base);
@@ -778,7 +781,6 @@ name:
if (hat)
profile->label.flags |= FLAG_HAT;
- mutex_lock_nested(&profile->ns->lock, profile->ns->level);
p = __find_child(&parent->base.profiles, bname);
if (p) {
aa_free_profile(profile);
@@ -786,7 +788,6 @@ name:
} else {
__add_profile(&parent->base.profiles, profile);
}
- mutex_unlock(&profile->ns->lock);
/* refcount released by caller */
out:
@@ -800,6 +801,18 @@ fail:
return NULL;
}
+struct aa_profile *aa_new_learning_profile(struct aa_profile *parent, bool hat,
+ const char *base, gfp_t gfp)
+{
+ struct aa_profile *profile;
+
+ mutex_lock_nested(&parent->ns->lock, parent->ns->level);
+ profile = __aa_new_learning_profile(parent, hat, base, gfp);
+ mutex_unlock(&parent->ns->lock);
+
+ return profile;
+}
+
/**
* replacement_allowed - test to see if replacement is allowed
* @profile: profile to test if it can be replaced (MAYBE NULL)
@@ -1158,6 +1171,8 @@ static struct aa_profile *update_to_newest_parent(struct aa_profile *new)
* @label: label that is attempting to load/replace policy
* @mask: permission mask
* @udata: serialized data stream (NOT NULL)
+ * @compressed_profile: The userspace-provided compressed profile. May be NULL
+ * @compressed_size: If compressed_data is not NULL, the compressed data size
*
* unpack and replace a profile on the profile list and uses of that profile
* by any task creds via invalidating the old version of the profile, which
@@ -1167,7 +1182,8 @@ static struct aa_profile *update_to_newest_parent(struct aa_profile *new)
* Returns: size of data consumed else error code on failure.
*/
ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
- u32 mask, struct aa_loaddata *udata)
+ u32 mask, struct aa_loaddata *udata,
+ char *compressed_profile, size_t compressed_size)
{
const char *ns_name = NULL, *info = NULL;
struct aa_ns *ns = NULL;
@@ -1180,7 +1196,7 @@ ssize_t aa_replace_profiles(struct aa_ns *policy_ns, struct aa_label *label,
op = mask & AA_MAY_REPLACE_POLICY ? OP_PROF_REPL : OP_PROF_LOAD;
aa_get_profile_loaddata(udata);
/* released below */
- error = aa_unpack(udata, &lh, &ns_name);
+ error = aa_unpack(udata, &lh, &ns_name, compressed_profile, compressed_size);
if (error)
goto out;
diff --git a/security/apparmor/policy_compat.c b/security/apparmor/policy_compat.c
index 5fc16d56fbf4..94e4b781d33c 100644
--- a/security/apparmor/policy_compat.c
+++ b/security/apparmor/policy_compat.c
@@ -97,7 +97,7 @@ static u32 map_old_perms(u32 old)
return new;
}
-static void compute_fperms_allow(struct aa_perms *perms, struct aa_dfa *dfa,
+static void compute_fperms_allow(struct aa_perms *perms, const struct aa_dfa *dfa,
aa_state_t state)
{
perms->allow |= AA_MAY_GETATTR;
@@ -109,7 +109,7 @@ static void compute_fperms_allow(struct aa_perms *perms, struct aa_dfa *dfa,
perms->allow |= AA_MAY_ONEXEC;
}
-static struct aa_perms compute_fperms_user(struct aa_dfa *dfa,
+static struct aa_perms compute_fperms_user(const struct aa_dfa *dfa,
aa_state_t state)
{
struct aa_perms perms = { };
@@ -124,7 +124,7 @@ static struct aa_perms compute_fperms_user(struct aa_dfa *dfa,
return perms;
}
-static struct aa_perms compute_fperms_other(struct aa_dfa *dfa,
+static struct aa_perms compute_fperms_other(const struct aa_dfa *dfa,
aa_state_t state)
{
struct aa_perms perms = { };
@@ -147,7 +147,7 @@ static struct aa_perms compute_fperms_other(struct aa_dfa *dfa,
*
* Returns: remapped perm table
*/
-static struct aa_perms *compute_fperms(struct aa_dfa *dfa,
+static struct aa_perms *compute_fperms(const struct aa_dfa *dfa,
u32 *size)
{
aa_state_t state;
@@ -171,7 +171,7 @@ static struct aa_perms *compute_fperms(struct aa_dfa *dfa,
return table;
}
-static struct aa_perms *compute_xmatch_perms(struct aa_dfa *xmatch,
+static struct aa_perms *compute_xmatch_perms(const struct aa_dfa *xmatch,
u32 *size)
{
struct aa_perms *perms;
@@ -207,7 +207,7 @@ static u32 map_xbits(u32 x)
((x & 0x7e) << 9);
}
-static struct aa_perms compute_perms_entry(struct aa_dfa *dfa,
+static struct aa_perms compute_perms_entry(const struct aa_dfa *dfa,
aa_state_t state,
u32 version)
{
@@ -246,7 +246,7 @@ static struct aa_perms compute_perms_entry(struct aa_dfa *dfa,
return perms;
}
-static struct aa_perms *compute_perms(struct aa_dfa *dfa, u32 version,
+static struct aa_perms *compute_perms(const struct aa_dfa *dfa, u32 version,
u32 *size)
{
unsigned int state;
diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c
index d9dcff167c48..f1fc48e72d0e 100644
--- a/security/apparmor/policy_unpack.c
+++ b/security/apparmor/policy_unpack.c
@@ -25,6 +25,7 @@
#include "include/crypto.h"
#include "include/file.h"
#include "include/match.h"
+#include "include/net.h"
#include "include/path.h"
#include "include/policy.h"
#include "include/policy_unpack.h"
@@ -730,7 +731,7 @@ static bool verify_tags(struct aa_tags_struct *tags, const char **info)
/* count followed by count indexes into hdrs */
u32 cnt = tags->sets.table[i];
- if (i+cnt >= tags->sets.size) {
+ if ((u64)i + cnt >= tags->sets.size) {
AA_DEBUG(DEBUG_UNPACK,
"tagset too large %d+%d > sets.table[%d]",
i, cnt, tags->sets.size);
@@ -1482,7 +1483,7 @@ static int verify_header(struct aa_ext *e, int required, const char **ns)
* @dfa: the dfa to check accept indexes are in range
* @table_size: the permission table size the indexes should be within
*/
-static bool verify_dfa_accept_index(struct aa_dfa *dfa, int table_size)
+static bool verify_dfa_accept_index(const struct aa_dfa *dfa, int table_size)
{
int i;
for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) {
@@ -1492,7 +1493,7 @@ static bool verify_dfa_accept_index(struct aa_dfa *dfa, int table_size)
return true;
}
-static bool verify_perm(struct aa_perms *perm)
+static bool verify_perm(const struct aa_perms *perm)
{
/* TODO: allow option to just force the perms into a valid state */
if (perm->allow & perm->deny)
@@ -1717,6 +1718,8 @@ static int compress_loaddata(struct aa_loaddata *data)
* @udata: user data copied to kmem (NOT NULL)
* @lh: list to place unpacked profiles in a aa_repl_ws
* @ns: Returns namespace profile is in if specified else NULL (NOT NULL)
+ * @compressed_data: The userspace-provided compressed data. May be NULL
+ * @compressed_size: If compressed_data is not NULL, the compressed data size
*
* Unpack user data and return refcounted allocated profile(s) stored in
* @lh in order of discovery, with the list chain stored in base.list
@@ -1725,12 +1728,12 @@ static int compress_loaddata(struct aa_loaddata *data)
* Returns: profile(s) on @lh else error pointer if fails to unpack
*/
int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
- const char **ns)
+ const char **ns, char *compressed_data, size_t compressed_size)
{
struct aa_load_ent *tmp, *ent;
struct aa_profile *profile = NULL;
char *ns_name = NULL;
- int error;
+ int error = 0;
struct aa_ext e = {
.start = udata->data,
.end = udata->data + udata->size,
@@ -1783,10 +1786,23 @@ int aa_unpack(struct aa_loaddata *udata, struct list_head *lh,
}
if (aa_g_export_binary) {
- error = compress_loaddata(udata);
+ /* Do we have userspace-compressed data? */
+ if (compressed_data) {
+ kvfree(udata->data);
+ udata->data = compressed_data;
+ udata->compressed_size = compressed_size;
+ compressed_data = NULL; /* consumed */
+
+ } else
+ error = compress_loaddata(udata);
+
if (error)
goto fail;
+ } else if (compressed_data) {
+ kvfree(compressed_data);
+ compressed_data = NULL;
}
+
return 0;
fail_profile:
@@ -1794,6 +1810,8 @@ fail_profile:
aa_put_profile(profile);
fail:
+ if (compressed_data)
+ kvfree(compressed_data);
list_for_each_entry_safe(ent, tmp, lh, list) {
list_del_init(&ent->list);
aa_load_ent_free(ent);
diff --git a/security/apparmor/task.c b/security/apparmor/task.c
index b9fb3738124e..e16ff4130bc2 100644
--- a/security/apparmor/task.c
+++ b/security/apparmor/task.c
@@ -14,6 +14,7 @@
#include <linux/gfp.h>
#include <linux/ptrace.h>
+#include <linux/task_work.h>
#include "include/path.h"
#include "include/audit.h"
@@ -89,6 +90,32 @@ int aa_replace_current_label(struct aa_label *label)
return 0;
}
+static void aa_replace_stale_label_tw_func(struct callback_head *tw)
+{
+ struct aa_task_ctx *ctx = task_ctx(current);
+ struct aa_label *label;
+
+ ctx->label_replacement_pending = false;
+ label = aa_current_raw_label();
+ if (!label_is_stale(label))
+ return;
+ label = aa_get_newest_label(label);
+ aa_replace_current_label(label);
+ aa_put_label(label);
+}
+
+/* replace the current task's stale label on syscall return */
+void aa_schedule_stale_label_replacement(void)
+{
+ struct aa_task_ctx *ctx = task_ctx(current);
+
+ if (ctx->label_replacement_pending)
+ return;
+ init_task_work(&ctx->label_replacement_tw, aa_replace_stale_label_tw_func);
+ if (task_work_add(current, &ctx->label_replacement_tw, TWA_RESUME) == 0)
+ ctx->label_replacement_pending = true;
+}
+
/**
* aa_set_current_onexec - set the tasks change_profile to happen onexec
diff --git a/security/keys/trusted-keys/trusted_tpm1.c b/security/keys/trusted-keys/trusted_tpm1.c
index 8f57c6111e7e..bf0bf7f36970 100644
--- a/security/keys/trusted-keys/trusted_tpm1.c
+++ b/security/keys/trusted-keys/trusted_tpm1.c
@@ -18,7 +18,6 @@
#include <keys/trusted-type.h>
#include <linux/key-type.h>
#include <linux/tpm.h>
-#include <linux/tpm_command.h>
#include <keys/trusted_tpm.h>
@@ -317,9 +316,8 @@ static int TSS_checkhmac2(unsigned char *buffer,
* For key specific tpm requests, we will generate and send our
* own TPM command packets using the drivers send function.
*/
-static int trusted_tpm_send(unsigned char *cmd, size_t buflen)
+static int trusted_tpm_send(struct tpm_buf *buf)
{
- struct tpm_buf buf;
int rc;
if (!chip)
@@ -329,12 +327,9 @@ static int trusted_tpm_send(unsigned char *cmd, size_t buflen)
if (rc)
return rc;
- buf.flags = 0;
- buf.length = buflen;
- buf.data = cmd;
- dump_tpm_buf(cmd);
- rc = tpm_transmit_cmd(chip, &buf, 4, "sending data");
- dump_tpm_buf(cmd);
+ dump_tpm_buf(buf->data);
+ rc = tpm_transmit_cmd(chip, buf, 4, "sending data");
+ dump_tpm_buf(buf->data);
if (rc > 0)
/* TPM error */
@@ -380,7 +375,7 @@ static int osap(struct tpm_buf *tb, struct osapsess *s,
tpm_buf_append_u32(tb, handle);
tpm_buf_append(tb, ononce, TPM_NONCE_SIZE);
- ret = trusted_tpm_send(tb->data, tb->length);
+ ret = trusted_tpm_send(tb);
if (ret < 0)
return ret;
@@ -404,7 +399,7 @@ static int oiap(struct tpm_buf *tb, uint32_t *handle, unsigned char *nonce)
return -ENODEV;
tpm_buf_reset(tb, TPM_TAG_RQU_COMMAND, TPM_ORD_OIAP);
- ret = trusted_tpm_send(tb->data, tb->length);
+ ret = trusted_tpm_send(tb);
if (ret < 0)
return ret;
@@ -513,7 +508,7 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
tpm_buf_append_u8(tb, cont);
tpm_buf_append(tb, td->pubauth, SHA1_DIGEST_SIZE);
- ret = trusted_tpm_send(tb->data, tb->length);
+ ret = trusted_tpm_send(tb);
if (ret < 0)
goto out;
@@ -604,7 +599,7 @@ static int tpm_unseal(struct tpm_buf *tb,
tpm_buf_append_u8(tb, cont);
tpm_buf_append(tb, authdata2, SHA1_DIGEST_SIZE);
- ret = trusted_tpm_send(tb->data, tb->length);
+ ret = trusted_tpm_send(tb);
if (ret < 0) {
pr_info("authhmac failed (%d)\n", ret);
return ret;
@@ -631,23 +626,23 @@ static int tpm_unseal(struct tpm_buf *tb,
static int key_seal(struct trusted_key_payload *p,
struct trusted_key_options *o)
{
- struct tpm_buf tb;
int ret;
- ret = tpm_buf_init(&tb, 0, 0);
- if (ret)
- return ret;
+ struct tpm_buf *tb __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!tb)
+ return -ENOMEM;
+
+ tpm_buf_init(tb, TPM_BUFSIZE);
/* include migratable flag at end of sealed key */
p->key[p->key_len] = p->migratable;
- ret = tpm_seal(&tb, o->keytype, o->keyhandle, o->keyauth,
+ ret = tpm_seal(tb, o->keytype, o->keyhandle, o->keyauth,
p->key, p->key_len + 1, p->blob, &p->blob_len,
o->blobauth, o->pcrinfo, o->pcrinfo_len);
if (ret < 0)
pr_info("srkseal failed (%d)\n", ret);
- tpm_buf_destroy(&tb);
return ret;
}
@@ -657,14 +652,15 @@ static int key_seal(struct trusted_key_payload *p,
static int key_unseal(struct trusted_key_payload *p,
struct trusted_key_options *o)
{
- struct tpm_buf tb;
int ret;
- ret = tpm_buf_init(&tb, 0, 0);
- if (ret)
- return ret;
+ struct tpm_buf *tb __free(kfree) = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!tb)
+ return -ENOMEM;
+
+ tpm_buf_init(tb, TPM_BUFSIZE);
- ret = tpm_unseal(&tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
+ ret = tpm_unseal(tb, o->keyhandle, o->keyauth, p->blob, p->blob_len,
o->blobauth, p->key, &p->key_len);
if (ret < 0)
pr_info("srkunseal failed (%d)\n", ret);
@@ -672,7 +668,6 @@ static int key_unseal(struct trusted_key_payload *p,
/* pull migratable flag out of sealed key */
p->migratable = p->key[--p->key_len];
- tpm_buf_destroy(&tb);
return ret;
}
diff --git a/security/keys/trusted-keys/trusted_tpm2.c b/security/keys/trusted-keys/trusted_tpm2.c
index 6340823f8b53..67225dd562a9 100644
--- a/security/keys/trusted-keys/trusted_tpm2.c
+++ b/security/keys/trusted-keys/trusted_tpm2.c
@@ -9,7 +9,6 @@
#include <linux/string.h>
#include <linux/err.h>
#include <linux/tpm.h>
-#include <linux/tpm_command.h>
#include <keys/trusted-type.h>
#include <keys/trusted_tpm.h>
@@ -234,7 +233,8 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
struct trusted_key_options *options)
{
off_t offset = TPM_HEADER_SIZE;
- struct tpm_buf buf, sized;
+ struct tpm_buf *buf __free(kfree) = NULL;
+ struct tpm_buf *sized __free(kfree) = NULL;
int blob_len = 0;
int hash;
u32 flags;
@@ -255,97 +255,100 @@ int tpm2_seal_trusted(struct tpm_chip *chip,
if (rc)
goto out_put;
- rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
- if (rc) {
+ buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf) {
+ rc = -ENOMEM;
tpm2_end_auth_session(chip);
goto out_put;
}
- rc = tpm_buf_init_sized(&sized);
- if (rc) {
- tpm_buf_destroy(&buf);
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE);
+
+ sized = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!sized) {
+ rc = -ENOMEM;
tpm2_end_auth_session(chip);
goto out_put;
}
- rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ tpm_buf_init_sized(sized, TPM_BUFSIZE);
+
+ rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
if (rc)
goto out;
- tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_DECRYPT,
+ tpm_buf_append_hmac_session(chip, buf, TPM2_SA_DECRYPT,
options->keyauth, TPM_DIGEST_SIZE);
/* sensitive */
- tpm_buf_append_u16(&sized, options->blobauth_len);
+ tpm_buf_append_u16(sized, options->blobauth_len);
if (options->blobauth_len)
- tpm_buf_append(&sized, options->blobauth, options->blobauth_len);
+ tpm_buf_append(sized, options->blobauth, options->blobauth_len);
- tpm_buf_append_u16(&sized, payload->key_len);
- tpm_buf_append(&sized, payload->key, payload->key_len);
- tpm_buf_append(&buf, sized.data, sized.length);
+ tpm_buf_append_u16(sized, payload->key_len);
+ tpm_buf_append(sized, payload->key, payload->key_len);
+ tpm_buf_append(buf, sized->data, sized->length);
/* public */
- tpm_buf_reset_sized(&sized);
- tpm_buf_append_u16(&sized, TPM_ALG_KEYEDHASH);
- tpm_buf_append_u16(&sized, hash);
+ tpm_buf_reset_sized(sized);
+ tpm_buf_append_u16(sized, TPM_ALG_KEYEDHASH);
+ tpm_buf_append_u16(sized, hash);
/* key properties */
flags = 0;
flags |= options->policydigest_len ? 0 : TPM2_OA_USER_WITH_AUTH;
flags |= payload->migratable ? 0 : (TPM2_OA_FIXED_TPM | TPM2_OA_FIXED_PARENT);
- tpm_buf_append_u32(&sized, flags);
+ tpm_buf_append_u32(sized, flags);
/* policy */
- tpm_buf_append_u16(&sized, options->policydigest_len);
+ tpm_buf_append_u16(sized, options->policydigest_len);
if (options->policydigest_len)
- tpm_buf_append(&sized, options->policydigest, options->policydigest_len);
+ tpm_buf_append(sized, options->policydigest, options->policydigest_len);
/* public parameters */
- tpm_buf_append_u16(&sized, TPM_ALG_NULL);
- tpm_buf_append_u16(&sized, 0);
+ tpm_buf_append_u16(sized, TPM_ALG_NULL);
+ tpm_buf_append_u16(sized, 0);
- tpm_buf_append(&buf, sized.data, sized.length);
+ tpm_buf_append(buf, sized->data, sized->length);
/* outside info */
- tpm_buf_append_u16(&buf, 0);
+ tpm_buf_append_u16(buf, 0);
/* creation PCR */
- tpm_buf_append_u32(&buf, 0);
+ tpm_buf_append_u32(buf, 0);
- if (buf.flags & TPM_BUF_OVERFLOW) {
+ if (buf->flags & TPM_BUF_INVALID) {
rc = -E2BIG;
tpm2_end_auth_session(chip);
goto out;
}
- rc = tpm_buf_fill_hmac_session(chip, &buf);
+ rc = tpm_buf_fill_hmac_session(chip, buf);
if (rc)
goto out;
- rc = tpm_transmit_cmd(chip, &buf, 4, "sealing data");
- rc = tpm_buf_check_hmac_response(chip, &buf, rc);
+ rc = tpm_transmit_cmd(chip, buf, 4, "sealing data");
+ rc = tpm_buf_check_hmac_response(chip, buf, rc);
if (rc)
goto out;
- blob_len = tpm_buf_read_u32(&buf, &offset);
- if (blob_len > MAX_BLOB_SIZE || buf.flags & TPM_BUF_BOUNDARY_ERROR) {
+ blob_len = tpm_buf_read_u32(buf, &offset);
+ if (blob_len > MAX_BLOB_SIZE || buf->flags & TPM_BUF_INVALID) {
rc = -E2BIG;
goto out;
}
- if (buf.length - offset < blob_len) {
+ if (buf->length - offset < blob_len) {
rc = -EFAULT;
goto out;
}
- blob_len = tpm2_key_encode(payload, options, &buf.data[offset], blob_len);
+ blob_len = tpm2_key_encode(payload, options, &buf->data[offset], blob_len);
if (blob_len < 0)
rc = blob_len;
out:
- tpm_buf_destroy(&sized);
- tpm_buf_destroy(&buf);
-
if (!rc)
payload->blob_len = blob_len;
@@ -373,7 +376,7 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
u32 *blob_handle)
{
u8 *blob_ref __free(kfree) = NULL;
- struct tpm_buf buf;
+ struct tpm_buf *buf __free(kfree) = NULL;
unsigned int private_len;
unsigned int public_len;
unsigned int blob_len;
@@ -427,39 +430,38 @@ static int tpm2_load_cmd(struct tpm_chip *chip,
if (rc)
return rc;
- rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
- if (rc) {
+ buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf) {
tpm2_end_auth_session(chip);
- return rc;
+ return -ENOMEM;
}
- rc = tpm_buf_append_name(chip, &buf, options->keyhandle, NULL);
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD);
+
+ rc = tpm_buf_append_name(chip, buf, options->keyhandle, NULL);
if (rc)
- goto out;
+ return rc;
- tpm_buf_append_hmac_session(chip, &buf, 0, options->keyauth,
+ tpm_buf_append_hmac_session(chip, buf, 0, options->keyauth,
TPM_DIGEST_SIZE);
- tpm_buf_append(&buf, blob, blob_len);
+ tpm_buf_append(buf, blob, blob_len);
- if (buf.flags & TPM_BUF_OVERFLOW) {
- rc = -E2BIG;
+ if (buf->flags & TPM_BUF_INVALID) {
tpm2_end_auth_session(chip);
- goto out;
+ return -E2BIG;
}
- rc = tpm_buf_fill_hmac_session(chip, &buf);
+ rc = tpm_buf_fill_hmac_session(chip, buf);
if (rc)
- goto out;
+ return rc;
- rc = tpm_transmit_cmd(chip, &buf, 4, "loading blob");
- rc = tpm_buf_check_hmac_response(chip, &buf, rc);
+ rc = tpm_transmit_cmd(chip, buf, 4, "loading blob");
+ rc = tpm_buf_check_hmac_response(chip, buf, rc);
if (!rc)
*blob_handle = be32_to_cpup(
- (__be32 *) &buf.data[TPM_HEADER_SIZE]);
-
-out:
- tpm_buf_destroy(&buf);
+ (__be32 *)&buf->data[TPM_HEADER_SIZE]);
return tpm_ret_to_err(rc);
}
@@ -482,7 +484,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
u32 blob_handle)
{
struct tpm_header *head;
- struct tpm_buf buf;
+ struct tpm_buf *buf __free(kfree) = NULL;
u16 data_len;
int offset;
u8 *data;
@@ -492,18 +494,21 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
if (rc)
return rc;
- rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
- if (rc) {
+ buf = kzalloc(TPM_BUFSIZE, GFP_KERNEL);
+ if (!buf) {
tpm2_end_auth_session(chip);
- return rc;
+ return -ENOMEM;
}
- rc = tpm_buf_append_name(chip, &buf, blob_handle, NULL);
+ tpm_buf_init(buf, TPM_BUFSIZE);
+ tpm_buf_reset(buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL);
+
+ rc = tpm_buf_append_name(chip, buf, blob_handle, NULL);
if (rc)
- goto out;
+ return rc;
if (!options->policyhandle) {
- tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT,
+ tpm_buf_append_hmac_session(chip, buf, TPM2_SA_ENCRYPT,
options->blobauth,
options->blobauth_len);
} else {
@@ -518,39 +523,36 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
* could repeat our actions with the exfiltrated
* password.
*/
- tpm2_buf_append_auth(&buf, options->policyhandle,
+ tpm2_buf_append_auth(buf, options->policyhandle,
NULL /* nonce */, 0, 0,
options->blobauth, options->blobauth_len);
if (tpm2_chip_auth(chip)) {
- tpm_buf_append_hmac_session(chip, &buf, TPM2_SA_ENCRYPT, NULL, 0);
+ tpm_buf_append_hmac_session(chip, buf, TPM2_SA_ENCRYPT,
+ NULL, 0);
} else {
- offset = buf.handles * 4 + TPM_HEADER_SIZE;
- head = (struct tpm_header *)buf.data;
- if (tpm_buf_length(&buf) == offset)
+ offset = buf->handles * 4 + TPM_HEADER_SIZE;
+ head = (struct tpm_header *)buf->data;
+ if (tpm_buf_length(buf) == offset)
head->tag = cpu_to_be16(TPM2_ST_NO_SESSIONS);
}
}
- rc = tpm_buf_fill_hmac_session(chip, &buf);
+ rc = tpm_buf_fill_hmac_session(chip, buf);
if (rc)
- goto out;
+ return rc;
- rc = tpm_transmit_cmd(chip, &buf, 6, "unsealing");
- rc = tpm_buf_check_hmac_response(chip, &buf, rc);
+ rc = tpm_transmit_cmd(chip, buf, 6, "unsealing");
+ rc = tpm_buf_check_hmac_response(chip, buf, rc);
if (!rc) {
data_len = be16_to_cpup(
- (__be16 *) &buf.data[TPM_HEADER_SIZE + 4]);
- if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE) {
- rc = -EFAULT;
- goto out;
- }
+ (__be16 *)&buf->data[TPM_HEADER_SIZE + 4]);
+ if (data_len < MIN_KEY_SIZE || data_len > MAX_KEY_SIZE)
+ return -EFAULT;
- if (tpm_buf_length(&buf) < TPM_HEADER_SIZE + 6 + data_len) {
- rc = -EFAULT;
- goto out;
- }
- data = &buf.data[TPM_HEADER_SIZE + 6];
+ if (tpm_buf_length(buf) < TPM_HEADER_SIZE + 6 + data_len)
+ return -EFAULT;
+ data = &buf->data[TPM_HEADER_SIZE + 6];
if (payload->old_format) {
/* migratable flag is at the end of the key */
@@ -567,8 +569,6 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
}
}
-out:
- tpm_buf_destroy(&buf);
return tpm_ret_to_err(rc);
}