565 lines · 15.5 KB · C
RawDownload
/* 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; 

Lines 1–500 of 565.