diff options
author | Paul Moore <pmoore@redhat.com> | 2013-12-04 16:10:51 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-12-20 10:45:09 -0500 |
commit | 348d7867154c8d7b6f472cbe5918b1169dd13b0c (patch) | |
tree | 7e326e48368b2aa7e0edf226af00c78b2c5ee452 | |
parent | 216c4a776a12148e8386070da71b9f10ab854e93 (diff) |
selinux: handle TCP SYN-ACK packets correctly in selinux_ip_postroute()
commit 446b802437f285de68ffb8d6fac3c44c3cab5b04 upstream.
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>
Signed-off-by: Paul Moore <pmoore@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-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 27860faab12e..5e58d7dd7b69 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -3737,6 +3737,30 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) | |||
3737 | return 0; | 3737 | return 0; |
3738 | } | 3738 | } |
3739 | 3739 | ||
3740 | /** | ||
3741 | * selinux_conn_sid - Determine the child socket label for a connection | ||
3742 | * @sk_sid: the parent socket's SID | ||
3743 | * @skb_sid: the packet's SID | ||
3744 | * @conn_sid: the resulting connection SID | ||
3745 | * | ||
3746 | * If @skb_sid is valid then the user:role:type information from @sk_sid is | ||
3747 | * combined with the MLS information from @skb_sid in order to create | ||
3748 | * @conn_sid. If @skb_sid is not valid then then @conn_sid is simply a copy | ||
3749 | * of @sk_sid. Returns zero on success, negative values on failure. | ||
3750 | * | ||
3751 | */ | ||
3752 | static int selinux_conn_sid(u32 sk_sid, u32 skb_sid, u32 *conn_sid) | ||
3753 | { | ||
3754 | int err = 0; | ||
3755 | |||
3756 | if (skb_sid != SECSID_NULL) | ||
3757 | err = security_sid_mls_copy(sk_sid, skb_sid, conn_sid); | ||
3758 | else | ||
3759 | *conn_sid = sk_sid; | ||
3760 | |||
3761 | return err; | ||
3762 | } | ||
3763 | |||
3740 | /* socket security operations */ | 3764 | /* socket security operations */ |
3741 | 3765 | ||
3742 | static int socket_sockcreate_sid(const struct task_security_struct *tsec, | 3766 | static int socket_sockcreate_sid(const struct task_security_struct *tsec, |
@@ -4343,7 +4367,7 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, | |||
4343 | struct sk_security_struct *sksec = sk->sk_security; | 4367 | struct sk_security_struct *sksec = sk->sk_security; |
4344 | int err; | 4368 | int err; |
4345 | u16 family = sk->sk_family; | 4369 | u16 family = sk->sk_family; |
4346 | u32 newsid; | 4370 | u32 connsid; |
4347 | u32 peersid; | 4371 | u32 peersid; |
4348 | 4372 | ||
4349 | /* handle mapped IPv4 packets arriving via IPv6 sockets */ | 4373 | /* handle mapped IPv4 packets arriving via IPv6 sockets */ |
@@ -4353,16 +4377,11 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, | |||
4353 | err = selinux_skb_peerlbl_sid(skb, family, &peersid); | 4377 | err = selinux_skb_peerlbl_sid(skb, family, &peersid); |
4354 | if (err) | 4378 | if (err) |
4355 | return err; | 4379 | return err; |
4356 | if (peersid == SECSID_NULL) { | 4380 | err = selinux_conn_sid(sksec->sid, peersid, &connsid); |
4357 | req->secid = sksec->sid; | 4381 | if (err) |
4358 | req->peer_secid = SECSID_NULL; | 4382 | return err; |
4359 | } else { | 4383 | req->secid = connsid; |
4360 | err = security_sid_mls_copy(sksec->sid, peersid, &newsid); | 4384 | req->peer_secid = peersid; |
4361 | if (err) | ||
4362 | return err; | ||
4363 | req->secid = newsid; | ||
4364 | req->peer_secid = peersid; | ||
4365 | } | ||
4366 | 4385 | ||
4367 | return selinux_netlbl_inet_conn_request(req, family); | 4386 | return selinux_netlbl_inet_conn_request(req, family); |
4368 | } | 4387 | } |
@@ -4736,12 +4755,12 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, | |||
4736 | if (!secmark_active && !peerlbl_active) | 4755 | if (!secmark_active && !peerlbl_active) |
4737 | return NF_ACCEPT; | 4756 | return NF_ACCEPT; |
4738 | 4757 | ||
4739 | /* if the packet is being forwarded then get the peer label from the | ||
4740 | * packet itself; otherwise check to see if it is from a local | ||
4741 | * application or the kernel, if from an application get the peer label | ||
4742 | * from the sending socket, otherwise use the kernel's sid */ | ||
4743 | sk = skb->sk; | 4758 | sk = skb->sk; |
4744 | if (sk == NULL) { | 4759 | if (sk == NULL) { |
4760 | /* Without an associated socket the packet is either coming | ||
4761 | * from the kernel or it is being forwarded; check the packet | ||
4762 | * to determine which and if the packet is being forwarded | ||
4763 | * query the packet directly to determine the security label. */ | ||
4745 | if (skb->skb_iif) { | 4764 | if (skb->skb_iif) { |
4746 | secmark_perm = PACKET__FORWARD_OUT; | 4765 | secmark_perm = PACKET__FORWARD_OUT; |
4747 | if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) | 4766 | if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) |
@@ -4750,7 +4769,26 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, | |||
4750 | secmark_perm = PACKET__SEND; | 4769 | secmark_perm = PACKET__SEND; |
4751 | peer_sid = SECINITSID_KERNEL; | 4770 | peer_sid = SECINITSID_KERNEL; |
4752 | } | 4771 | } |
4772 | } else if (sk->sk_state == TCP_LISTEN) { | ||
4773 | /* Locally generated packet but the associated socket is in the | ||
4774 | * listening state which means this is a SYN-ACK packet. In | ||
4775 | * this particular case the correct security label is assigned | ||
4776 | * to the connection/request_sock but unfortunately we can't | ||
4777 | * query the request_sock as it isn't queued on the parent | ||
4778 | * socket until after the SYN-ACK packet is sent; the only | ||
4779 | * viable choice is to regenerate the label like we do in | ||
4780 | * selinux_inet_conn_request(). See also selinux_ip_output() | ||
4781 | * for similar problems. */ | ||
4782 | u32 skb_sid; | ||
4783 | struct sk_security_struct *sksec = sk->sk_security; | ||
4784 | if (selinux_skb_peerlbl_sid(skb, family, &skb_sid)) | ||
4785 | return NF_DROP; | ||
4786 | if (selinux_conn_sid(sksec->sid, skb_sid, &peer_sid)) | ||
4787 | return NF_DROP; | ||
4788 | secmark_perm = PACKET__SEND; | ||
4753 | } else { | 4789 | } else { |
4790 | /* Locally generated packet, fetch the security label from the | ||
4791 | * associated socket. */ | ||
4754 | struct sk_security_struct *sksec = sk->sk_security; | 4792 | struct sk_security_struct *sksec = sk->sk_security; |
4755 | peer_sid = sksec->sid; | 4793 | peer_sid = sksec->sid; |
4756 | secmark_perm = PACKET__SEND; | 4794 | secmark_perm = PACKET__SEND; |