]> Pileus Git - ~andy/linux/commitdiff
Merge branch 'master' of git://git.infradead.org/users/pcmoore/selinux_fixes into...
authorJames Morris <james.l.morris@oracle.com>
Fri, 13 Dec 2013 02:27:55 +0000 (13:27 +1100)
committerJames Morris <james.l.morris@oracle.com>
Fri, 13 Dec 2013 02:27:55 +0000 (13:27 +1100)
1  2 
security/selinux/hooks.c

diff --combined security/selinux/hooks.c
index 794c3ca49eac92998caa17be71a4bdc472c2e9c8,6db2e589a1f30798747bb386b78d5e75545cc355..bff7791bb65b0f1f59a61414f1f555b3d0f18bfe
@@@ -53,6 -53,7 +53,7 @@@
  #include <net/ip.h>           /* for local_port_range[] */
  #include <net/sock.h>
  #include <net/tcp.h>          /* struct or_callable used in sock_rcv_skb */
+ #include <net/inet_connection_sock.h>
  #include <net/net_namespace.h>
  #include <net/netlabel.h>
  #include <linux/uaccess.h>
@@@ -3828,7 -3829,7 +3829,7 @@@ static int selinux_skb_peerlbl_sid(stru
        u32 nlbl_sid;
        u32 nlbl_type;
  
-       err = selinux_skb_xfrm_sid(skb, &xfrm_sid);
+       err = selinux_xfrm_skb_sid(skb, &xfrm_sid);
        if (unlikely(err))
                return -EACCES;
        err = selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid);
        return 0;
  }
  
