diff options
author | Paul Moore <pmoore@redhat.com> | 2013-12-04 16:10:51 -0500 |
---|---|---|
committer | Paul Moore <pmoore@redhat.com> | 2013-12-12 17:21:31 -0500 |
commit | 446b802437f285de68ffb8d6fac3c44c3cab5b04 (patch) | |
tree | 2123c25875f8ad75114592e4755d21429765a6c0 | |
parent | 47180068276a04ed31d24fe04c673138208b07a9 (diff) |
selinux: handle TCP SYN-ACK packets correctly in selinux_ip_postroute()
In selinux_ip_postroute() we perform access checks based on the
packet's security label. For locally generated traffic we get the
packet's security label from the associated socket; this works in all
cases except for TCP SYN-ACK packets. In the case of SYN-ACK packet's
the correct security label is stored in the connection's request_sock,
not the server's socket. Unfortunately, at the point in time when
selinux_ip_postroute() is called we can't query the request_sock
directly, we need to recreate the label using the same logic that
originally labeled the associated request_sock.
See the inline comments for more explanation.
Reported-by: Janak Desai <Janak.Desai@gtri.gatech.edu>
Tested-by: Janak Desai <Janak.Desai@gtri.gatech.edu>
Cc: stable@vger.kernel.org
Signed-off-by: Paul Moore <pmoore@redhat.com>
-rw-r--r-- | security/selinux/hooks.c | 68 |
1 files changed, 53 insertions, 15 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 877bab748c87..cc076a9b0344 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -3847,6 +3847,30 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) | |||
3847 | return 0; | 3847 | return 0; |
3848 | } | 3848 | } |
3849 | 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 | |||
3850 | /* socket security operations */ | 3874 | /* socket security operations */ |
3851 | 3875 | ||
3852 | static int socket_sockcreate_sid(const struct task_security_struct *tsec, | 3876 | static int socket_sockcreate_sid(const struct task_security_struct *tsec, |
@@ -4453,7 +4477,7 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, | |||
4453 | struct sk_security_struct *sksec = sk->sk_security; | 4477 | struct sk_security_struct *sksec = sk->sk_security; |
4454 | int err; | 4478 | int err; |
4455 | u16 family = sk->sk_family; | 4479 | u16 family = sk->sk_family; |
4456 | u32 newsid; | 4480 | u32 connsid; |
4457 | u32 peersid; | 4481 | u32 peersid; |
4458 | 4482 | ||
4459 | /* handle mapped IPv4 packets arriving via IPv6 sockets */ | 4483 | /* handle mapped IPv4 packets arriving via IPv6 sockets */ |
@@ -4463,16 +4487,11 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, | |||
4463 | err = selinux_skb_peerlbl_sid(skb, family, &peersid); | 4487 | err = selinux_skb_peerlbl_sid(skb, family, &peersid); |
4464 | if (err) | 4488 | if (err) |
4465 | return err; | 4489 | return err; |
4466 | if (peersid == SECSID_NULL) { | 4490 | err = selinux_conn_sid(sksec->sid, peersid, &connsid); |
4467 | req->secid = sksec->sid; | 4491 | if (err) |
4468 | req->peer_secid = SECSID_NULL; | 4492 | return err; |
4469 | } else { | 4493 | req->secid = connsid; |
4470 | err = security_sid_mls_copy(sksec->sid, peersid, &newsid); | 4494 | req->peer_secid = peersid; |
4471 | if (err) | ||
4472 | return err; | ||
4473 | req->secid = newsid; | ||
4474 | req->peer_secid = peersid; | ||
4475 | } | ||
4476 | 4495 | ||
4477 | return selinux_netlbl_inet_conn_request(req, family); | 4496 | return selinux_netlbl_inet_conn_request(req, family); |
4478 | } | 4497 | } |
@@ -4846,12 +4865,12 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, | |||
4846 | if (!secmark_active && !peerlbl_active) | 4865 | if (!secmark_active && !peerlbl_active) |
4847 | return NF_ACCEPT; | 4866 | return NF_ACCEPT; |
4848 | 4867 | ||
4849 | /* if the packet is being forwarded then get the peer label from the | ||
4850 | * packet itself; otherwise check to see if it is from a local | ||
4851 | * application or the kernel, if from an application get the peer label | ||
4852 | * from the sending socket, otherwise use the kernel's sid */ | ||
4853 | sk = skb->sk; | 4868 | sk = skb->sk; |
4854 | if (sk == NULL) { | 4869 | if (sk == NULL) { |
4870 | /* Without an associated socket the packet is either coming | ||
4871 | * from the kernel or it is being forwarded; check the packet | ||
4872 | * to determine which and if the packet is being forwarded | ||
4873 | * query the packet directly to determine the security label. */ | ||
4855 | if (skb->skb_iif) { | 4874 | if (skb->skb_iif) { |
4856 | secmark_perm = PACKET__FORWARD_OUT; | 4875 | secmark_perm = PACKET__FORWARD_OUT; |
4857 | if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) | 4876 | if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) |
@@ -4860,7 +4879,26 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, | |||
4860 | secmark_perm = PACKET__SEND; | 4879 | secmark_perm = PACKET__SEND; |
4861 | peer_sid = SECINITSID_KERNEL; | 4880 | peer_sid = SECINITSID_KERNEL; |
4862 | } | 4881 | } |
4882 | } else if (sk->sk_state == TCP_LISTEN) { | ||
4883 | /* Locally generated packet but the associated socket is in the | ||
4884 | * listening state which means this is a SYN-ACK packet. In | ||
4885 | * this particular case the correct security label is assigned | ||
4886 | * to the connection/request_sock but unfortunately we can't | ||
4887 | * query the request_sock as it isn't queued on the parent | ||
4888 | * socket until after the SYN-ACK packet is sent; the only | ||
4889 | * viable choice is to regenerate the label like we do in | ||
4890 | * selinux_inet_conn_request(). See also selinux_ip_output() | ||
4891 | * for similar problems. */ | ||
4892 | u32 skb_sid; | ||
4893 | struct sk_security_struct *sksec = sk->sk_security; | ||
4894 | if (selinux_skb_peerlbl_sid(skb, family, &skb_sid)) | ||
4895 | return NF_DROP; | ||
4896 | if (selinux_conn_sid(sksec->sid, skb_sid, &peer_sid)) | ||
4897 | return NF_DROP; | ||
4898 | secmark_perm = PACKET__SEND; | ||
4863 | } else { | 4899 | } else { |
4900 | /* Locally generated packet, fetch the security label from the | ||
4901 | * associated socket. */ | ||
4864 | struct sk_security_struct *sksec = sk->sk_security; | 4902 | struct sk_security_struct *sksec = sk->sk_security; |
4865 | peer_sid = sksec->sid; | 4903 | peer_sid = sksec->sid; |
4866 | secmark_perm = PACKET__SEND; | 4904 | secmark_perm = PACKET__SEND; |