diff options
Diffstat (limited to 'security')
| -rw-r--r-- | security/selinux/hooks.c | 50 |
1 files changed, 28 insertions, 22 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 4cafe6a19167..be5817df0a9d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
| @@ -4576,6 +4576,7 @@ static int selinux_socket_post_create(struct socket *sock, int family, | |||
| 4576 | static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) | 4576 | static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) |
| 4577 | { | 4577 | { |
| 4578 | struct sock *sk = sock->sk; | 4578 | struct sock *sk = sock->sk; |
| 4579 | struct sk_security_struct *sksec = sk->sk_security; | ||
| 4579 | u16 family; | 4580 | u16 family; |
| 4580 | int err; | 4581 | int err; |
| 4581 | 4582 | ||
| @@ -4587,11 +4588,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
| 4587 | family = sk->sk_family; | 4588 | family = sk->sk_family; |
| 4588 | if (family == PF_INET || family == PF_INET6) { | 4589 | if (family == PF_INET || family == PF_INET6) { |
| 4589 | char *addrp; | 4590 | char *addrp; |
| 4590 | struct sk_security_struct *sksec = sk->sk_security; | ||
| 4591 | struct common_audit_data ad; | 4591 | struct common_audit_data ad; |
| 4592 | struct lsm_network_audit net = {0,}; | 4592 | struct lsm_network_audit net = {0,}; |
| 4593 | struct sockaddr_in *addr4 = NULL; | 4593 | struct sockaddr_in *addr4 = NULL; |
| 4594 | struct sockaddr_in6 *addr6 = NULL; | 4594 | struct sockaddr_in6 *addr6 = NULL; |
| 4595 | u16 family_sa = address->sa_family; | ||
| 4595 | unsigned short snum; | 4596 | unsigned short snum; |
| 4596 | u32 sid, node_perm; | 4597 | u32 sid, node_perm; |
| 4597 | 4598 | ||
| @@ -4601,11 +4602,20 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
| 4601 | * need to check address->sa_family as it is possible to have | 4602 | * need to check address->sa_family as it is possible to have |
| 4602 | * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET. | 4603 | * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET. |
| 4603 | */ | 4604 | */ |
| 4604 | switch (address->sa_family) { | 4605 | switch (family_sa) { |
| 4606 | case AF_UNSPEC: | ||
| 4605 | case AF_INET: | 4607 | case AF_INET: |
| 4606 | if (addrlen < sizeof(struct sockaddr_in)) | 4608 | if (addrlen < sizeof(struct sockaddr_in)) |
| 4607 | return -EINVAL; | 4609 | return -EINVAL; |
| 4608 | addr4 = (struct sockaddr_in *)address; | 4610 | addr4 = (struct sockaddr_in *)address; |
| 4611 | if (family_sa == AF_UNSPEC) { | ||
| 4612 | /* see __inet_bind(), we only want to allow | ||
| 4613 | * AF_UNSPEC if the address is INADDR_ANY | ||
| 4614 | */ | ||
| 4615 | if (addr4->sin_addr.s_addr != htonl(INADDR_ANY)) | ||
| 4616 | goto err_af; | ||
| 4617 | family_sa = AF_INET; | ||
| 4618 | } | ||
| 4609 | snum = ntohs(addr4->sin_port); | 4619 | snum = ntohs(addr4->sin_port); |
| 4610 | addrp = (char *)&addr4->sin_addr.s_addr; | 4620 | addrp = (char *)&addr4->sin_addr.s_addr; |
| 4611 | break; | 4621 | break; |
| @@ -4617,15 +4627,14 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
| 4617 | addrp = (char *)&addr6->sin6_addr.s6_addr; | 4627 | addrp = (char *)&addr6->sin6_addr.s6_addr; |
| 4618 | break; | 4628 | break; |
| 4619 | default: | 4629 | default: |
| 4620 | /* Note that SCTP services expect -EINVAL, whereas | 4630 | goto err_af; |
| 4621 | * others expect -EAFNOSUPPORT. | ||
| 4622 | */ | ||
| 4623 | if (sksec->sclass == SECCLASS_SCTP_SOCKET) | ||
| 4624 | return -EINVAL; | ||
| 4625 | else | ||
| 4626 | return -EAFNOSUPPORT; | ||
| 4627 | } | 4631 | } |
| 4628 | 4632 | ||
| 4633 | ad.type = LSM_AUDIT_DATA_NET; | ||
| 4634 | ad.u.net = &net; | ||
| 4635 | ad.u.net->sport = htons(snum); | ||
| 4636 | ad.u.net->family = family_sa; | ||
| 4637 | |||
| 4629 | if (snum) { | 4638 | if (snum) { |
| 4630 | int low, high; | 4639 | int low, high; |
| 4631 | 4640 | ||
| @@ -4637,10 +4646,6 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
| 4637 | snum, &sid); | 4646 | snum, &sid); |
| 4638 | if (err) | 4647 | if (err) |
| 4639 | goto out; | 4648 | goto out; |
| 4640 | ad.type = LSM_AUDIT_DATA_NET; | ||
| 4641 | ad.u.net = &net; | ||
| 4642 | ad.u.net->sport = htons(snum); | ||
| 4643 | ad.u.net->family = family; | ||
| 4644 | err = avc_has_perm(&selinux_state, | 4649 | err = avc_has_perm(&selinux_state, |
| 4645 | sksec->sid, sid, | 4650 | sksec->sid, sid, |
| 4646 | sksec->sclass, | 4651 | sksec->sclass, |
| @@ -4672,16 +4677,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
| 4672 | break; | 4677 | break; |
| 4673 | } | 4678 | } |
| 4674 | 4679 | ||
| 4675 | err = sel_netnode_sid(addrp, family, &sid); | 4680 | err = sel_netnode_sid(addrp, family_sa, &sid); |
| 4676 | if (err) | 4681 | if (err) |
| 4677 | goto out; | 4682 | goto out; |
| 4678 | 4683 | ||
| 4679 | ad.type = LSM_AUDIT_DATA_NET; | 4684 | if (family_sa == AF_INET) |
| 4680 | ad.u.net = &net; | ||
| 4681 | ad.u.net->sport = htons(snum); | ||
| 4682 | ad.u.net->family = family; | ||
| 4683 | |||
| 4684 | if (address->sa_family == AF_INET) | ||
| 4685 | ad.u.net->v4info.saddr = addr4->sin_addr.s_addr; | 4685 | ad.u.net->v4info.saddr = addr4->sin_addr.s_addr; |
| 4686 | else | 4686 | else |
| 4687 | ad.u.net->v6info.saddr = addr6->sin6_addr; | 4687 | ad.u.net->v6info.saddr = addr6->sin6_addr; |
| @@ -4694,6 +4694,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
| 4694 | } | 4694 | } |
| 4695 | out: | 4695 | out: |
| 4696 | return err; | 4696 | return err; |
| 4697 | err_af: | ||
| 4698 | /* Note that SCTP services expect -EINVAL, others -EAFNOSUPPORT. */ | ||
| 4699 | if (sksec->sclass == SECCLASS_SCTP_SOCKET) | ||
| 4700 | return -EINVAL; | ||
| 4701 | return -EAFNOSUPPORT; | ||
| 4697 | } | 4702 | } |
| 4698 | 4703 | ||
| 4699 | /* This supports connect(2) and SCTP connect services such as sctp_connectx(3) | 4704 | /* This supports connect(2) and SCTP connect services such as sctp_connectx(3) |
| @@ -4771,7 +4776,7 @@ static int selinux_socket_connect_helper(struct socket *sock, | |||
| 4771 | ad.type = LSM_AUDIT_DATA_NET; | 4776 | ad.type = LSM_AUDIT_DATA_NET; |
| 4772 | ad.u.net = &net; | 4777 | ad.u.net = &net; |
| 4773 | ad.u.net->dport = htons(snum); | 4778 | ad.u.net->dport = htons(snum); |
| 4774 | ad.u.net->family = sk->sk_family; | 4779 | ad.u.net->family = address->sa_family; |
| 4775 | err = avc_has_perm(&selinux_state, | 4780 | err = avc_has_perm(&selinux_state, |
| 4776 | sksec->sid, sid, sksec->sclass, perm, &ad); | 4781 | sksec->sid, sid, sksec->sclass, perm, &ad); |
| 4777 | if (err) | 4782 | if (err) |
| @@ -5272,6 +5277,7 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname, | |||
| 5272 | while (walk_size < addrlen) { | 5277 | while (walk_size < addrlen) { |
| 5273 | addr = addr_buf; | 5278 | addr = addr_buf; |
| 5274 | switch (addr->sa_family) { | 5279 | switch (addr->sa_family) { |
| 5280 | case AF_UNSPEC: | ||
| 5275 | case AF_INET: | 5281 | case AF_INET: |
| 5276 | len = sizeof(struct sockaddr_in); | 5282 | len = sizeof(struct sockaddr_in); |
| 5277 | break; | 5283 | break; |
| @@ -5279,7 +5285,7 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname, | |||
| 5279 | len = sizeof(struct sockaddr_in6); | 5285 | len = sizeof(struct sockaddr_in6); |
| 5280 | break; | 5286 | break; |
| 5281 | default: | 5287 | default: |
| 5282 | return -EAFNOSUPPORT; | 5288 | return -EINVAL; |
| 5283 | } | 5289 | } |
| 5284 | 5290 | ||
| 5285 | err = -EINVAL; | 5291 | err = -EINVAL; |
