Skip to content

Commit

Permalink
selinux: Convert socket related access controls to use socket labels
Browse files Browse the repository at this point in the history
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 <[email protected]>
Acked-by: Eric Paris <[email protected]>
Signed-off-by: James Morris <[email protected]>
  • Loading branch information
pcmoore authored and James Morris committed Aug 2, 2010
1 parent 84914b7 commit 253bfae
Showing 1 changed file with 45 additions and 74 deletions.
119 changes: 45 additions & 74 deletions security/selinux/hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -3651,26 +3651,19 @@ static u32 socket_sockcreate_sid(const struct task_security_struct *tsec)
return tsec->sockcreate_sid ? : tsec->sid;
}

static int socket_has_perm(struct task_struct *task, struct socket *sock,
u32 perms)
static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms)
{
struct inode_security_struct *isec;
struct sk_security_struct *sksec = sk->sk_security;
struct common_audit_data ad;
u32 sid;
int err = 0;
u32 tsid = task_sid(task);

isec = SOCK_INODE(sock)->i_security;

if (isec->sid == SECINITSID_KERNEL)
goto out;
sid = task_sid(task);
if (sksec->sid == SECINITSID_KERNEL)
return 0;

COMMON_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.sk = sock->sk;
err = avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad);
ad.u.net.sk = sk;

out:
return err;
return avc_has_perm(tsid, sksec->sid, sksec->sclass, perms, &ad);
}

