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