diff options
author | Paul Moore <paul.moore@hp.com> | 2010-04-22 14:46:19 -0400 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2010-08-02 01:34:39 -0400 |
commit | 253bfae6e0ad97554799affa0266052968a45808 (patch) | |
tree | c3599a18f06664160a55a20b30428ba4faf6e2c0 /security/selinux | |
parent | 84914b7ed1c5e0f3199a5a6997022758a70fcaff (diff) |
selinux: Convert socket related access controls to use socket labels
At present, the socket related access controls use a mix of inode and
socket labels; while there should be no practical difference (they
_should_ always be the same), it makes the code more confusing. This
patch attempts to convert all of the socket related access control
points (with the exception of some of the inode/fd based controls) to
use the socket's own label. In the process, I also converted the
socket_has_perm() function to take a 'sock' argument instead of a
'socket' since that was adding a bit more overhead in some cases.
Signed-off-by: Paul Moore <paul.moore@hp.com>
Acked-by: Eric Paris <eparis@redhat.com>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/selinux')
-rw-r--r-- | security/selinux/hooks.c | 119 |
1 files changed, 45 insertions, 74 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 01f52424cfe5..e95004010c8b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -3651,26 +3651,19 @@ static u32 socket_sockcreate_sid(const struct task_security_struct *tsec) | |||
3651 | return tsec->sockcreate_sid ? : tsec->sid; | 3651 | return tsec->sockcreate_sid ? : tsec->sid; |
3652 | } | 3652 | } |
3653 | 3653 | ||
3654 | static int socket_has_perm(struct task_struct *task, struct socket *sock, | 3654 | static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) |
3655 | u32 perms) | ||
3656 | { | 3655 | { |
3657 | struct inode_security_struct *isec; | 3656 | struct sk_security_struct *sksec = sk->sk_security; |
3658 | struct common_audit_data ad; | 3657 | struct common_audit_data ad; |
3659 | u32 sid; | 3658 | u32 tsid = task_sid(task); |
3660 | int err = 0; | ||
3661 | 3659 | ||
3662 | isec = SOCK_INODE(sock)->i_security; | 3660 | if (sksec->sid == SECINITSID_KERNEL) |
3663 | 3661 | return 0; | |
3664 | if (isec->sid == SECINITSID_KERNEL) | ||
3665 | goto out; | ||
3666 | sid = task_sid(task); | ||
3667 | 3662 | ||
3668 | COMMON_AUDIT_DATA_INIT(&ad, NET); | 3663 | COMMON_AUDIT_DATA_INIT(&ad, NET); |
3669 | ad.u.net.sk = sock->sk; | 3664 | ad.u.net.sk = sk; |
3670 | err = avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad); | ||
3671 | 3665 | ||
3672 | out: | 3666 | return avc_has_perm(tsid, sksec->sid, sksec->sclass, perms, &ad); |
3673 | return err; | ||
3674 | } | 3667 | } |
3675 | 3668 | ||
3676 | static int selinux_socket_create(int family, int type, | 3669 | static int selinux_socket_create(int family, int type, |
@@ -3722,10 +3715,11 @@ static int selinux_socket_post_create(struct socket *sock, int family, | |||
3722 | 3715 | ||
3723 | static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) | 3716 | static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) |
3724 | { | 3717 | { |
3718 | struct sock *sk = sock->sk; | ||
3725 | u16 family; | 3719 | u16 family; |
3726 | int err; | 3720 | int err; |
3727 | 3721 | ||
3728 | err = socket_has_perm(current, sock, SOCKET__BIND); | 3722 | err = sock_has_perm(current, sk, SOCKET__BIND); |
3729 | if (err) | 3723 | if (err) |
3730 | goto out; | 3724 | goto out; |
3731 | 3725 | ||
@@ -3734,19 +3728,16 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
3734 | * Multiple address binding for SCTP is not supported yet: we just | 3728 | * Multiple address binding for SCTP is not supported yet: we just |
3735 | * check the first address now. | 3729 | * check the first address now. |
3736 | */ | 3730 | */ |
3737 | family = sock->sk->sk_family; | 3731 | family = sk->sk_family; |
3738 | if (family == PF_INET || family == PF_INET6) { | 3732 | if (family == PF_INET || family == PF_INET6) { |
3739 | char *addrp; | 3733 | char *addrp; |
3740 | struct inode_security_struct *isec; | 3734 | struct sk_security_struct *sksec = sk->sk_security; |
3741 | struct common_audit_data ad; | 3735 | struct common_audit_data ad; |
3742 | struct sockaddr_in *addr4 = NULL; | 3736 | struct sockaddr_in *addr4 = NULL; |
3743 | struct sockaddr_in6 *addr6 = NULL; | 3737 | struct sockaddr_in6 *addr6 = NULL; |
3744 | unsigned short snum; | 3738 | unsigned short snum; |
3745 | struct sock *sk = sock->sk; | ||
3746 | u32 sid, node_perm; | 3739 | u32 sid, node_perm; |
3747 | 3740 | ||
3748 | isec = SOCK_INODE(sock)->i_security; | ||
3749 | |||
3750 | if (family == PF_INET) { | 3741 | if (family == PF_INET) { |
3751 | addr4 = (struct sockaddr_in *)address; | 3742 | addr4 = (struct sockaddr_in *)address; |
3752 | snum = ntohs(addr4->sin_port); | 3743 | snum = ntohs(addr4->sin_port); |
@@ -3770,15 +3761,15 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
3770 | COMMON_AUDIT_DATA_INIT(&ad, NET); | 3761 | COMMON_AUDIT_DATA_INIT(&ad, NET); |
3771 | ad.u.net.sport = htons(snum); | 3762 | ad.u.net.sport = htons(snum); |
3772 | ad.u.net.family = family; | 3763 | ad.u.net.family = family; |
3773 | err = avc_has_perm(isec->sid, sid, | 3764 | err = avc_has_perm(sksec->sid, sid, |
3774 | isec->sclass, | 3765 | sksec->sclass, |
3775 | SOCKET__NAME_BIND, &ad); | 3766 | SOCKET__NAME_BIND, &ad); |
3776 | if (err) | 3767 | if (err) |
3777 | goto out; | 3768 | goto out; |
3778 | } | 3769 | } |
3779 | } | 3770 | } |
3780 | 3771 | ||
3781 | switch (isec->sclass) { | 3772 | switch (sksec->sclass) { |
3782 | case SECCLASS_TCP_SOCKET: | 3773 | case SECCLASS_TCP_SOCKET: |
3783 | node_perm = TCP_SOCKET__NODE_BIND; | 3774 | node_perm = TCP_SOCKET__NODE_BIND; |
3784 | break; | 3775 | break; |
@@ -3809,8 +3800,8 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in | |||
3809 | else | 3800 | else |
3810 | ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr); | 3801 | ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr); |
3811 | 3802 | ||
3812 | err = avc_has_perm(isec->sid, sid, | 3803 | err = avc_has_perm(sksec->sid, sid, |
3813 | isec->sclass, node_perm, &ad); | 3804 | sksec->sclass, node_perm, &ad); |
3814 | if (err) | 3805 | if (err) |
3815 | goto out; | 3806 | goto out; |
3816 | } | 3807 | } |
@@ -3821,19 +3812,18 @@ out: | |||
3821 | static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) | 3812 | static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) |
3822 | { | 3813 | { |
3823 | struct sock *sk = sock->sk; | 3814 | struct sock *sk = sock->sk; |
3824 | struct inode_security_struct *isec; | 3815 | struct sk_security_struct *sksec = sk->sk_security; |
3825 | int err; | 3816 | int err; |
3826 | 3817 | ||
3827 | err = socket_has_perm(current, sock, SOCKET__CONNECT); | 3818 | err = sock_has_perm(current, sk, SOCKET__CONNECT); |
3828 | if (err) | 3819 | if (err) |
3829 | return err; | 3820 | return err; |
3830 | 3821 | ||
3831 | /* | 3822 | /* |
3832 | * If a TCP or DCCP socket, check name_connect permission for the port. | 3823 | * If a TCP or DCCP socket, check name_connect permission for the port. |
3833 | */ | 3824 | */ |
3834 | isec = SOCK_INODE(sock)->i_security; | 3825 | if (sksec->sclass == SECCLASS_TCP_SOCKET || |
3835 | if (isec->sclass == SECCLASS_TCP_SOCKET || | 3826 | sksec->sclass == SECCLASS_DCCP_SOCKET) { |
3836 | isec->sclass == SECCLASS_DCCP_SOCKET) { | ||
3837 | struct common_audit_data ad; | 3827 | struct common_audit_data ad; |
3838 | struct sockaddr_in *addr4 = NULL; | 3828 | struct sockaddr_in *addr4 = NULL; |
3839 | struct sockaddr_in6 *addr6 = NULL; | 3829 | struct sockaddr_in6 *addr6 = NULL; |
@@ -3856,13 +3846,13 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, | |||
3856 | if (err) | 3846 | if (err) |
3857 | goto out; | 3847 | goto out; |
3858 | 3848 | ||
3859 | perm = (isec->sclass == SECCLASS_TCP_SOCKET) ? | 3849 | perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ? |
3860 | TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; | 3850 | TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; |
3861 | 3851 | ||
3862 | COMMON_AUDIT_DATA_INIT(&ad, NET); | 3852 | COMMON_AUDIT_DATA_INIT(&ad, NET); |
3863 | ad.u.net.dport = htons(snum); | 3853 | ad.u.net.dport = htons(snum); |
3864 | ad.u.net.family = sk->sk_family; | 3854 | ad.u.net.family = sk->sk_family; |
3865 | err = avc_has_perm(isec->sid, sid, isec->sclass, perm, &ad); | 3855 | err = avc_has_perm(sksec->sid, sid, sksec->sclass, perm, &ad); |
3866 | if (err) | 3856 | if (err) |
3867 | goto out; | 3857 | goto out; |
3868 | } | 3858 | } |
@@ -3875,7 +3865,7 @@ out: | |||
3875 | 3865 | ||
3876 | static int selinux_socket_listen(struct socket *sock, int backlog) | 3866 | static int selinux_socket_listen(struct socket *sock, int backlog) |
3877 | { | 3867 | { |
3878 | return socket_has_perm(current, sock, SOCKET__LISTEN); | 3868 | return sock_has_perm(current, sock->sk, SOCKET__LISTEN); |
3879 | } | 3869 | } |
3880 | 3870 | ||
3881 | static int selinux_socket_accept(struct socket *sock, struct socket *newsock) | 3871 | static int selinux_socket_accept(struct socket *sock, struct socket *newsock) |
@@ -3884,7 +3874,7 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) | |||
3884 | struct inode_security_struct *isec; | 3874 | struct inode_security_struct *isec; |
3885 | struct inode_security_struct *newisec; | 3875 | struct inode_security_struct *newisec; |
3886 | 3876 | ||
3887 | err = socket_has_perm(current, sock, SOCKET__ACCEPT); | 3877 | err = sock_has_perm(current, sock->sk, SOCKET__ACCEPT); |
3888 | if (err) | 3878 | if (err) |
3889 | return err; | 3879 | return err; |
3890 | 3880 | ||
@@ -3901,30 +3891,30 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) | |||
3901 | static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, | 3891 | static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, |
3902 | int size) | 3892 | int size) |
3903 | { | 3893 | { |
3904 | return socket_has_perm(current, sock, SOCKET__WRITE); | 3894 | return sock_has_perm(current, sock->sk, SOCKET__WRITE); |
3905 | } | 3895 | } |
3906 | 3896 | ||
3907 | static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, | 3897 | static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, |
3908 | int size, int flags) | 3898 | int size, int flags) |
3909 | { | 3899 | { |
3910 | return socket_has_perm(current, sock, SOCKET__READ); | 3900 | return sock_has_perm(current, sock->sk, SOCKET__READ); |
3911 | } | 3901 | } |
3912 | 3902 | ||
3913 | static int selinux_socket_getsockname(struct socket *sock) | 3903 | static int selinux_socket_getsockname(struct socket *sock) |
3914 | { | 3904 | { |
3915 | return socket_has_perm(current, sock, SOCKET__GETATTR); | 3905 | return sock_has_perm(current, sock->sk, SOCKET__GETATTR); |
3916 | } | 3906 | } |
3917 | 3907 | ||
3918 | static int selinux_socket_getpeername(struct socket *sock) | 3908 | static int selinux_socket_getpeername(struct socket *sock) |
3919 | { | 3909 | { |
3920 | return socket_has_perm(current, sock, SOCKET__GETATTR); | 3910 | return sock_has_perm(current, sock->sk, SOCKET__GETATTR); |
3921 | } | 3911 | } |
3922 | 3912 | ||
3923 | static int selinux_socket_setsockopt(struct socket *sock, int level, int optname) | 3913 | static int selinux_socket_setsockopt(struct socket *sock, int level, int optname) |
3924 | { | 3914 | { |
3925 | int err; | 3915 | int err; |
3926 | 3916 | ||
3927 | err = socket_has_perm(current, sock, SOCKET__SETOPT); | 3917 | err = sock_has_perm(current, sock->sk, SOCKET__SETOPT); |
3928 | if (err) | 3918 | if (err) |
3929 | return err; | 3919 | return err; |
3930 | 3920 | ||
@@ -3934,12 +3924,12 @@ static int selinux_socket_setsockopt(struct socket *sock, int level, int optname | |||
3934 | static int selinux_socket_getsockopt(struct socket *sock, int level, | 3924 | static int selinux_socket_getsockopt(struct socket *sock, int level, |
3935 | int optname) | 3925 | int optname) |
3936 | { | 3926 | { |
3937 | return socket_has_perm(current, sock, SOCKET__GETOPT); | 3927 | return sock_has_perm(current, sock->sk, SOCKET__GETOPT); |
3938 | } | 3928 | } |
3939 | 3929 | ||
3940 | static int selinux_socket_shutdown(struct socket *sock, int how) | 3930 | static int selinux_socket_shutdown(struct socket *sock, int how) |
3941 | { | 3931 | { |
3942 | return socket_has_perm(current, sock, SOCKET__SHUTDOWN); | 3932 | return sock_has_perm(current, sock->sk, SOCKET__SHUTDOWN); |
3943 | } | 3933 | } |
3944 | 3934 | ||
3945 | static int selinux_socket_unix_stream_connect(struct socket *sock, | 3935 | static int selinux_socket_unix_stream_connect(struct socket *sock, |
@@ -3977,23 +3967,15 @@ static int selinux_socket_unix_stream_connect(struct socket *sock, | |||
3977 | static int selinux_socket_unix_may_send(struct socket *sock, | 3967 | static int selinux_socket_unix_may_send(struct socket *sock, |
3978 | struct socket *other) | 3968 | struct socket *other) |
3979 | { | 3969 | { |
3980 | struct inode_security_struct *isec; | 3970 | struct sk_security_struct *ssec = sock->sk->sk_security; |
3981 | struct inode_security_struct *other_isec; | 3971 | struct sk_security_struct *osec = other->sk->sk_security; |
3982 | struct common_audit_data ad; | 3972 | struct common_audit_data ad; |
3983 | int err; | ||
3984 | |||
3985 | isec = SOCK_INODE(sock)->i_security; | ||
3986 | other_isec = SOCK_INODE(other)->i_security; | ||
3987 | 3973 | ||
3988 | COMMON_AUDIT_DATA_INIT(&ad, NET); | 3974 | COMMON_AUDIT_DATA_INIT(&ad, NET); |
3989 | ad.u.net.sk = other->sk; | 3975 | ad.u.net.sk = other->sk; |
3990 | 3976 | ||
3991 | err = avc_has_perm(isec->sid, other_isec->sid, | 3977 | return avc_has_perm(ssec->sid, osec->sid, osec->sclass, SOCKET__SENDTO, |
3992 | isec->sclass, SOCKET__SENDTO, &ad); | 3978 | &ad); |
3993 | if (err) | ||
3994 | return err; | ||
3995 | |||
3996 | return 0; | ||
3997 | } | 3979 | } |
3998 | 3980 | ||
3999 | static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family, | 3981 | static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family, |
@@ -4132,26 +4114,18 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op | |||
4132 | int err = 0; | 4114 | int err = 0; |
4133 | char *scontext; | 4115 | char *scontext; |
4134 | u32 scontext_len; | 4116 | u32 scontext_len; |
4135 | struct sk_security_struct *sksec; | 4117 | struct sk_security_struct *sksec = sock->sk->sk_security; |
4136 | struct inode_security_struct *isec; | ||
4137 | u32 peer_sid = SECSID_NULL; | 4118 | u32 peer_sid = SECSID_NULL; |
4138 | 4119 | ||
4139 | isec = SOCK_INODE(sock)->i_security; | 4120 | if (sksec->sclass == SECCLASS_UNIX_STREAM_SOCKET || |
4140 | 4121 | sksec->sclass == SECCLASS_TCP_SOCKET) | |
4141 | if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET || | ||
4142 | isec->sclass == SECCLASS_TCP_SOCKET) { | ||
4143 | sksec = sock->sk->sk_security; | ||
4144 | peer_sid = sksec->peer_sid; | 4122 | peer_sid = sksec->peer_sid; |
4145 | } | 4123 | if (peer_sid == SECSID_NULL) |
4146 | if (peer_sid == SECSID_NULL) { | 4124 | return -ENOPROTOOPT; |
4147 | err = -ENOPROTOOPT; | ||
4148 | goto out; | ||
4149 | } | ||
4150 | 4125 | ||
4151 | err = security_sid_to_context(peer_sid, &scontext, &scontext_len); | 4126 | err = security_sid_to_context(peer_sid, &scontext, &scontext_len); |
4152 | |||
4153 | if (err) | 4127 | if (err) |
4154 | goto out; | 4128 | return err; |
4155 | 4129 | ||
4156 | if (scontext_len > len) { | 4130 | if (scontext_len > len) { |
4157 | err = -ERANGE; | 4131 | err = -ERANGE; |
@@ -4164,9 +4138,7 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op | |||
4164 | out_len: | 4138 | out_len: |
4165 | if (put_user(scontext_len, optlen)) | 4139 | if (put_user(scontext_len, optlen)) |
4166 | err = -EFAULT; | 4140 | err = -EFAULT; |
4167 | |||
4168 | kfree(scontext); | 4141 | kfree(scontext); |
4169 | out: | ||
4170 | return err; | 4142 | return err; |
4171 | } | 4143 | } |
4172 | 4144 | ||
@@ -4378,8 +4350,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) | |||
4378 | int err = 0; | 4350 | int err = 0; |
4379 | u32 perm; | 4351 | u32 perm; |
4380 | struct nlmsghdr *nlh; | 4352 | struct nlmsghdr *nlh; |
4381 | struct socket *sock = sk->sk_socket; | 4353 | struct sk_security_struct *sksec = sk->sk_security; |
4382 | struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; | ||
4383 | 4354 | ||
4384 | if (skb->len < NLMSG_SPACE(0)) { | 4355 | if (skb->len < NLMSG_SPACE(0)) { |
4385 | err = -EINVAL; | 4356 | err = -EINVAL; |
@@ -4387,13 +4358,13 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) | |||
4387 | } | 4358 | } |
4388 | nlh = nlmsg_hdr(skb); | 4359 | nlh = nlmsg_hdr(skb); |
4389 | 4360 | ||
4390 | err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm); | 4361 | err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); |
4391 | if (err) { | 4362 | if (err) { |
4392 | if (err == -EINVAL) { | 4363 | if (err == -EINVAL) { |
4393 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_SELINUX_ERR, | 4364 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_SELINUX_ERR, |
4394 | "SELinux: unrecognized netlink message" | 4365 | "SELinux: unrecognized netlink message" |
4395 | " type=%hu for sclass=%hu\n", | 4366 | " type=%hu for sclass=%hu\n", |
4396 | nlh->nlmsg_type, isec->sclass); | 4367 | nlh->nlmsg_type, sksec->sclass); |
4397 | if (!selinux_enforcing || security_get_allow_unknown()) | 4368 | if (!selinux_enforcing || security_get_allow_unknown()) |
4398 | err = 0; | 4369 | err = 0; |
4399 | } | 4370 | } |
@@ -4404,7 +4375,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) | |||
4404 | goto out; | 4375 | goto out; |
4405 | } | 4376 | } |
4406 | 4377 | ||
4407 | err = socket_has_perm(current, sock, perm); | 4378 | err = sock_has_perm(current, sk, perm); |
4408 | out: | 4379 | out: |
4409 | return err; | 4380 | return err; |
4410 | } | 4381 | } |