selinux: process labeled IPsec TCP SYN-ACK packets properly in selinux_ip_postroute()
authorPaul Moore <pmoore@redhat.com>
Mon, 9 Dec 2013 21:11:53 +0000 (16:11 -0500)
committerPaul Moore <pmoore@redhat.com>
Tue, 10 Dec 2013 19:50:25 +0000 (14:50 -0500)
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>
Cc: stable@vger.kernel.org
Signed-off-by: Paul Moore <pmoore@redhat.com>
security/selinux/hooks.c

index 95cb134..a98228e 100644 (file)
@@ -4846,22 +4846,31 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
         * 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 = selinux_peerlbl_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.
+        * 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 = selinux_peerlbl_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
@@ -4889,6 +4898,25 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex,
                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;