diff options
author | Paul Moore <paul.moore@hp.com> | 2008-10-10 10:16:30 -0400 |
---|---|---|
committer | Paul Moore <paul.moore@hp.com> | 2008-10-10 10:16:30 -0400 |
commit | d8395c876bb8a560c8a032887e191b95499a25d6 (patch) | |
tree | 6c2ef0d59e04b90a9ef673fa34e1c042d22f128e | |
parent | 948a72438d4178d0728c4b0a38836d280b846939 (diff) |
selinux: Better local/forward check in selinux_ip_postroute()
It turns out that checking to see if skb->sk is NULL is not a very good
indicator of a forwarded packet as some locally generated packets also have
skb->sk set to NULL. Fix this by not only checking the skb->sk field but also
the IP[6]CB(skb)->flags field for the IP[6]SKB_FORWARDED flag. While we are
at it, we are calling selinux_parse_skb() much earlier than we really should
resulting in potentially wasted cycles parsing packets for information we
might no use; so shuffle the code around a bit to fix this.
Signed-off-by: Paul Moore <paul.moore@hp.com>
Acked-by: James Morris <jmorris@namei.org>
-rw-r--r-- | security/selinux/hooks.c | 126 |
1 files changed, 81 insertions, 45 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 223f474bee86..b520667a24be 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -4070,20 +4070,28 @@ static int selinux_sock_rcv_skb_iptables_compat(struct sock *sk, | |||
4070 | } | 4070 | } |
4071 | 4071 | ||
4072 | static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, | 4072 | static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, |
4073 | struct avc_audit_data *ad, | 4073 | u16 family) |
4074 | u16 family, char *addrp) | ||
4075 | { | 4074 | { |
4076 | int err; | 4075 | int err; |
4077 | struct sk_security_struct *sksec = sk->sk_security; | 4076 | struct sk_security_struct *sksec = sk->sk_security; |
4078 | u32 peer_sid; | 4077 | u32 peer_sid; |
4079 | u32 sk_sid = sksec->sid; | 4078 | u32 sk_sid = sksec->sid; |
4079 | struct avc_audit_data ad; | ||
4080 | char *addrp; | ||
4081 | |||
4082 | AVC_AUDIT_DATA_INIT(&ad, NET); | ||
4083 | ad.u.net.netif = skb->iif; | ||
4084 | ad.u.net.family = family; | ||
4085 | err = selinux_parse_skb(skb, &ad, &addrp, 1, NULL); | ||
4086 | if (err) | ||
4087 | return err; | ||
4080 | 4088 | ||
4081 | if (selinux_compat_net) | 4089 | if (selinux_compat_net) |
4082 | err = selinux_sock_rcv_skb_iptables_compat(sk, skb, ad, | 4090 | err = selinux_sock_rcv_skb_iptables_compat(sk, skb, &ad, |
4083 | family, addrp); | 4091 | family, addrp); |
4084 | else | 4092 | else |
4085 | err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, | 4093 | err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, |
4086 | PACKET__RECV, ad); | 4094 | PACKET__RECV, &ad); |
4087 | if (err) | 4095 | if (err) |
4088 | return err; | 4096 | return err; |
4089 | 4097 | ||
@@ -4092,12 +4100,12 @@ static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, | |||
4092 | if (err) | 4100 | if (err) |
4093 | return err; | 4101 | return err; |
4094 | err = avc_has_perm(sk_sid, peer_sid, | 4102 | err = avc_has_perm(sk_sid, peer_sid, |
4095 | SECCLASS_PEER, PEER__RECV, ad); | 4103 | SECCLASS_PEER, PEER__RECV, &ad); |
4096 | } else { | 4104 | } else { |
4097 | err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, ad); | 4105 | err = selinux_netlbl_sock_rcv_skb(sksec, skb, family, &ad); |
4098 | if (err) | 4106 | if (err) |
4099 | return err; | 4107 | return err; |
4100 | err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, ad); | 4108 | err = selinux_xfrm_sock_rcv_skb(sksec->sid, skb, &ad); |
4101 | } | 4109 | } |
4102 | 4110 | ||
4103 | return err; | 4111 | return err; |
@@ -4111,6 +4119,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) | |||
4111 | u32 sk_sid = sksec->sid; | 4119 | u32 sk_sid = sksec->sid; |
4112 | struct avc_audit_data ad; | 4120 | struct avc_audit_data ad; |
4113 | char *addrp; | 4121 | char *addrp; |
4122 | u8 secmark_active; | ||
4123 | u8 peerlbl_active; | ||
4114 | 4124 | ||
4115 | if (family != PF_INET && family != PF_INET6) | 4125 | if (family != PF_INET && family != PF_INET6) |
4116 | return 0; | 4126 | return 0; |
@@ -4119,6 +4129,18 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) | |||
4119 | if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) | 4129 | if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) |
4120 | family = PF_INET; | 4130 | family = PF_INET; |
4121 | 4131 | ||
4132 | /* If any sort of compatibility mode is enabled then handoff processing | ||
4133 | * to the selinux_sock_rcv_skb_compat() function to deal with the | ||
4134 | * special handling. We do this in an attempt to keep this function | ||
4135 | * as fast and as clean as possible. */ | ||
4136 | if (selinux_compat_net || !selinux_policycap_netpeer) | ||
4137 | return selinux_sock_rcv_skb_compat(sk, skb, family); | ||
4138 | |||
4139 | secmark_active = selinux_secmark_enabled(); | ||
4140 | peerlbl_active = netlbl_enabled() || selinux_xfrm_enabled(); | ||
4141 | if (!secmark_active && !peerlbl_active) | ||
4142 | return 0; | ||
4143 | |||
4122 | AVC_AUDIT_DATA_INIT(&ad, NET); | 4144 | AVC_AUDIT_DATA_INIT(&ad, NET); |
4123 | ad.u.net.netif = skb->iif; | 4145 | ad.u.net.netif = skb->iif; |
4124 | ad.u.net.family = family; | 4146 | ad.u.net.family = family; |
@@ -4126,15 +4148,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) | |||
4126 | if (err) | 4148 | if (err) |
4127 | return err; | 4149 | return err; |
4128 | 4150 | ||
4129 | /* If any sort of compatibility mode is enabled then handoff processing | 4151 | if (peerlbl_active) { |
4130 | * to the selinux_sock_rcv_skb_compat() function to deal with the | ||
4131 | * special handling. We do this in an attempt to keep this function | ||
4132 | * as fast and as clean as possible. */ | ||
4133 | if (selinux_compat_net || !selinux_policycap_netpeer) | ||
4134 | return selinux_sock_rcv_skb_compat(sk, skb, &ad, | ||
4135 | family, addrp); | ||
4136 | |||
4137 | if (netlbl_enabled() || selinux_xfrm_enabled()) { | ||
4138 | u32 peer_sid; | 4152 | u32 peer_sid; |
4139 | 4153 | ||
4140 | err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); | 4154 | err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); |
@@ -4148,7 +4162,7 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) | |||
4148 | PEER__RECV, &ad); | 4162 | PEER__RECV, &ad); |
4149 | } | 4163 | } |
4150 | 4164 | ||
4151 | if (selinux_secmark_enabled()) { | 4165 | if (secmark_active) { |
4152 | err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, | 4166 | err = avc_has_perm(sk_sid, skb->secmark, SECCLASS_PACKET, |
4153 | PACKET__RECV, &ad); | 4167 | PACKET__RECV, &ad); |
4154 | if (err) | 4168 | if (err) |
@@ -4396,15 +4410,15 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, | |||
4396 | if (!secmark_active && !peerlbl_active) | 4410 | if (!secmark_active && !peerlbl_active) |
4397 | return NF_ACCEPT; | 4411 | return NF_ACCEPT; |
4398 | 4412 | ||
4413 | if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0) | ||
4414 | return NF_DROP; | ||
4415 | |||
4399 | AVC_AUDIT_DATA_INIT(&ad, NET); | 4416 | AVC_AUDIT_DATA_INIT(&ad, NET); |
4400 | ad.u.net.netif = ifindex; | 4417 | ad.u.net.netif = ifindex; |
4401 | ad.u.net.family = family; | 4418 | ad.u.net.family = family; |
4402 | if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0) | 4419 | if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0) |
4403 | return NF_DROP; | 4420 | return NF_DROP; |
4404 | 4421 | ||
4405 | if (selinux_skb_peerlbl_sid(skb, family, &peer_sid) != 0) | ||
4406 | return NF_DROP; | ||
4407 | |||
4408 | if (peerlbl_active) | 4422 | if (peerlbl_active) |
4409 | if (selinux_inet_sys_rcv_skb(ifindex, addrp, family, | 4423 | if (selinux_inet_sys_rcv_skb(ifindex, addrp, family, |
4410 | peer_sid, &ad) != 0) | 4424 | peer_sid, &ad) != 0) |
@@ -4505,30 +4519,36 @@ static int selinux_ip_postroute_iptables_compat(struct sock *sk, | |||
4505 | 4519 | ||
4506 | static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, | 4520 | static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, |
4507 | int ifindex, | 4521 | int ifindex, |
4508 | struct avc_audit_data *ad, | 4522 | u16 family) |
4509 | u16 family, | ||
4510 | char *addrp, | ||
4511 | u8 proto) | ||
4512 | { | 4523 | { |
4513 | struct sock *sk = skb->sk; | 4524 | struct sock *sk = skb->sk; |
4514 | struct sk_security_struct *sksec; | 4525 | struct sk_security_struct *sksec; |
4526 | struct avc_audit_data ad; | ||
4527 | char *addrp; | ||
4528 | u8 proto; | ||
4515 | 4529 | ||
4516 | if (sk == NULL) | 4530 | if (sk == NULL) |
4517 | return NF_ACCEPT; | 4531 | return NF_ACCEPT; |
4518 | sksec = sk->sk_security; | 4532 | sksec = sk->sk_security; |
4519 | 4533 | ||
4534 | AVC_AUDIT_DATA_INIT(&ad, NET); | ||
4535 | ad.u.net.netif = ifindex; | ||
4536 | ad.u.net.family = family; | ||
4537 | if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto)) | ||
4538 | return NF_DROP; | ||
4539 | |||
4520 | if (selinux_compat_net) { | 4540 | if (selinux_compat_net) { |
4521 | if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex, | 4541 | if (selinux_ip_postroute_iptables_compat(skb->sk, ifindex, |
4522 | ad, family, addrp)) | 4542 | &ad, family, addrp)) |
4523 | return NF_DROP; | 4543 | return NF_DROP; |
4524 | } else { | 4544 | } else { |
4525 | if (avc_has_perm(sksec->sid, skb->secmark, | 4545 | if (avc_has_perm(sksec->sid, skb->secmark, |
4526 | SECCLASS_PACKET, PACKET__SEND, ad)) | 4546 | SECCLASS_PACKET, PACKET__SEND, &ad)) |
4527 | return NF_DROP; | 4547 | return NF_DROP; |
4528 | } | 4548 | } |
4529 | 4549 | ||
4530 | if (selinux_policycap_netpeer) | 4550 | if (selinux_policycap_netpeer) |
4531 | if (selinux_xfrm_postroute_last(sksec->sid, skb, ad, proto)) | 4551 | if (selinux_xfrm_postroute_last(sksec->sid, skb, &ad, proto)) |
4532 | return NF_DROP; | 4552 | return NF_DROP; |
4533 | 4553 | ||
4534 | return NF_ACCEPT; | 4554 | return NF_ACCEPT; |
@@ -4542,23 +4562,15 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, | |||
4542 | struct sock *sk; | 4562 | struct sock *sk; |
4543 | struct avc_audit_data ad; | 4563 | struct avc_audit_data ad; |
4544 | char *addrp; | 4564 | char *addrp; |
4545 | u8 proto; | ||
4546 | u8 secmark_active; | 4565 | u8 secmark_active; |
4547 | u8 peerlbl_active; | 4566 | u8 peerlbl_active; |
4548 | 4567 | ||
4549 | AVC_AUDIT_DATA_INIT(&ad, NET); | ||
4550 | ad.u.net.netif = ifindex; | ||
4551 | ad.u.net.family = family; | ||
4552 | if (selinux_parse_skb(skb, &ad, &addrp, 0, &proto)) | ||
4553 | return NF_DROP; | ||
4554 | |||
4555 | /* If any sort of compatibility mode is enabled then handoff processing | 4568 | /* If any sort of compatibility mode is enabled then handoff processing |
4556 | * to the selinux_ip_postroute_compat() function to deal with the | 4569 | * to the selinux_ip_postroute_compat() function to deal with the |
4557 | * special handling. We do this in an attempt to keep this function | 4570 | * special handling. We do this in an attempt to keep this function |
4558 | * as fast and as clean as possible. */ | 4571 | * as fast and as clean as possible. */ |
4559 | if (selinux_compat_net || !selinux_policycap_netpeer) | 4572 | if (selinux_compat_net || !selinux_policycap_netpeer) |
4560 | return selinux_ip_postroute_compat(skb, ifindex, &ad, | 4573 | return selinux_ip_postroute_compat(skb, ifindex, family); |
4561 | family, addrp, proto); | ||
4562 | 4574 | ||
4563 | /* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec | 4575 | /* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec |
4564 | * packet transformation so allow the packet to pass without any checks | 4576 | * packet transformation so allow the packet to pass without any checks |
@@ -4574,21 +4586,45 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, | |||
4574 | if (!secmark_active && !peerlbl_active) | 4586 | if (!secmark_active && !peerlbl_active) |
4575 | return NF_ACCEPT; | 4587 | return NF_ACCEPT; |
4576 | 4588 | ||
4577 | /* if the packet is locally generated (skb->sk != NULL) then use the | 4589 | /* if the packet is being forwarded then get the peer label from the |
4578 | * socket's label as the peer label, otherwise the packet is being | 4590 | * packet itself; otherwise check to see if it is from a local |
4579 | * forwarded through this system and we need to fetch the peer label | 4591 | * application or the kernel, if from an application get the peer label |
4580 | * directly from the packet */ | 4592 | * from the sending socket, otherwise use the kernel's sid */ |
4581 | sk = skb->sk; | 4593 | sk = skb->sk; |
4582 | if (sk) { | 4594 | if (sk == NULL) { |
4595 | switch (family) { | ||
4596 | case PF_INET: | ||
4597 | if (IPCB(skb)->flags & IPSKB_FORWARDED) | ||
4598 | secmark_perm = PACKET__FORWARD_OUT; | ||
4599 | else | ||
4600 | secmark_perm = PACKET__SEND; | ||
4601 | break; | ||
4602 | case PF_INET6: | ||
4603 | if (IP6CB(skb)->flags & IP6SKB_FORWARDED) | ||
4604 | secmark_perm = PACKET__FORWARD_OUT; | ||
4605 | else | ||
4606 | secmark_perm = PACKET__SEND; | ||
4607 | break; | ||
4608 | default: | ||
4609 | return NF_DROP; | ||
4610 | } | ||
4611 | if (secmark_perm == PACKET__FORWARD_OUT) { | ||
4612 | if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) | ||
4613 | return NF_DROP; | ||
4614 | } else | ||
4615 | peer_sid = SECINITSID_KERNEL; | ||
4616 | } else { | ||
4583 | struct sk_security_struct *sksec = sk->sk_security; | 4617 | struct sk_security_struct *sksec = sk->sk_security; |
4584 | peer_sid = sksec->sid; | 4618 | peer_sid = sksec->sid; |
4585 | secmark_perm = PACKET__SEND; | 4619 | secmark_perm = PACKET__SEND; |
4586 | } else { | ||
4587 | if (selinux_skb_peerlbl_sid(skb, family, &peer_sid)) | ||
4588 | return NF_DROP; | ||
4589 | secmark_perm = PACKET__FORWARD_OUT; | ||
4590 | } | 4620 | } |
4591 | 4621 | ||
4622 | AVC_AUDIT_DATA_INIT(&ad, NET); | ||
4623 | ad.u.net.netif = ifindex; | ||
4624 | ad.u.net.family = family; | ||
4625 | if (selinux_parse_skb(skb, &ad, &addrp, 0, NULL)) | ||
4626 | return NF_DROP; | ||
4627 | |||
4592 | if (secmark_active) | 4628 | if (secmark_active) |
4593 | if (avc_has_perm(peer_sid, skb->secmark, | 4629 | if (avc_has_perm(peer_sid, skb->secmark, |
4594 | SECCLASS_PACKET, secmark_perm, &ad)) | 4630 | SECCLASS_PACKET, secmark_perm, &ad)) |