diff --git a/security/apparmor/af_inet.c b/security/apparmor/af_inet.c index 780c905b029f..516c9016537c 100644 --- a/security/apparmor/af_inet.c +++ b/security/apparmor/af_inet.c @@ -21,61 +21,545 @@ #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) { - return aa_af_perm(current_cred(), label, OP_CREATE, - AA_MAY_CREATE, family, type, - 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) { - return aa_sk_perm(OP_BIND, AA_MAY_BIND, sock->sk); + 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) { - return aa_sk_perm(OP_CONNECT, AA_MAY_CONNECT, sock->sk); + 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) { - return aa_sk_perm(OP_LISTEN, AA_MAY_LISTEN, sock->sk); + 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) { - return aa_sk_perm(OP_ACCEPT, AA_MAY_ACCEPT, sock->sk); + 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) { - return aa_sk_perm(op, request, sock->sk); + 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) { - return aa_sk_perm(op, request, sock->sk); + 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)); } -/* revaliation, get/set attr/getsockname/peername */ +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) { - return aa_sk_perm(op, request, sock->sk); + 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) { - return aa_label_sk_perm(subj_cred, label, op, request, sock->sk); + 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/audit.c b/security/apparmor/audit.c index 15e42e96b163..f22dca9b0cc3 100644 --- a/security/apparmor/audit.c +++ b/security/apparmor/audit.c @@ -52,7 +52,7 @@ static const char *const aa_class_names[] = { "unknown", "unknown", "net", - "unknown", + "netv9", "label", "posix_mqueue", "io_uring",