| From c0828e50485932b7e019df377a6b0a8d1ebd3080 Mon Sep 17 00:00:00 2001 |
| From: Paul Moore <pmoore@redhat.com> |
| Date: Tue, 10 Dec 2013 14:58:01 -0500 |
| Subject: selinux: process labeled IPsec TCP SYN-ACK packets properly in selinux_ip_postroute() |
| |
| From: Paul Moore <pmoore@redhat.com> |
| |
| commit c0828e50485932b7e019df377a6b0a8d1ebd3080 upstream. |
| |
| Due to difficulty in arriving at the proper security label for |
| TCP SYN-ACK packets in selinux_ip_postroute(), we need to check packets |
| while/before they are undergoing XFRM transforms instead of waiting |
| until afterwards so that we can determine the correct security label. |
| |
| Reported-by: Janak Desai <Janak.Desai@gtri.gatech.edu> |
| Signed-off-by: Paul Moore <pmoore@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| security/selinux/hooks.c | 43 ++++++++++++++++++++++++++++++++++++------- |
| 1 file changed, 36 insertions(+), 7 deletions(-) |
| |
| --- a/security/selinux/hooks.c |
| +++ b/security/selinux/hooks.c |
| @@ -4729,22 +4729,32 @@ static unsigned int selinux_ip_postroute |
| * as fast and as clean as possible. */ |
| if (!selinux_policycap_netpeer) |
| return selinux_ip_postroute_compat(skb, ifindex, family); |
| + |
| + secmark_active = selinux_secmark_enabled(); |
| + peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled(); |
| + if (!secmark_active && !peerlbl_active) |
| + return NF_ACCEPT; |
| + |
| + sk = skb->sk; |
| + |
| #ifdef CONFIG_XFRM |
| /* 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. |
| * NOTE: there appear to be some IPv6 multicast cases where skb->dst |
| - * is NULL, in this case go ahead and apply access control. */ |
| - if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL) |
| + * is NULL, in this case go ahead and apply access control. |
| + * is NULL, in this case go ahead and apply access control. |
| + * NOTE: if this is a local socket (skb->sk != NULL) that is in the |
| + * TCP listening state we cannot wait until the XFRM processing |
| + * is done as we will miss out on the SA label if we do; |
| + * unfortunately, this means more work, but it is only once per |
| + * connection. */ |
| + if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL && |
| + !(sk != NULL && sk->sk_state == TCP_LISTEN)) |
| return NF_ACCEPT; |
| #endif |
| - secmark_active = selinux_secmark_enabled(); |
| - peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled(); |
| - if (!secmark_active && !peerlbl_active) |
| - return NF_ACCEPT; |
| |
| - sk = skb->sk; |
| if (sk == NULL) { |
| /* Without an associated socket the packet is either coming |
| * from the kernel or it is being forwarded; check the packet |
| @@ -4772,6 +4782,25 @@ static unsigned int selinux_ip_postroute |
| struct sk_security_struct *sksec = sk->sk_security; |
| if (selinux_skb_peerlbl_sid(skb, family, &skb_sid)) |
| return NF_DROP; |
| + /* At this point, if the returned skb peerlbl is SECSID_NULL |
| + * and the packet has been through at least one XFRM |
| + * transformation then we must be dealing with the "final" |
| + * form of labeled IPsec packet; since we've already applied |
| + * all of our access controls on this packet we can safely |
| + * pass the packet. */ |
| + if (skb_sid == SECSID_NULL) { |
| + switch (family) { |
| + case PF_INET: |
| + if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) |
| + return NF_ACCEPT; |
| + break; |
| + case PF_INET6: |
| + if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) |
| + return NF_ACCEPT; |
| + default: |
| + return NF_DROP_ERR(-ECONNREFUSED); |
| + } |
| + } |
| if (selinux_conn_sid(sksec->sid, skb_sid, &peer_sid)) |
| return NF_DROP; |
| secmark_perm = PACKET__SEND; |