diff options
author | Alexey Kodanev <alexey.kodanev@oracle.com> | 2018-05-11 13:15:11 -0400 |
---|---|---|
committer | Paul Moore <paul@paul-moore.com> | 2018-05-14 15:17:02 -0400 |
commit | 0f8db8cc73df60b3de9a5eebd8f117b56eff5b03 (patch) | |
tree | c890791d42e34d4dcb9bad3307143246a13adb81 /security/selinux/hooks.c | |
parent | 6b6bc6205d98796361962ee282a063f18ba8dc57 (diff) |
selinux: add AF_UNSPEC and INADDR_ANY checks to selinux_socket_bind()
Commit d452930fd3b9 ("selinux: Add SCTP support") breaks compatibility
with the old programs that can pass sockaddr_in structure with AF_UNSPEC
and INADDR_ANY to bind(). As a result, bind() returns EAFNOSUPPORT error.
This was found with LTP/asapi_01 test.
Similar to commit 29c486df6a20 ("net: ipv4: relax AF_INET check in
bind()"), which relaxed AF_INET check for compatibility, add AF_UNSPEC
case to AF_INET and make sure that the address is INADDR_ANY.
Fixes: d452930fd3b9 ("selinux: Add SCTP support")
Signed-off-by: Alexey Kodanev <alexey.kodanev@oracle.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
Diffstat (limited to 'security/selinux/hooks.c')
-rw-r--r-- | security/selinux/hooks.c | 29 |
1 files changed, 19 insertions, 10 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 21b377aef69a..16df6cca9a1b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -4568,6 +4568,7 @@ static int selinux_socket_post_create(struct socket *sock, int family, | |||
4568 | static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) | 4568 | static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) |
4569 | { | 4569 | { |
4570 | struct sock *sk = sock->sk; | 4570 | struct sock *sk = sock->sk; |
4571 | struct sk_security_struct *sksec = sk->sk_security; | ||
4571 | u16 family; | 4572 | u16 family; |
4572 | int err; | 4573 | int err; |
4573 | 4574 | ||
@@ -4579,11 +4580,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
4579 | family = sk->sk_family; | 4580 | family = sk->sk_family; |
4580 | if (family == PF_INET || family == PF_INET6) { | 4581 | if (family == PF_INET || family == PF_INET6) { |
4581 | char *addrp; | 4582 | char *addrp; |
4582 | struct sk_security_struct *sksec = sk->sk_security; | ||
4583 | struct common_audit_data ad; | 4583 | struct common_audit_data ad; |
4584 | struct lsm_network_audit net = {0,}; | 4584 | struct lsm_network_audit net = {0,}; |
4585 | struct sockaddr_in *addr4 = NULL; | 4585 | struct sockaddr_in *addr4 = NULL; |
4586 | struct sockaddr_in6 *addr6 = NULL; | 4586 | struct sockaddr_in6 *addr6 = NULL; |
4587 | u16 family_sa = address->sa_family; | ||
4587 | unsigned short snum; | 4588 | unsigned short snum; |
4588 | u32 sid, node_perm; | 4589 | u32 sid, node_perm; |
4589 | 4590 | ||
@@ -4593,11 +4594,20 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
4593 | * need to check address->sa_family as it is possible to have | 4594 | * need to check address->sa_family as it is possible to have |
4594 | * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET. | 4595 | * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET. |
4595 | */ | 4596 | */ |
4596 | switch (address->sa_family) { | 4597 | switch (family_sa) { |
4598 | case AF_UNSPEC: | ||
4597 | case AF_INET: | 4599 | case AF_INET: |
4598 | if (addrlen < sizeof(struct sockaddr_in)) | 4600 | if (addrlen < sizeof(struct sockaddr_in)) |
4599 | return -EINVAL; | 4601 | return -EINVAL; |
4600 | addr4 = (struct sockaddr_in *)address; | 4602 | addr4 = (struct sockaddr_in *)address; |
4603 | if (family_sa == AF_UNSPEC) { | ||
4604 | /* see __inet_bind(), we only want to allow | ||
4605 | * AF_UNSPEC if the address is INADDR_ANY | ||
4606 | */ | ||
4607 | if (addr4->sin_addr.s_addr != htonl(INADDR_ANY)) | ||
4608 | goto err_af; | ||
4609 | family_sa = AF_INET; | ||
4610 | } | ||
4601 | snum = ntohs(addr4->sin_port); | 4611 | snum = ntohs(addr4->sin_port); |
4602 | addrp = (char *)&addr4->sin_addr.s_addr; | 4612 | addrp = (char *)&addr4->sin_addr.s_addr; |
4603 | break; | 4613 | break; |
@@ -4609,13 +4619,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
4609 | addrp = (char *)&addr6->sin6_addr.s6_addr; | 4619 | addrp = (char *)&addr6->sin6_addr.s6_addr; |
4610 | break; | 4620 | break; |
4611 | default: | 4621 | default: |
4612 | /* Note that SCTP services expect -EINVAL, whereas | 4622 | goto err_af; |
4613 | * others expect -EAFNOSUPPORT. | ||
4614 | */ | ||
4615 | if (sksec->sclass == SECCLASS_SCTP_SOCKET) | ||
4616 | return -EINVAL; | ||
4617 | else | ||
4618 | return -EAFNOSUPPORT; | ||
4619 | } | 4623 | } |
4620 | 4624 | ||
4621 | if (snum) { | 4625 | if (snum) { |
@@ -4673,7 +4677,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
4673 | ad.u.net->sport = htons(snum); | 4677 | ad.u.net->sport = htons(snum); |
4674 | ad.u.net->family = family; | 4678 | ad.u.net->family = family; |
4675 | 4679 | ||
4676 | if (address->sa_family == AF_INET) | 4680 | if (family_sa == AF_INET) |
4677 | ad.u.net->v4info.saddr = addr4->sin_addr.s_addr; | 4681 | ad.u.net->v4info.saddr = addr4->sin_addr.s_addr; |
4678 | else | 4682 | else |
4679 | ad.u.net->v6info.saddr = addr6->sin6_addr; | 4683 | ad.u.net->v6info.saddr = addr6->sin6_addr; |
@@ -4686,6 +4690,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
4686 | } | 4690 | } |
4687 | out: | 4691 | out: |
4688 | return err; | 4692 | return err; |
4693 | err_af: | ||
4694 | /* Note that SCTP services expect -EINVAL, others -EAFNOSUPPORT. */ | ||
4695 | if (sksec->sclass == SECCLASS_SCTP_SOCKET) | ||
4696 | return -EINVAL; | ||
4697 | return -EAFNOSUPPORT; | ||
4689 | } | 4698 | } |
4690 | 4699 | ||
4691 | /* This supports connect(2) and SCTP connect services such as sctp_connectx(3) | 4700 | /* This supports connect(2) and SCTP connect services such as sctp_connectx(3) |