Something went wrong. Try again.
(I'm so sorry!) Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566/* 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;}