Re: [RFC PATCH] SELinux: Add network ingress and egress control permission checks

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 4 Jan 2008, Paul Moore wrote:

> This patch implements packet ingress/egress controls for SELinux which allow
> SELinux security policy to control the flow of all IPv4 and IPv6 packets into
> and out of the system.  Currently SELinux does not have proper control over
> forwarded packets and this patch corrects this problem.
> 
> Special thanks to Venkat Yekkirala <vyekkirala@xxxxxxxxxxxxx> whose earlier
> work on this topic eventually led to this patch.
> 
> Signed-off-by: Paul Moore <paul.moore@xxxxxx>

Please send this to netdev for review.

> ---
> 
>  security/selinux/hooks.c |  374 ++++++++++++++++++++++++++++++++--------------
>  1 files changed, 260 insertions(+), 114 deletions(-)
> 
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index d16f586..98f2ff6 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -12,8 +12,8 @@
>   *  Copyright (C) 2003 Red Hat, Inc., James Morris <jmorris@xxxxxxxxxx>
>   *  Copyright (C) 2004-2005 Trusted Computer Solutions, Inc.
>   *                          <dgoeddel@xxxxxxxxxxxxx>
> - *  Copyright (C) 2006 Hewlett-Packard Development Company, L.P.
> - *                     Paul Moore, <paul.moore@xxxxxx>
> + *  Copyright (C) 2006, 2007 Hewlett-Packard Development Company, L.P.
> + *                     Paul Moore <paul.moore@xxxxxx>
>   *  Copyright (C) 2007 Hitachi Software Engineering Co., Ltd.
>   *                     Yuichi Nakamura <ynakam@xxxxxxxxxxxxxx>
>   *
> @@ -3608,6 +3608,52 @@ static int selinux_socket_unix_may_send(struct socket *sock,
>  	return 0;
>  }
>  
> +static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family,
> +				    u32 peer_sid,
> +				    struct avc_audit_data *ad)
> +{
> +	int err;
> +	u32 if_sid;
> +	u32 node_sid;
> +
> +	err = sel_netif_sid(ifindex, &if_sid);
> +	if (err)
> +		return err;
> +	err = avc_has_perm(peer_sid, if_sid,
> +			   SECCLASS_NETIF, NETIF__INGRESS, ad);
> +	if (err)
> +		return err;
> +
> +	err = sel_netnode_sid(addrp, family, &node_sid);
> +	if (err)
> +		return err;
> +	return avc_has_perm(peer_sid, node_sid,
> +			    SECCLASS_NODE, NODE__RECVFROM, ad);
> +}
> +
> +static int selinux_inet_sys_snd_skb(int ifindex, char *addrp, u16 family,
> +				    u32 peer_sid,
> +				    struct avc_audit_data *ad)
> +{
> +	int err;
> +	u32 if_sid;
> +	u32 node_sid;
> +
> +	err = sel_netif_sid(ifindex, &if_sid);
> +	if (err)
> +		return err;
> +	err = avc_has_perm(peer_sid, if_sid,
> +			   SECCLASS_NETIF, NETIF__EGRESS, ad);
> +	if (err)
> +		return err;
> +
> +	err = sel_netnode_sid(addrp, family, &node_sid);
> +	if (err)
> +		return err;
> +	return avc_has_perm(peer_sid, node_sid,
> +			    SECCLASS_NODE, NODE__SENDTO, ad);
> +}
> +
>  static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk,
>  						struct sk_buff *skb,
>  						struct avc_audit_data *ad,
> @@ -3748,6 +3794,10 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
>  		err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
>  		if (err)
>  			return err;
> +		err = selinux_inet_sys_rcv_skb(skb->iif, addrp, family,
> +					       peer_sid, &ad);
> +		if (err)
> +			return err;
>  		err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER,
>  				   PEER__RECV, &ad);
>  	}
> @@ -3964,151 +4014,220 @@ out:
>  
>  #ifdef CONFIG_NETFILTER
>  
> -static int selinux_ip_postroute_last_compat(struct sock *sk,
> -					    struct net_device *dev,
> -					    struct avc_audit_data *ad,
> -					    u16 family,
> -					    char *addrp)
> +static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex,
> +				       u16 family)
>  {
> -	int err = 0;
> -	u32 netif_perm, node_perm, node_sid, if_sid, send_perm = 0;
> -	struct socket *sock;
> -	struct inode *inode;
> -	struct inode_security_struct *isec;
> +	if (!selinux_policycap_netpeer)
> +		return NF_ACCEPT;
>  
> -	sock = sk->sk_socket;
> -	if (!sock)
> -		goto out;
> +	if (netlbl_enabled() || selinux_xfrm_enabled()) {
> +		char *addrp;
> +		u32 peer_sid;
> +		struct avc_audit_data ad;
>  
> -	inode = SOCK_INODE(sock);
> -	if (!inode)
> -		goto out;
> +		AVC_AUDIT_DATA_INIT(&ad, NET);
> +		ad.u.net.netif = ifindex;
> +		ad.u.net.family = family;
> +		if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0)
> +			return NF_DROP;
> +
> +		if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0)
> +			return NF_DROP;
> +		if (selinux_inet_sys_rcv_skb(ifindex, addrp, family,
> +					     peer_sid, &ad) != 0)
> +			return NF_DROP;
> +	}
>  
> -	isec = inode->i_security;
> -	
> -	err = sel_netif_sid(dev->ifindex, &if_sid);
> -	if (err)
> -		goto out;
> +	return NF_ACCEPT;
> +}
> +
> +static unsigned int selinux_ipv4_forward(unsigned int hooknum,
> +					 struct sk_buff *skb,
> +					 const struct net_device *in,
> +					 const struct net_device *out,
> +					 int (*okfn)(struct sk_buff *))
> +{
> +	return selinux_ip_forward(skb, in->ifindex, PF_INET);
> +}
> +
> +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
> +static unsigned int selinux_ipv6_forward(unsigned int hooknum,
> +					 struct sk_buff *skb,
> +					 const struct net_device *in,
> +					 const struct net_device *out,
> +					 int (*okfn)(struct sk_buff *))
> +{
> +	return selinux_ip_forward(skb, in->ifindex, PF_INET6);
> +}
> +#endif	/* IPV6 */
> +
> +static int selinux_ip_postroute_iptables_compat(struct sock *sk,
> +						int ifindex,
> +						struct avc_audit_data *ad,
> +						u16 family, char *addrp)
> +{
> +	int err;
> +	struct sk_security_struct *sksec = sk->sk_security;
> +	u16 sk_class;
> +	u32 netif_perm, node_perm, send_perm;
> +	u32 port_sid, node_sid, if_sid, sk_sid;
>  
> -	switch (isec->sclass) {
> +	sk_sid = sksec->sid;
> +	sk_class = sksec->sclass;
> +
> +	switch (sk_class) {
>  	case SECCLASS_UDP_SOCKET:
>  		netif_perm = NETIF__UDP_SEND;
>  		node_perm = NODE__UDP_SEND;
>  		send_perm = UDP_SOCKET__SEND_MSG;
>  		break;
> -	
>  	case SECCLASS_TCP_SOCKET:
>  		netif_perm = NETIF__TCP_SEND;
>  		node_perm = NODE__TCP_SEND;
>  		send_perm = TCP_SOCKET__SEND_MSG;
>  		break;
> -
>  	case SECCLASS_DCCP_SOCKET:
>  		netif_perm = NETIF__DCCP_SEND;
>  		node_perm = NODE__DCCP_SEND;
>  		send_perm = DCCP_SOCKET__SEND_MSG;
>  		break;
> -
>  	default:
>  		netif_perm = NETIF__RAWIP_SEND;
>  		node_perm = NODE__RAWIP_SEND;
> +		send_perm = 0;
>  		break;
>  	}
>  
> -	err = avc_has_perm(isec->sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
> +	err = sel_netif_sid(ifindex, &if_sid);
>  	if (err)
> -		goto out;
> +		return err;
> +	err = avc_has_perm(sk_sid, if_sid, SECCLASS_NETIF, netif_perm, ad);
> +	if (err)
> +		return err;
>  		
>  	err = sel_netnode_sid(addrp, family, &node_sid);
>  	if (err)
> -		goto out;
> -	
> -	err = avc_has_perm(isec->sid, node_sid, SECCLASS_NODE, node_perm, ad);
> +		return err;
> +	err = avc_has_perm(sk_sid, node_sid, SECCLASS_NODE, node_perm, ad);
>  	if (err)
> -		goto out;
> +		return err;
>  
> -	if (send_perm) {
> -		u32 port_sid;
> -		
> -		err = security_port_sid(sk->sk_family,
> -		                        sk->sk_type,
> -		                        sk->sk_protocol,
> -		                        ntohs(ad->u.net.dport),
> -		                        &port_sid);
> -		if (err)
> -			goto out;
> +	if (!send_perm)
> +		return 0;
> +	err = security_port_sid(sk->sk_family, sk->sk_type,
> +				sk->sk_protocol, ntohs(ad->u.net.dport),
> +				&port_sid);
> +	if (err)
> +		return err;
> +	return avc_has_perm(sk_sid, port_sid, sk_class, send_perm, ad);
> +}
> +
> +static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
> +						int ifindex,
> +						struct avc_audit_data *ad,
> +						u16 family,
> +						char *addrp,
> +						u8 proto)
> +{
> +	int err;
> +	struct sock *sk = skb->sk;
> +	struct sk_security_struct *sksec;
>  
> -		err = avc_has_perm(isec->sid, port_sid, isec->sclass,
> -				   send_perm, ad);
> +	if (sk == NULL)
> +		return NF_ACCEPT;
> +	sksec = sk->sk_security;
> +
> +	if (selinux_compat_net)
> +		err = selinux_ip_postroute_iptables_compat(skb->sk, ifindex,
> +							   ad, family, addrp);
> +	else
> +		err = avc_has_perm(sksec->sid, skb->secmark,
> +				   SECCLASS_PACKET, PACKET__SEND, ad);
> +	if (err)
> +		return NF_DROP;
> +
> +	if (!selinux_policycap_netpeer) {
> +		err = selinux_xfrm_postroute_last(sksec->sid, skb, ad, proto);
> +		if (err)
> +			return NF_DROP;
>  	}
> -out:
> -	return err;
> +
> +	return NF_ACCEPT;
>  }
>  
> -static unsigned int selinux_ip_postroute_last(unsigned int hooknum,
> -                                              struct sk_buff *skb,
> -                                              const struct net_device *in,
> -                                              const struct net_device *out,
> -                                              int (*okfn)(struct sk_buff *),
> -                                              u16 family)
> +static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
> +					 u16 family)
>  {
> -	char *addrp;
> -	int err = 0;
> -	struct sock *sk;
> +	int err;
>  	struct avc_audit_data ad;
> -	struct net_device *dev = (struct net_device *)out;
> -	struct sk_security_struct *sksec;
> +	char *addrp;
>  	u8 proto;
>  
> -	sk = skb->sk;
> -	if (!sk)
> -		goto out;
> -
> -	sksec = sk->sk_security;
> -
>  	AVC_AUDIT_DATA_INIT(&ad, NET);
> -	ad.u.net.netif = dev->ifindex;
> +	ad.u.net.netif = ifindex;
>  	ad.u.net.family = family;
> -
>  	err = selinux_parse_skb(skb, &ad, &addrp, 0, &proto);
>  	if (err)
> -		goto out;
> +		return NF_DROP;
>  
> -	if (selinux_compat_net)
> -		err = selinux_ip_postroute_last_compat(sk, dev, &ad,
> -						       family, addrp);
> -	else
> -		err = avc_has_perm(sksec->sid, skb->secmark, SECCLASS_PACKET,
> -				   PACKET__SEND, &ad);
> +	/* If any sort of compatibility mode is enabled then handoff processing
> +	 * to the selinux_ip_postroute_compat() function to deal with the
> +	 * special handling.  We do this in an attempt to keep this function
> +	 * as fast and as clean as possible. */
> +	if (selinux_compat_net || !selinux_policycap_netpeer)
> +		return selinux_ip_postroute_compat(skb, ifindex, &ad,
> +						   family, addrp, proto);
>  
> -	if (err)
> -		goto out;
> +	/* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec
> +	 * packet transformation so allow the packet to pass without any checks
> +	 * since we'll have another chance to perform access control checks
> +	 * when the packet is on it's final way out. */
> +	if (skb->dst->xfrm != NULL)
> +		return NF_ACCEPT;
>  
> -	err = selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto);
> -out:
> -	return err ? NF_DROP : NF_ACCEPT;
> +	if (netlbl_enabled() || selinux_xfrm_enabled()) {
> +		u32 peer_sid;
> +		struct sock *sk = skb->sk;
> +
> +		if (sk) {
> +			/* packet was locally generated, get the peer label
> +			 * from the originating socket */
> +			struct sk_security_struct *sksec = sk->sk_security;
> +			peer_sid = sksec->sid;
> +		} else {
> +			/* packet was generated by another host, get the peer
> +			 * label directly from the packet */
> +			err = selinux_skb_peerlbl_sid(skb, family, &peer_sid);
> +			if (err)
> +				return NF_DROP;
> +		}
> +		err = selinux_inet_sys_snd_skb(ifindex, addrp, family,
> +					       peer_sid, &ad);
> +		if (err)
> +			return NF_DROP;
> +	}
> +
> +	return NF_ACCEPT;
>  }
>  
> -static unsigned int selinux_ipv4_postroute_last(unsigned int hooknum,
> -						struct sk_buff *skb,
> -						const struct net_device *in,
> -						const struct net_device *out,
> -						int (*okfn)(struct sk_buff *))
> +static unsigned int selinux_ipv4_postroute(unsigned int hooknum,
> +					   struct sk_buff *skb,
> +					   const struct net_device *in,
> +					   const struct net_device *out,
> +					   int (*okfn)(struct sk_buff *))
>  {
> -	return selinux_ip_postroute_last(hooknum, skb, in, out, okfn, PF_INET);
> +	return selinux_ip_postroute(skb, out->ifindex, PF_INET);
>  }
>  
>  #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
> -
> -static unsigned int selinux_ipv6_postroute_last(unsigned int hooknum,
> -						struct sk_buff *skb,
> -						const struct net_device *in,
> -						const struct net_device *out,
> -						int (*okfn)(struct sk_buff *))
> +static unsigned int selinux_ipv6_postroute(unsigned int hooknum,
> +					   struct sk_buff *skb,
> +					   const struct net_device *in,
> +					   const struct net_device *out,
> +					   int (*okfn)(struct sk_buff *))
>  {
> -	return selinux_ip_postroute_last(hooknum, skb, in, out, okfn, PF_INET6);
> +	return selinux_ip_postroute(skb, out->ifindex, PF_INET6);
>  }
> -
>  #endif	/* IPV6 */
>  
>  #endif	/* CONFIG_NETFILTER */
> @@ -5093,22 +5212,40 @@ security_initcall(selinux_init);
>  
>  #if defined(CONFIG_NETFILTER)
>  
> -static struct nf_hook_ops selinux_ipv4_op = {
> -	.hook =		selinux_ipv4_postroute_last,
> -	.owner =	THIS_MODULE,
> -	.pf =		PF_INET,
> -	.hooknum =	NF_IP_POST_ROUTING,
> -	.priority =	NF_IP_PRI_SELINUX_LAST,
> +static struct nf_hook_ops selinux_ipv4_ops[] = {
> +	{
> +		.hook =		selinux_ipv4_postroute,
> +		.owner =	THIS_MODULE,
> +		.pf =		PF_INET,
> +		.hooknum =	NF_IP_POST_ROUTING,
> +		.priority =	NF_IP_PRI_SELINUX_LAST,
> +	},
> +	{
> +		.hook =		selinux_ipv4_forward,
> +		.owner =	THIS_MODULE,
> +		.pf =		PF_INET,
> +		.hooknum =	NF_IP_FORWARD,
> +		.priority =	NF_IP_PRI_SELINUX_FIRST,
> +	}
>  };
>  
>  #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
>  
> -static struct nf_hook_ops selinux_ipv6_op = {
> -	.hook =		selinux_ipv6_postroute_last,
> -	.owner =	THIS_MODULE,
> -	.pf =		PF_INET6,
> -	.hooknum =	NF_IP6_POST_ROUTING,
> -	.priority =	NF_IP6_PRI_SELINUX_LAST,
> +static struct nf_hook_ops selinux_ipv6_ops[] = {
> +	{
> +		.hook =		selinux_ipv6_postroute,
> +		.owner =	THIS_MODULE,
> +		.pf =		PF_INET6,
> +		.hooknum =	NF_IP6_POST_ROUTING,
> +		.priority =	NF_IP6_PRI_SELINUX_LAST,
> +	},
> +	{
> +		.hook =		selinux_ipv6_forward,
> +		.owner =	THIS_MODULE,
> +		.pf =		PF_INET6,
> +		.hooknum =	NF_IP6_FORWARD,
> +		.priority =	NF_IP6_PRI_SELINUX_FIRST,
> +	}
>  };
>  
>  #endif	/* IPV6 */
> @@ -5116,22 +5253,27 @@ static struct nf_hook_ops selinux_ipv6_op = {
>  static int __init selinux_nf_ip_init(void)
>  {
>  	int err = 0;
> +	u32 iter;
>  
>  	if (!selinux_enabled)
>  		goto out;
>  
>  	printk(KERN_DEBUG "SELinux:  Registering netfilter hooks\n");
>  
> -	err = nf_register_hook(&selinux_ipv4_op);
> -	if (err)
> -		panic("SELinux: nf_register_hook for IPv4: error %d\n", err);
> +	for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++) {
> +		err = nf_register_hook(&selinux_ipv4_ops[iter]);
> +		if (err)
> +			panic("SELinux: nf_register_hook for IPv4: error %d\n",
> +			      err);
> +	}
>  
>  #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
> -
> -	err = nf_register_hook(&selinux_ipv6_op);
> -	if (err)
> -		panic("SELinux: nf_register_hook for IPv6: error %d\n", err);
> -
> +	for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++) {
> +		err = nf_register_hook(&selinux_ipv6_ops[iter]);
> +		if (err)
> +			panic("SELinux: nf_register_hook for IPv6: error %d\n",
> +			      err);
> +	}
>  #endif	/* IPV6 */
>  
>  out:
> @@ -5143,11 +5285,15 @@ __initcall(selinux_nf_ip_init);
>  #ifdef CONFIG_SECURITY_SELINUX_DISABLE
>  static void selinux_nf_ip_exit(void)
>  {
> +	u32 iter;
> +
>  	printk(KERN_DEBUG "SELinux:  Unregistering netfilter hooks\n");
>  
> -	nf_unregister_hook(&selinux_ipv4_op);
> +	for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++)
> +		nf_unregister_hook(&selinux_ipv4_ops[iter]);
>  #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
> -	nf_unregister_hook(&selinux_ipv6_op);
> +	for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++)
> +		nf_unregister_hook(&selinux_ipv6_ops[iter]);
>  #endif	/* IPV6 */
>  }
>  #endif
> 
> 
> --
> This message was distributed to subscribers of the selinux mailing list.
> If you no longer wish to subscribe, send mail to majordomo@xxxxxxxxxxxxx with
> the words "unsubscribe selinux" without quotes as the message.
> 

-- 
James Morris
<jmorris@xxxxxxxxx>

--
This message was distributed to subscribers of the selinux mailing list.
If you no longer wish to subscribe, send mail to majordomo@xxxxxxxxxxxxx with
the words "unsubscribe selinux" without quotes as the message.

[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux