summaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-26 11:04:24 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-26 11:04:24 -0700
commit8fefe68784ae1606e11a5c65c04167c3b95051a0 (patch)
tree191bbdc7d0135d368f225f6e116ed17980bbc574 /security
parent2bdcd6cf2ac4186c3640ac54cfc48c14de80948c (diff)
parent3daad923a8685adb66087e0d819559b7eb6ba975 (diff)
Merge tag 'apparmor-pr-2026-08-26' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor
Pull AppArmor updates from John Johansen: "The biggest functional change is Jann Horn's fix for how aparmor is doing stale cred updates after a policy replacement. apparmor: fix cred UAF caused by begin_current_label_crit_section() It moves the update to be done during task_work at the end of the syscall. One major feature is allowing policy to be compressed in userspace instead of after the fact (in kernel) if we need to hold onto it for CRIU/introspection. The other major change is to do with network mediation. It is a lot of code churn but does not do any functional changes to mediation. It moves the code around, and refactors it to use newer patterns for consistency, and in preparation for some improvements in mediation in a future patchset. Features: - support loading compressed policies - add audit mode to provide a mechanism to silence complain messages - refactor network mediation to use new patterns, and prepare to for extended inet mediation (no functional change) Cleanups: - switch website link to https - make include headers self-contained, and fix circular include - constify aa_label, aa_dfa, aa_profile, and aa_perms paraneters - mark static tables and structs as read only - drop use of _confined variant for iteration - refactory mount to use check_perms - refactor network mediation code to be together - refactor xattr attachment, to take the file path - optimize current_label_crit_section() - leverage audit_log_n_untrustedstring() when possible Bug Fixes: - initialized policy lists heads before fail path - fix deadlock in complain-mode change_hat - auditing of mount binary data - fix error debug output in fn_label_build - fix race condition in label replacement - fix unconfined user namespace restriction forced stack - fix error handling for copy_from_user in policy_update - fix out-of-bounds write when null terminating a label vec - fix integer overflow in verify_tags() bounds check - fix cred UAF caused by begin_current_label_crit_section() - use SEND_SIG_NOINFO instead of NULL in aa_audit()" * tag 'apparmor-pr-2026-08-26' of git://git.kernel.org/pub/scm/linux/kernel/git/jj/linux-apparmor: (40 commits) apparmor: policy_int make sure list heads are initialized before fail path apparmor: fix deadlock in complain-mode change_hat apparmor: constify aa_label parameters on read-only query helpers apparmor: constify aa_dfa parameters on read-only compute paths apparmor: constify aa_profile parameters on read-only compute paths apparmor: constify aa_perms parameters that are read-only apparmor: drop use of _confined variant for iteration apparmor: refactory mount to use check_perms apparmor: fix auditing of mount binary data apparmor: add audit mode to provide a mechanism to silence complain messages apparmor: mark static tables and structs as read only apparmor: fix error debug output in fn_label_build apparmor: make table entry count last enum for static tables apparmor: fix race condition in label replacement apparmor: refactor xattr attachment, to take the file path apparmor: fix unconfined user namespace restriction forced stack apparmor: reserve mediation class for packet mediation apparmor: move sock_rcv_skb() next to inet_conn_request apparmor: move netfilter functions next to the LSM network operations apparmor: refactor network socket mediation to support compatibility ...
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
36 files changed, 1609 insertions, 613 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