diff options
Diffstat (limited to 'security/selinux/hooks.c')
-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 398d165f884e..179dd20bec0a 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -4588,6 +4588,7 @@ static int selinux_socket_post_create(struct socket *sock, int family, | |||
4588 | static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) | 4588 | static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) |
4589 | { | 4589 | { |
4590 | struct sock *sk = sock->sk; | 4590 | struct sock *sk = sock->sk; |
4591 | struct sk_security_struct *sksec = sk->sk_security; | ||
4591 | u16 family; | 4592 | u16 family; |
4592 | int err; | 4593 | int err; |
4593 | 4594 | ||
@@ -4599,11 +4600,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
4599 | family = sk->sk_family; | 4600 | family = sk->sk_family; |
4600 | if (family == PF_INET || family == PF_INET6) { | 4601 | if (family == PF_INET || family == PF_INET6) { |
4601 | char *addrp; | 4602 | char *addrp; |
4602 | struct sk_security_struct *sksec = sk->sk_security; | ||
4603 | struct common_audit_data ad; | 4603 | struct common_audit_data ad; |
4604 | struct lsm_network_audit net = {0,}; | 4604 | struct lsm_network_audit net = {0,}; |
4605 | struct sockaddr_in *addr4 = NULL; | 4605 | struct sockaddr_in *addr4 = NULL; |
4606 | struct sockaddr_in6 *addr6 = NULL; | 4606 | struct sockaddr_in6 *addr6 = NULL; |
4607 | u16 family_sa = address->sa_family; | ||
4607 | unsigned short snum; | 4608 | unsigned short snum; |
4608 | u32 sid, node_perm; | 4609 | u32 sid, node_perm; |
4609 | 4610 | ||
@@ -4613,11 +4614,20 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
4613 | * need to check address->sa_family as it is possible to have | 4614 | * need to check address->sa_family as it is possible to have |
4614 | * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET. | 4615 | * sk->sk_family = PF_INET6 with addr->sa_family = AF_INET. |
4615 | */ | 4616 | */ |
4616 | switch (address->sa_family) { | 4617 | switch (family_sa) { |
4618 | case AF_UNSPEC: | ||
4617 | case AF_INET: | 4619 | case AF_INET: |
4618 | if (addrlen < sizeof(struct sockaddr_in)) | 4620 | if (addrlen < sizeof(struct sockaddr_in)) |
4619 | return -EINVAL; | 4621 | return -EINVAL; |
4620 | addr4 = (struct sockaddr_in *)address; | 4622 | addr4 = (struct sockaddr_in *)address; |
4623 | if (family_sa == AF_UNSPEC) { | ||
4624 | /* see __inet_bind(), we only want to allow | ||
4625 | * AF_UNSPEC if the address is INADDR_ANY | ||
4626 | */ | ||
4627 | if (addr4->sin_addr.s_addr != htonl(INADDR_ANY)) | ||
4628 | goto err_af; | ||
4629 | family_sa = AF_INET; | ||
4630 | } | ||
4621 | snum = ntohs(addr4->sin_port); | 4631 | snum = ntohs(addr4->sin_port); |
4622 | addrp = (char *)&addr4->sin_addr.s_addr; | 4632 | addrp = (char *)&addr4->sin_addr.s_addr; |
4623 | break; | 4633 | break; |
@@ -4629,15 +4639,14 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
4629 | addrp = (char *)&addr6->sin6_addr.s6_addr; | 4639 | addrp = (char *)&addr6->sin6_addr.s6_addr; |
4630 | break; | 4640 | break; |
4631 | default: | 4641 | default: |
4632 | /* Note that SCTP services expect -EINVAL, whereas | 4642 | goto err_af; |
4633 | * others expect -EAFNOSUPPORT. | ||
4634 | */ | ||
4635 | if (sksec->sclass == SECCLASS_SCTP_SOCKET) | ||
4636 | return -EINVAL; | ||
4637 | else | ||
4638 | return -EAFNOSUPPORT; | ||
4639 | } | 4643 | } |
4640 | 4644 | ||
4645 | ad.type = LSM_AUDIT_DATA_NET; | ||
4646 | ad.u.net = &net; | ||
4647 | ad.u.net->sport = htons(snum); | ||
4648 | ad.u.net->family = family_sa; | ||
4649 | |||
4641 | if (snum) { | 4650 | if (snum) { |
4642 | int low, high; | 4651 | int low, high; |
4643 | 4652 | ||
@@ -4649,10 +4658,6 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
4649 | snum, &sid); | 4658 | snum, &sid); |
4650 | if (err) | 4659 | if (err) |
4651 | goto out; | 4660 | goto out; |
4652 | ad.type = LSM_AUDIT_DATA_NET; | ||
4653 | ad.u.net = &net; | ||
4654 | ad.u.net->sport = htons(snum); | ||
4655 | ad.u.net->family = family; | ||
4656 | err = avc_has_perm(&selinux_state, | 4661 | err = avc_has_perm(&selinux_state, |
4657 | sksec->sid, sid, | 4662 | sksec->sid, sid, |
4658 | sksec->sclass, | 4663 | sksec->sclass, |
@@ -4684,16 +4689,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
4684 | break; | 4689 | break; |
4685 | } | 4690 | } |
4686 | 4691 | ||
4687 | err = sel_netnode_sid(addrp, family, &sid); | 4692 | err = sel_netnode_sid(addrp, family_sa, &sid); |
4688 | if (err) | 4693 | if (err) |
4689 | goto out; | 4694 | goto out; |
4690 | 4695 | ||
4691 | ad.type = LSM_AUDIT_DATA_NET; | 4696 | if (family_sa == AF_INET) |
4692 | ad.u.net = &net; | ||
4693 | ad.u.net->sport = htons(snum); | ||
4694 | ad.u.net->family = family; | ||
4695 | |||
4696 | if (address->sa_family == AF_INET) | ||
4697 | ad.u.net->v4info.saddr = addr4->sin_addr.s_addr; | 4697 | ad.u.net->v4info.saddr = addr4->sin_addr.s_addr; |
4698 | else | 4698 | else |
4699 | ad.u.net->v6info.saddr = addr6->sin6_addr; | 4699 | ad.u.net->v6info.saddr = addr6->sin6_addr; |
@@ -4706,6 +4706,11 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
4706 | } | 4706 | } |
4707 | out: | 4707 | out: |
4708 | return err; | 4708 | return err; |
4709 | err_af: | ||
4710 | /* Note that SCTP services expect -EINVAL, others -EAFNOSUPPORT. */ | ||
4711 | if (sksec->sclass == SECCLASS_SCTP_SOCKET) | ||
4712 | return -EINVAL; | ||
4713 | return -EAFNOSUPPORT; | ||
4709 | } | 4714 | } |
4710 | 4715 | ||
4711 | /* This supports connect(2) and SCTP connect services such as sctp_connectx(3) | 4716 | /* This supports connect(2) and SCTP connect services such as sctp_connectx(3) |
@@ -4783,7 +4788,7 @@ static int selinux_socket_connect_helper(struct socket *sock, | |||
4783 | ad.type = LSM_AUDIT_DATA_NET; | 4788 | ad.type = LSM_AUDIT_DATA_NET; |
4784 | ad.u.net = &net; | 4789 | ad.u.net = &net; |
4785 | ad.u.net->dport = htons(snum); | 4790 | ad.u.net->dport = htons(snum); |
4786 | ad.u.net->family = sk->sk_family; | 4791 | ad.u.net->family = address->sa_family; |
4787 | err = avc_has_perm(&selinux_state, | 4792 | err = avc_has_perm(&selinux_state, |
4788 | sksec->sid, sid, sksec->sclass, perm, &ad); | 4793 | sksec->sid, sid, sksec->sclass, perm, &ad); |
4789 | if (err) | 4794 | if (err) |
@@ -5284,6 +5289,7 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname, | |||
5284 | while (walk_size < addrlen) { | 5289 | while (walk_size < addrlen) { |
5285 | addr = addr_buf; | 5290 | addr = addr_buf; |
5286 | switch (addr->sa_family) { | 5291 | switch (addr->sa_family) { |
5292 | case AF_UNSPEC: | ||
5287 | case AF_INET: | 5293 | case AF_INET: |
5288 | len = sizeof(struct sockaddr_in); | 5294 | len = sizeof(struct sockaddr_in); |
5289 | break; | 5295 | break; |
@@ -5291,7 +5297,7 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname, | |||
5291 | len = sizeof(struct sockaddr_in6); | 5297 | len = sizeof(struct sockaddr_in6); |
5292 | break; | 5298 | break; |
5293 | default: | 5299 | default: |
5294 | return -EAFNOSUPPORT; | 5300 | return -EINVAL; |
5295 | } | 5301 | } |
5296 | 5302 | ||
5297 | err = -EINVAL; | 5303 | err = -EINVAL; |