diff options
author | James Morris <james.l.morris@oracle.com> | 2013-12-12 21:27:55 -0500 |
---|---|---|
committer | James Morris <james.l.morris@oracle.com> | 2013-12-12 21:27:55 -0500 |
commit | d93aca6050b10cd7d8b491637c3b5344c5680cac (patch) | |
tree | 216cf72ea9fc6e7e1f995029cf5ead4a4aeb3301 /security/selinux/hooks.c | |
parent | 54fb723cc48db2fde964fb9bb0eaaccf2cf31a9f (diff) | |
parent | c0828e50485932b7e019df377a6b0a8d1ebd3080 (diff) |
Merge branch 'master' of git://git.infradead.org/users/pcmoore/selinux_fixes into for-linus
Diffstat (limited to 'security/selinux/hooks.c')
-rw-r--r-- | security/selinux/hooks.c | 137 |
1 files changed, 112 insertions, 25 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 794c3ca49eac..bff7791bb65b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -53,6 +53,7 @@ | |||
53 | #include <net/ip.h> /* for local_port_range[] */ | 53 | #include <net/ip.h> /* for local_port_range[] */ |
54 | #include <net/sock.h> | 54 | #include <net/sock.h> |
55 | #include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */ | 55 | #include <net/tcp.h> /* struct or_callable used in sock_rcv_skb */ |
56 | #include <net/inet_connection_sock.h> | ||
56 | #include <net/net_namespace.h> | 57 | #include <net/net_namespace.h> |
57 | #include <net/netlabel.h> | 58 | #include <net/netlabel.h> |
58 | #include <linux/uaccess.h> | 59 | #include <linux/uaccess.h> |
@@ -3828,7 +3829,7 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) | |||
3828 | u32 nlbl_sid; | 3829 | u32 nlbl_sid; |
3829 | u32 nlbl_type; | 3830 | u32 nlbl_type; |
3830 | 3831 | ||
3831 | err = selinux_skb_xfrm_sid(skb, &xfrm_sid); | 3832 | err = selinux_xfrm_skb_sid(skb, &xfrm_sid); |
3832 | if (unlikely(err)) | 3833 | if (unlikely(err)) |
3833 | return -EACCES; | 3834 | return -EACCES; |
3834 | err = selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid); | 3835 | err = selinux_netlbl_skbuff_getsid(skb, family, &nlbl_type, &nlbl_sid); |
@@ -3846,6 +3847,30 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) | |||
3846 | return 0; | 3847 | return 0; |
3847 | } | 3848 | } |
3848 | 3849 | ||
3850 | /** | ||
3851 | * selinux_conn_sid - Determine the child socket label for a connection | ||
3852 | * @sk_sid: the parent socket's SID | ||
3853 | * @skb_sid: the packet's SID | ||
3854 | * @conn_sid: the resulting connection SID | ||
3855 | * | ||
3856 | * If @skb_sid is valid then the user:role:type information from @sk_sid is | ||
3857 | * combined with the MLS information from @skb_sid in order to create | ||
3858 | * @conn_sid. If @skb_sid is not valid then then @conn_sid is simply a copy | ||
3859 | * of @sk_sid. Returns zero on success, negative values on failure. | ||
3860 | * | ||
3861 | */ | ||
3862 | static int selinux_conn_sid(u32 sk_sid, u32 skb_sid, u32 *conn_sid) | ||
3863 | { | ||
3864 | int err = 0; | ||
3865 | |||
3866 | if (skb_sid != SECSID_NULL) | ||
3867 | err = security_sid_mls_copy(sk_sid, skb_sid, conn_sid); | ||
3868 | else | ||
3869 | *conn_sid = sk_sid; | ||
3870 | |||
3871 | return err; | ||
3872 | } | ||
3873 | |||
3849 | /* socket security operations */ | 3874 | /* socket security operations */ |
3850 | 3875 | ||
3851 | static int socket_sockcreate_sid(const struct task_security_struct *tsec, | 3876 | static int socket_sockcreate_sid(const struct task_security_struct *tsec, |
@@ -4452,7 +4477,7 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, | |||
4452 | struct sk_security_struct *sksec = sk->sk_security; | 4477 | struct sk_security_struct *sksec = sk->sk_security; |
4453 | int err; | 4478 | int err; |
4454 | u16 family = sk->sk_family; | 4479 | u16 family = sk->sk_family; |
4455 | u32 newsid; | 4480 | u32 connsid; |
4456 | u32 peersid; | 4481 | u32 peersid; |
4457 | 4482 | ||
4458 | /* handle mapped IPv4 packets arriving via IPv6 sockets */ | 4483 | /* handle mapped IPv4 packets arriving via IPv6 sockets */ |
@@ -4462,16 +4487,11 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, | |||
4462 | err = selinux_skb_peerlbl_sid(skb, family, &peersid); | 4487 | err = selinux_skb_peerlbl_sid(skb, family, &peersid); |
4463 | if (err) | 4488 | if (err) |
4464 | return err; | 4489 | return err; |
4465 | if (peersid == SECSID_NULL) { | 4490 | err = selinux_conn_sid(sksec->sid, peersid, &connsid); |
4466 | req->secid = sksec->sid; | 4491 | if (err) |
4467 | req->peer_secid = SECSID_NULL; | 4492 | return err; |
4468 | } else { | 4493 | req->secid = connsid; |
4469 | err = security_sid_mls_copy(sksec->sid, peersid, &newsid); | 4494 | req->peer_secid = peersid; |
4470 | if (err) | ||
4471 | return err; | ||
4472 | req->secid = newsid; | ||
4473 | req->peer_secid = peersid; | ||
4474 | } | ||
4475 | 4495 | ||
4476 | return selinux_netlbl_inet_conn_request(req, family); | 4496 | return selinux_netlbl_inet_conn_request(req, family); |
4477 | } | 4497 | } |
@@ -4731,6 +4751,7 @@ static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops, | |||
4731 | static unsigned int selinux_ip_output(struct sk_buff *skb, | 4751 | static unsigned int selinux_ip_output(struct sk_buff *skb, |
4732 | u16 family) | 4752 | u16 family) |
4733 | { | 4753 | { |
4754 | struct sock *sk; | ||
4734 | u32 sid; | 4755 | u32 sid; |
4735 | 4756 | ||
4736 | if (!netlbl_enabled()) | 4757 | if (!netlbl_enabled()) |
@@ -4739,8 +4760,27 @@ static unsigned int selinux_ip_output(struct sk_buff *skb, | |||
4739 | /* we do this in the LOCAL_OUT path and not the POST_ROUTING path | 4760 | /* we do this in the LOCAL_OUT path and not the POST_ROUTING path |
4740 | * because we want to make sure we apply the necessary labeling | 4761 | * because we want to make sure we apply the necessary labeling |
4741 | * before IPsec is applied so we can leverage AH protection */ | 4762 | * before IPsec is applied so we can leverage AH protection */ |
4742 | if (skb->sk) { | 4763 | sk = skb->sk; |
4743 | struct sk_security_struct *sksec = skb->sk->sk_security; | 4764 | if (sk) { |
4765 | struct sk_security_struct *sksec; | ||
4766 | |||
4767 | if (sk->sk_state == TCP_LISTEN) | ||
4768 | /* if the socket is the listening state then this | ||
4769 | * packet is a SYN-ACK packet which means it needs to | ||
4770 | * be labeled based on the connection/request_sock and | ||
4771 | * not the parent socket. unfortunately, we can't | ||
4772 | * lookup the request_sock yet as it isn't queued on | ||
4773 | * the parent socket until after the SYN-ACK is sent. | ||
4774 | * the "solution" is to simply pass the packet as-is | ||
4775 | * as any IP option based labeling should be copied | ||
4776 | * from the initial connection request (in the IP | ||
4777 | * layer). it is far from ideal, but until we get a | ||
4778 | * security label in the packet itself this is the | ||
4779 | * best we can do. */ | ||
4780 | return NF_ACCEPT; | ||
4781 | |||
4782 | /* standard practice, label using the parent socket */ | ||
4783 | sksec = sk->sk_security; | ||
4744 | sid = sksec->sid; | 4784 | sid = sksec->sid; |
4745 | } else | 4785 | } else |
4746 | sid = SECINITSID_KERNEL; | 4786 | sid = SECINITSID_KERNEL; |
@@ -4810,27 +4850,36 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, | |||
4810 | * as fast and as clean as possible. */ | 4850 | * as fast and as clean as possible. */ |
4811 | if (!selinux_policycap_netpeer) | 4851 | if (!selinux_policycap_netpeer) |
4812 | return selinux_ip_postroute_compat(skb, ifindex, family); | 4852 | return selinux_ip_postroute_compat(skb, ifindex, family); |
4853 | |||
4854 | secmark_active = selinux_secmark_enabled(); | ||
4855 | peerlbl_active = selinux_peerlbl_enabled(); | ||
4856 | if (!secmark_active && !peerlbl_active) | ||
4857 | return NF_ACCEPT; | ||
4858 | |||
4859 | sk = skb->sk; | ||
4860 | |||
4813 | #ifdef CONFIG_XFRM | 4861 | #ifdef CONFIG_XFRM |
4814 | /* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec | 4862 | /* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec |
4815 | * packet transformation so allow the packet to pass without any checks | 4863 | * packet transformation so allow the packet to pass without any checks |
4816 | * since we'll have another chance to perform access control checks | 4864 | * since we'll have another chance to perform access control checks |
4817 | * when the packet is on it's final way out. | 4865 | * when the packet is on it's final way out. |
4818 | * NOTE: there appear to be some IPv6 multicast cases where skb->dst | 4866 | * NOTE: there appear to be some IPv6 multicast cases where skb->dst |
4819 | * is NULL, in this case go ahead and apply access control. */ | 4867 | * is NULL, in this case go ahead and apply access control. |
4820 | if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL) | 4868 | * NOTE: if this is a local socket (skb->sk != NULL) that is in the |
4869 | * TCP listening state we cannot wait until the XFRM processing | ||
4870 | * is done as we will miss out on the SA label if we do; | ||
4871 | * unfortunately, this means more work, but it is only once per | ||
4872 | * connection. */ | ||
4873 | if (skb_dst(skb) != NULL && skb_dst(skb)->xfrm != NULL && | ||
4874 | !(sk != NULL && sk->sk_state == TCP_LISTEN)) | ||
4821 | return NF_ACCEPT; | 4875 | return NF_ACCEPT; |
4822 | #endif | 4876 | #endif |
4823 | secmark_active = selinux_secmark_enabled(); | ||
4824 | peerlbl_active = selinux_peerlbl_enabled(); | ||
4825 | if (!secmark_active && !peerlbl_active) | ||
4826 | return NF_ACCEPT; | ||
4827 | 4877 | ||
4828 | /* if the packet is being forwarded then get the peer label from the | ||
4829 | * packet itself; otherwise check to see if it is from a local | ||
4830 | * application or the kernel, if from an application get the peer label | ||
4831 | * from the sending socket, otherwise use the kernel's sid */ | ||
4832 | sk = skb->sk; | ||
4833 | if (sk == NULL) { | 4878 | if (sk == NULL) { |
4879 | /* Without an associated socket the packet is either coming | ||
4880 | * from the kernel or it is being forwarded; check the packet | ||
4881 | * to determine which and if the packet is being forwarded | ||
4882 | * query the packet directly to determine the security label. */ | ||
4834 | if (skb->skb_iif) { | 4883 | if (skb->skb_iif) { |
4835 | secmark_perm = PACKET__FORWARD_OUT; | 4884 | secmark_perm = PACKET__FORWARD_OUT; |
4836 | if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) | 4885 | if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) |
@@ -4839,7 +4888,45 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, | |||
4839 | secmark_perm = PACKET__SEND; | 4888 | secmark_perm = PACKET__SEND; |
4840 | peer_sid = SECINITSID_KERNEL; | 4889 | peer_sid = SECINITSID_KERNEL; |
4841 | } | 4890 | } |
4891 | } else if (sk->sk_state == TCP_LISTEN) { | ||
4892 | /* Locally generated packet but the associated socket is in the | ||
4893 | * listening state which means this is a SYN-ACK packet. In | ||
4894 | * this particular case the correct security label is assigned | ||
4895 | * to the connection/request_sock but unfortunately we can't | ||
4896 | * query the request_sock as it isn't queued on the parent | ||
4897 | * socket until after the SYN-ACK packet is sent; the only | ||
4898 | * viable choice is to regenerate the label like we do in | ||
4899 | * selinux_inet_conn_request(). See also selinux_ip_output() | ||
4900 | * for similar problems. */ | ||
4901 | u32 skb_sid; | ||
4902 | struct sk_security_struct *sksec = sk->sk_security; | ||
4903 | if (selinux_skb_peerlbl_sid(skb, family, &skb_sid)) | ||
4904 | return NF_DROP; | ||
4905 | /* At this point, if the returned skb peerlbl is SECSID_NULL | ||
4906 | * and the packet has been through at least one XFRM | ||
4907 | * transformation then we must be dealing with the "final" | ||
4908 | * form of labeled IPsec packet; since we've already applied | ||
4909 | * all of our access controls on this packet we can safely | ||
4910 | * pass the packet. */ | ||
4911 | if (skb_sid == SECSID_NULL) { | ||
4912 | switch (family) { | ||
4913 | case PF_INET: | ||
4914 | if (IPCB(skb)->flags & IPSKB_XFRM_TRANSFORMED) | ||
4915 | return NF_ACCEPT; | ||
4916 | break; | ||
4917 | case PF_INET6: | ||
4918 | if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) | ||
4919 | return NF_ACCEPT; | ||
4920 | default: | ||
4921 | return NF_DROP_ERR(-ECONNREFUSED); | ||
4922 | } | ||
4923 | } | ||
4924 | if (selinux_conn_sid(sksec->sid, skb_sid, &peer_sid)) | ||
4925 | return NF_DROP; | ||
4926 | secmark_perm = PACKET__SEND; | ||
4842 | } else { | 4927 | } else { |
4928 | /* Locally generated packet, fetch the security label from the | ||
4929 | * associated socket. */ | ||
4843 | struct sk_security_struct *sksec = sk->sk_security; | 4930 | struct sk_security_struct *sksec = sk->sk_security; |
4844 | peer_sid = sksec->sid; | 4931 | peer_sid = sksec->sid; |
4845 | secmark_perm = PACKET__SEND; | 4932 | secmark_perm = PACKET__SEND; |