aboutsummaryrefslogtreecommitdiffstats
path: root/security
diff options
context:
space:
mode:
authorPaul Moore <paul.moore@hp.com>2008-10-10 10:16:30 -0400
committerPaul Moore <paul.moore@hp.com>2008-10-10 10:16:30 -0400
commitd8395c876bb8a560c8a032887e191b95499a25d6 (patch)
tree6c2ef0d59e04b90a9ef673fa34e1c042d22f128e /security
parent948a72438d4178d0728c4b0a38836d280b846939 (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.c126
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
4072static int selinux_sock_rcv_skb_compat(struct sock *sk, struct sk_buff *skb, 4072static 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
4506static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, 4520static 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))