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 /security | |
| 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>
Diffstat (limited to 'security')
| -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)) |