+ /**
+  * selinux_conn_sid - Determine the child socket label for a connection
+  * @sk_sid: the parent socket's SID
+  * @skb_sid: the packet's SID
+  * @conn_sid: the resulting connection SID
+  *
+  * If @skb_sid is valid then the user:role:type information from @sk_sid is
+  * combined with the MLS information from @skb_sid in order to create
+  * @conn_sid.  If @skb_sid is not valid then then @conn_sid is simply a copy
+  * of @sk_sid.  Returns zero on success, negative values on failure.
+  *
+  */
+ static int selinux_conn_sid(u32 sk_sid, u32 skb_sid, u32 *conn_sid)
+ {
+       int err = 0;
+       if (skb_sid != SECSID_NULL)
+               err = security_sid_mls_copy(sk_sid, skb_sid, conn_sid);
+       else
+               *conn_sid = sk_sid;
+       return err;
+ }
  /* socket security operations */
  
  static int socket_sockcreate_sid(const struct task_security_struct *tsec,
@@@ -3969,7 -3994,7 +3994,7 @@@ static int selinux_socket_bind(struct s
                if (snum) {
                        int low, high;
  
 -                      inet_get_local_port_range(&low, &high);
 +                      inet_get_local_port_range(sock_net(sk), &low, &high);
  
                        if (snum < max(PROT_SOCK, low) || snum > high) {
                                err = sel_netport_sid(sk->sk_protocol,
@@@ -4452,7 -4477,7 +4477,7 @@@ static int selinux_inet_conn_request(st
        struct sk_security_struct *sksec = sk->sk_security;
        int err;
        u16 family = sk->sk_family;
-       u32 newsid;
+       u32 connsid;
        u32 peersid;
  
        /* handle mapped IPv4 packets arriving via IPv6 sockets */
        err = selinux_skb_peerlbl_sid(skb, family, &peersid);
        if (err)
                return err;
-       if (peersid == SECSID_NULL) {
-               req->secid = sksec->sid;
-               req->peer_secid = SECSID_NULL;
-       } else {
-               err = security_sid_mls_copy(sksec->sid, peersid, &newsid);
-               if (err)
-                       return err;
-               req->secid = newsid;
-               req->peer_secid = peersid;
-       }
+       err = selinux_conn_sid(sksec->sid, peersid, &connsid);
+       if (err)
+               return err;
+       req->secid = connsid;
+       req->peer_secid = peersid;
  
        return selinux_netlbl_inet_conn_request(req, family);
  }
@@@ -4708,7 -4728,7 +4728,7 @@@ static unsigned int selinux_ip_forward(
        return NF_ACCEPT;
  }
  
 -static unsigned int selinux_ipv4_forward(unsigned int hooknum,
 +static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops,
                                         struct sk_buff *skb,
                                         const struct net_device *in,
                                         const struct net_device *out,
  }
  
  #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 -static unsigned int selinux_ipv6_forward(unsigned int hooknum,
 +static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops,
                                         struct sk_buff *skb,
                                         const struct net_device *in,
                                         const struct net_device *out,
  static unsigned int selinux_ip_output(struct sk_buff *skb,
                                      u16 family)
  {
+       struct sock *sk;
        u32 sid;
  
        if (!netlbl_enabled())
        /* we do this in the LOCAL_OUT path and not the POST_ROUTING path
         * because we want to make sure we apply the necessary labeling
         * before IPsec is applied so we can leverage AH protection */
-       if (skb->sk) {
-               struct sk_security_struct *sksec = skb->sk->sk_security;
+       sk = skb->sk;
+       if (sk) {
+               struct sk_security_struct *sksec;
+               if (sk->sk_state == TCP_LISTEN)
+                       /* if the socket is the listening state then this
+                        * packet is a SYN-ACK packet which means it needs to
+                        * be labeled based on the connection/request_sock and
+                        * not the parent socket.  unfortunately, we can't
+                        * lookup the request_sock yet as it isn't queued on
+                        * the parent socket until after the SYN-ACK is sent.
+                        * the "solution" is to simply pass the packet as-is
+                        * as any IP option based labeling should be copied
+                        * from the initial connection request (in the IP
+                        * layer).  it is far from ideal, but until we get a
+                        * security label in the packet itself this is the
+                        * best we can do. */
+                       return NF_ACCEPT;
+               /* standard practice, label using the parent socket */
+               sksec = sk->sk_security;
                sid = sksec->sid;
        } else
                sid = SECINITSID_KERNEL;
        return NF_ACCEPT;
  }
  
 -static unsigned int selinux_ipv4_output(unsigned int hooknum,
 +static unsigned int selinux_ipv4_output(const struct nf_hook_ops *ops,
                                        struct sk_buff *skb,
                                        const struct net_device *in,
                                        const struct net_device *out,
@@@ -4810,27 -4850,36 +4850,36 @@@ static unsigned int selinux_ip_postrout
         * 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;
  
-       /* if the packet is being forwarded then get the peer label from the
-        * packet itself; otherwise check to see if it is from a local
-        * application or the kernel, if from an application get the peer label
-        * from the sending socket, otherwise use the kernel's sid */
-       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
+                * to determine which and if the packet is being forwarded
+                * query the packet directly to determine the security label. */
                if (skb->skb_iif) {
                        secmark_perm = PACKET__FORWARD_OUT;
                        if (selinux_skb_peerlbl_sid(skb, family, &peer_sid))
                        secmark_perm = PACKET__SEND;
                        peer_sid = SECINITSID_KERNEL;
                }
+       } else if (sk->sk_state == TCP_LISTEN) {
+               /* Locally generated packet but the associated socket is in the
+                * listening state which means this is a SYN-ACK packet.  In
+                * this particular case the correct security label is assigned
+                * to the connection/request_sock but unfortunately we can't
+                * query the request_sock as it isn't queued on the parent
+                * socket until after the SYN-ACK packet is sent; the only
+                * viable choice is to regenerate the label like we do in
+                * selinux_inet_conn_request().  See also selinux_ip_output()
+                * for similar problems. */
+               u32 skb_sid;
+               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;
        } else {
+               /* Locally generated packet, fetch the security label from the
+                * associated socket. */
                struct sk_security_struct *sksec = sk->sk_security;
                peer_sid = sksec->sid;
                secmark_perm = PACKET__SEND;
        return NF_ACCEPT;
  }
  
 -static unsigned int selinux_ipv4_postroute(unsigned int hooknum,
 +static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops,
                                           struct sk_buff *skb,
                                           const struct net_device *in,
                                           const struct net_device *out,
  }
  
  #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 -static unsigned int selinux_ipv6_postroute(unsigned int hooknum,
 +static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops,
                                           struct sk_buff *skb,
                                           const struct net_device *in,
                                           const struct net_device *out,