static int selinux_socket_create(int family, int type,
Expand Down Expand Up @@ -3722,10 +3715,11 @@ static int selinux_socket_post_create(struct socket *sock, int family,

static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
{
struct sock *sk = sock->sk;
u16 family;
int err;

err = socket_has_perm(current, sock, SOCKET__BIND);
err = sock_has_perm(current, sk, SOCKET__BIND);
if (err)
goto out;

Expand All @@ -3734,19 +3728,16 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
* Multiple address binding for SCTP is not supported yet: we just
* check the first address now.
*/
family = sock->sk->sk_family;
family = sk->sk_family;
if (family == PF_INET || family == PF_INET6) {
char *addrp;
struct inode_security_struct *isec;
struct sk_security_struct *sksec = sk->sk_security;
struct common_audit_data ad;
struct sockaddr_in *addr4 = NULL;
struct sockaddr_in6 *addr6 = NULL;
unsigned short snum;
struct sock *sk = sock->sk;
u32 sid, node_perm;

isec = SOCK_INODE(sock)->i_security;

if (family == PF_INET) {
addr4 = (struct sockaddr_in *)address;
snum = ntohs(addr4->sin_port);
Expand All @@ -3770,15 +3761,15 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
COMMON_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.sport = htons(snum);
ad.u.net.family = family;
err = avc_has_perm(isec->sid, sid,
isec->sclass,
err = avc_has_perm(sksec->sid, sid,
sksec->sclass,
SOCKET__NAME_BIND, &ad);
if (err)
goto out;
}
}

switch (isec->sclass) {
switch (sksec->sclass) {
case SECCLASS_TCP_SOCKET:
node_perm = TCP_SOCKET__NODE_BIND;
break;
Expand Down Expand Up @@ -3809,8 +3800,8 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
else
ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr);

err = avc_has_perm(isec->sid, sid,
isec->sclass, node_perm, &ad);
err = avc_has_perm(sksec->sid, sid,
sksec->sclass, node_perm, &ad);
if (err)
goto out;
}
Expand All @@ -3821,19 +3812,18 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in
static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
{
struct sock *sk = sock->sk;
struct inode_security_struct *isec;
struct sk_security_struct *sksec = sk->sk_security;
int err;

err = socket_has_perm(current, sock, SOCKET__CONNECT);
err = sock_has_perm(current, sk, SOCKET__CONNECT);
if (err)
return err;

/*
* If a TCP or DCCP socket, check name_connect permission for the port.
*/
isec = SOCK_INODE(sock)->i_security;
if (isec->sclass == SECCLASS_TCP_SOCKET ||
isec->sclass == SECCLASS_DCCP_SOCKET) {
if (sksec->sclass == SECCLASS_TCP_SOCKET ||
sksec->sclass == SECCLASS_DCCP_SOCKET) {
struct common_audit_data ad;
struct sockaddr_in *addr4 = NULL;
struct sockaddr_in6 *addr6 = NULL;
Expand All @@ -3856,13 +3846,13 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,
if (err)
goto out;

perm = (isec->sclass == SECCLASS_TCP_SOCKET) ?
perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ?
TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT;

COMMON_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.dport = htons(snum);
ad.u.net.family = sk->sk_family;
err = avc_has_perm(isec->sid, sid, isec->sclass, perm, &ad);
err = avc_has_perm(sksec->sid, sid, sksec->sclass, perm, &ad);
if (err)
goto out;
}
Expand All @@ -3875,7 +3865,7 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address,

static int selinux_socket_listen(struct socket *sock, int backlog)
{
return socket_has_perm(current, sock, SOCKET__LISTEN);
return sock_has_perm(current, sock->sk, SOCKET__LISTEN);
}

static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
Expand All @@ -3884,7 +3874,7 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
struct inode_security_struct *isec;
struct inode_security_struct *newisec;

err = socket_has_perm(current, sock, SOCKET__ACCEPT);
err = sock_has_perm(current, sock->sk, SOCKET__ACCEPT);
if (err)
return err;

Expand All @@ -3901,30 +3891,30 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg,
int size)
{
return socket_has_perm(current, sock, SOCKET__WRITE);
return sock_has_perm(current, sock->sk, SOCKET__WRITE);
}

static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg,
int size, int flags)
{
return socket_has_perm(current, sock, SOCKET__READ);
return sock_has_perm(current, sock->sk, SOCKET__READ);
}

static int selinux_socket_getsockname(struct socket *sock)
{
return socket_has_perm(current, sock, SOCKET__GETATTR);
return sock_has_perm(current, sock->sk, SOCKET__GETATTR);
}

static int selinux_socket_getpeername(struct socket *sock)
{
return socket_has_perm(current, sock, SOCKET__GETATTR);
return sock_has_perm(current, sock->sk, SOCKET__GETATTR);
}

static int selinux_socket_setsockopt(struct socket *sock, int level, int optname)
{
int err;

err = socket_has_perm(current, sock, SOCKET__SETOPT);
err = sock_has_perm(current, sock->sk, SOCKET__SETOPT);
if (err)
return err;

Expand All @@ -3934,12 +3924,12 @@ static int selinux_socket_setsockopt(struct socket *sock, int level, int optname
static int selinux_socket_getsockopt(struct socket *sock, int level,
int optname)
{
return socket_has_perm(current, sock, SOCKET__GETOPT);
return sock_has_perm(current, sock->sk, SOCKET__GETOPT);
}

static int selinux_socket_shutdown(struct socket *sock, int how)
{
return socket_has_perm(current, sock, SOCKET__SHUTDOWN);
return sock_has_perm(current, sock->sk, SOCKET__SHUTDOWN);
}

static int selinux_socket_unix_stream_connect(struct socket *sock,
Expand Down Expand Up @@ -3977,23 +3967,15 @@ static int selinux_socket_unix_stream_connect(struct socket *sock,
static int selinux_socket_unix_may_send(struct socket *sock,
struct socket *other)
{
struct inode_security_struct *isec;
struct inode_security_struct *other_isec;
struct sk_security_struct *ssec = sock->sk->sk_security;
struct sk_security_struct *osec = other->sk->sk_security;
struct common_audit_data ad;
int err;

isec = SOCK_INODE(sock)->i_security;
other_isec = SOCK_INODE(other)->i_security;

COMMON_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.sk = other->sk;

err = avc_has_perm(isec->sid, other_isec->sid,
isec->sclass, SOCKET__SENDTO, &ad);
if (err)
return err;

return 0;
return avc_has_perm(ssec->sid, osec->sid, osec->sclass, SOCKET__SENDTO,
&ad);
}

static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family,
Expand Down Expand Up @@ -4132,26 +4114,18 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op
int err = 0;
char *scontext;
u32 scontext_len;
struct sk_security_struct *sksec;
struct inode_security_struct *isec;
struct sk_security_struct *sksec = sock->sk->sk_security;
u32 peer_sid = SECSID_NULL;

isec = SOCK_INODE(sock)->i_security;

if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET ||
isec->sclass == SECCLASS_TCP_SOCKET) {
sksec = sock->sk->sk_security;
if (sksec->sclass == SECCLASS_UNIX_STREAM_SOCKET ||
sksec->sclass == SECCLASS_TCP_SOCKET)
peer_sid = sksec->peer_sid;
}
if (peer_sid == SECSID_NULL) {
err = -ENOPROTOOPT;
goto out;
}
if (peer_sid == SECSID_NULL)
return -ENOPROTOOPT;

err = security_sid_to_context(peer_sid, &scontext, &scontext_len);

if (err)
goto out;
return err;

if (scontext_len > len) {
err = -ERANGE;
Expand All @@ -4164,9 +4138,7 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op
out_len:
if (put_user(scontext_len, optlen))
err = -EFAULT;

kfree(scontext);
out:
return err;
}

Expand Down Expand Up @@ -4378,22 +4350,21 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
int err = 0;
u32 perm;
struct nlmsghdr *nlh;
struct socket *sock = sk->sk_socket;
struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
struct sk_security_struct *sksec = sk->sk_security;

if (skb->len < NLMSG_SPACE(0)) {
err = -EINVAL;
goto out;
}
nlh = nlmsg_hdr(skb);

err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm);
err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm);
if (err) {
if (err == -EINVAL) {
audit_log(current->audit_context, GFP_KERNEL, AUDIT_SELINUX_ERR,
"SELinux: unrecognized netlink message"
" type=%hu for sclass=%hu\n",
nlh->nlmsg_type, isec->sclass);
nlh->nlmsg_type, sksec->sclass);
if (!selinux_enforcing || security_get_allow_unknown())
err = 0;
}
Expand All @@ -4404,7 +4375,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb)
goto out;
}

err = socket_has_perm(current, sock, perm);
err = sock_has_perm(current, sk, perm);
out:
return err;
}
Expand Down

0 comments on commit 253bfae

Please sign in to comment.