Commit 0f8db8cc authored by Alexey Kodanev's avatar Alexey Kodanev Committed by Paul Moore

selinux: add AF_UNSPEC and INADDR_ANY checks to selinux_socket_bind()

Commit d452930f ("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 29c486df ("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: d452930f ("selinux: Add SCTP support")
Signed-off-by: default avatarAlexey Kodanev <alexey.kodanev@oracle.com>
Signed-off-by: default avatarPaul Moore <paul@paul-moore.com>
parent 6b6bc620
...@@ -4568,6 +4568,7 @@ static int selinux_socket_post_create(struct socket *sock, int family, ...@@ -4568,6 +4568,7 @@ static int selinux_socket_post_create(struct socket *sock, int family,
static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
{ {
struct sock *sk = sock->sk; struct sock *sk = sock->sk;
struct sk_security_struct *sksec = sk->sk_security;
u16 family; u16 family;
int err; int err;
...@@ -4579,11 +4580,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in ...@@ -4579,11 +4580,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
family = sk->sk_family; family = sk->sk_family;
if (family == PF_INET || family == PF_INET6) { if (family == PF_INET || family == PF_INET6) {
char *addrp; char *addrp;
struct sk_security_struct *sksec = sk->sk_security;
struct common_audit_data ad; struct common_audit_data ad;
struct lsm_network_audit net = {0,}; struct lsm_network_audit net = {0,};
struct sockaddr_in *addr4 = NULL; struct sockaddr_in *addr4 = NULL;
struct sockaddr_in6 *addr6 = NULL; struct sockaddr_in6 *addr6 = NULL;
u16 family_sa = address->sa_family;
unsigned short snum; unsigned short snum;
u32 sid, node_perm; u32 sid, node_perm;
...@@ -4593,11 +4594,20 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in ...@@ -4593,11 +4594,20 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
* need to check address->sa_family as it is possible to have * need to check address->sa_family as it is possible to have
* sk->sk_family = PF_INET6 with addr->sa_family = AF_INET. * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET.
*/ */
switch (address->sa_family) { switch (family_sa) {
case AF_UNSPEC:
case AF_INET: case AF_INET:
if (addrlen < sizeof(struct sockaddr_in)) if (addrlen < sizeof(struct sockaddr_in))
return -EINVAL; return -EINVAL;
addr4 = (struct sockaddr_in *)address; addr4 = (struct sockaddr_in *)address;
if (family_sa == AF_UNSPEC) {
/* see __inet_bind(), we only want to allow
* AF_UNSPEC if the address is INADDR_ANY
*/
if (addr4->sin_addr.s_addr != htonl(INADDR_ANY))
goto err_af;
family_sa = AF_INET;
}
snum = ntohs(addr4->sin_port); snum = ntohs(addr4->sin_port);
addrp = (char *)&addr4->sin_addr.s_addr; addrp = (char *)&addr4->sin_addr.s_addr;
break; break;
...@@ -4609,13 +4619,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in ...@@ -4609,13 +4619,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
addrp = (char *)&addr6->sin6_addr.s6_addr; addrp = (char *)&addr6->sin6_addr.s6_addr;
break; break;
default: default:
/* Note that SCTP services expect -EINVAL, whereas goto err_af;
* others expect -EAFNOSUPPORT.
*/
if (sksec->sclass == SECCLASS_SCTP_SOCKET)
return -EINVAL;
else
return -EAFNOSUPPORT;
} }
if (snum) { if (snum) {
...@@ -4673,7 +4677,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in ...@@ -4673,7 +4677,7 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
ad.u.net->sport = htons(snum); ad.u.net->sport = htons(snum);
ad.u.net->family = family; ad.u.net->family = family;
if (address->sa_family == AF_INET) if (family_sa == AF_INET)
ad.u.net->v4info.saddr = addr4->sin_addr.s_addr; ad.u.net->v4info.saddr = addr4->sin_addr.s_addr;
else else
ad.u.net->v6info.saddr = addr6->sin6_addr; 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,6 +4690,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
} }
out: out:
return err; return err;
err_af:
/* Note that SCTP services expect -EINVAL, others -EAFNOSUPPORT. */
if (sksec->sclass == SECCLASS_SCTP_SOCKET)
return -EINVAL;
return -EAFNOSUPPORT;
} }
/* This supports connect(2) and SCTP connect services such as sctp_connectx(3) /* This supports connect(2) and SCTP connect services such as sctp_connectx(3)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment