Commit 3610cda5 authored by David S. Miller's avatar David S. Miller

af_unix: Avoid socket->sk NULL OOPS in stream connect security hooks.

unix_release() can asynchornously set socket->sk to NULL, and
it does so without holding the unix_state_lock() on "other"
during stream connects.

However, the reverse mapping, sk->sk_socket, is only transitioned
to NULL under the unix_state_lock().

Therefore make the security hooks follow the reverse mapping instead
of the forward mapping.
Reported-by: default avatarJeremy Fitzhardinge <jeremy@goop.org>
Reported-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 44b82883
...@@ -796,8 +796,9 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) ...@@ -796,8 +796,9 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts)
* @unix_stream_connect: * @unix_stream_connect:
* Check permissions before establishing a Unix domain stream connection * Check permissions before establishing a Unix domain stream connection
* between @sock and @other. * between @sock and @other.
* @sock contains the socket structure. * @sock contains the sock structure.
* @other contains the peer socket structure. * @other contains the peer sock structure.
* @newsk contains the new sock structure.
* Return 0 if permission is granted. * Return 0 if permission is granted.
* @unix_may_send: * @unix_may_send:
* Check permissions before connecting or sending datagrams from @sock to * Check permissions before connecting or sending datagrams from @sock to
...@@ -1568,8 +1569,7 @@ struct security_operations { ...@@ -1568,8 +1569,7 @@ struct security_operations {
int (*inode_getsecctx)(struct inode *inode, void **ctx, u32 *ctxlen); int (*inode_getsecctx)(struct inode *inode, void **ctx, u32 *ctxlen);
#ifdef CONFIG_SECURITY_NETWORK #ifdef CONFIG_SECURITY_NETWORK
int (*unix_stream_connect) (struct socket *sock, int (*unix_stream_connect) (struct sock *sock, struct sock *other, struct sock *newsk);
struct socket *other, struct sock *newsk);
int (*unix_may_send) (struct socket *sock, struct socket *other); int (*unix_may_send) (struct socket *sock, struct socket *other);
int (*socket_create) (int family, int type, int protocol, int kern); int (*socket_create) (int family, int type, int protocol, int kern);
...@@ -2525,8 +2525,7 @@ static inline int security_inode_getsecctx(struct inode *inode, void **ctx, u32 ...@@ -2525,8 +2525,7 @@ static inline int security_inode_getsecctx(struct inode *inode, void **ctx, u32
#ifdef CONFIG_SECURITY_NETWORK #ifdef CONFIG_SECURITY_NETWORK
int security_unix_stream_connect(struct socket *sock, struct socket *other, int security_unix_stream_connect(struct sock *sock, struct sock *other, struct sock *newsk);
struct sock *newsk);
int security_unix_may_send(struct socket *sock, struct socket *other); int security_unix_may_send(struct socket *sock, struct socket *other);
int security_socket_create(int family, int type, int protocol, int kern); int security_socket_create(int family, int type, int protocol, int kern);
int security_socket_post_create(struct socket *sock, int family, int security_socket_post_create(struct socket *sock, int family,
...@@ -2567,8 +2566,8 @@ void security_tun_dev_post_create(struct sock *sk); ...@@ -2567,8 +2566,8 @@ void security_tun_dev_post_create(struct sock *sk);
int security_tun_dev_attach(struct sock *sk); int security_tun_dev_attach(struct sock *sk);
#else /* CONFIG_SECURITY_NETWORK */ #else /* CONFIG_SECURITY_NETWORK */
static inline int security_unix_stream_connect(struct socket *sock, static inline int security_unix_stream_connect(struct sock *sock,
struct socket *other, struct sock *other,
struct sock *newsk) struct sock *newsk)
{ {
return 0; return 0;
......
...@@ -1157,7 +1157,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr, ...@@ -1157,7 +1157,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
goto restart; goto restart;
} }
err = security_unix_stream_connect(sock, other->sk_socket, newsk); err = security_unix_stream_connect(sk, other, newsk);
if (err) { if (err) {
unix_state_unlock(sk); unix_state_unlock(sk);
goto out_unlock; goto out_unlock;
......
...@@ -548,7 +548,7 @@ static int cap_sem_semop(struct sem_array *sma, struct sembuf *sops, ...@@ -548,7 +548,7 @@ static int cap_sem_semop(struct sem_array *sma, struct sembuf *sops,
} }
#ifdef CONFIG_SECURITY_NETWORK #ifdef CONFIG_SECURITY_NETWORK
static int cap_unix_stream_connect(struct socket *sock, struct socket *other, static int cap_unix_stream_connect(struct sock *sock, struct sock *other,
struct sock *newsk) struct sock *newsk)
{ {
return 0; return 0;
......
...@@ -977,8 +977,7 @@ EXPORT_SYMBOL(security_inode_getsecctx); ...@@ -977,8 +977,7 @@ EXPORT_SYMBOL(security_inode_getsecctx);
#ifdef CONFIG_SECURITY_NETWORK #ifdef CONFIG_SECURITY_NETWORK
int security_unix_stream_connect(struct socket *sock, struct socket *other, int security_unix_stream_connect(struct sock *sock, struct sock *other, struct sock *newsk)
struct sock *newsk)
{ {
return security_ops->unix_stream_connect(sock, other, newsk); return security_ops->unix_stream_connect(sock, other, newsk);
} }
......
...@@ -3921,18 +3921,18 @@ static int selinux_socket_shutdown(struct socket *sock, int how) ...@@ -3921,18 +3921,18 @@ static int selinux_socket_shutdown(struct socket *sock, int how)
return sock_has_perm(current, sock->sk, SOCKET__SHUTDOWN); return sock_has_perm(current, sock->sk, SOCKET__SHUTDOWN);
} }
static int selinux_socket_unix_stream_connect(struct socket *sock, static int selinux_socket_unix_stream_connect(struct sock *sock,
struct socket *other, struct sock *other,
struct sock *newsk) struct sock *newsk)
{ {
struct sk_security_struct *sksec_sock = sock->sk->sk_security; struct sk_security_struct *sksec_sock = sock->sk_security;
struct sk_security_struct *sksec_other = other->sk->sk_security; struct sk_security_struct *sksec_other = other->sk_security;
struct sk_security_struct *sksec_new = newsk->sk_security; struct sk_security_struct *sksec_new = newsk->sk_security;
struct common_audit_data ad; struct common_audit_data ad;
int err; int err;
COMMON_AUDIT_DATA_INIT(&ad, NET); COMMON_AUDIT_DATA_INIT(&ad, NET);
ad.u.net.sk = other->sk; ad.u.net.sk = other;
err = avc_has_perm(sksec_sock->sid, sksec_other->sid, err = avc_has_perm(sksec_sock->sid, sksec_other->sid,
sksec_other->sclass, sksec_other->sclass,
......
...@@ -2408,22 +2408,22 @@ static int smack_setprocattr(struct task_struct *p, char *name, ...@@ -2408,22 +2408,22 @@ static int smack_setprocattr(struct task_struct *p, char *name,
/** /**
* smack_unix_stream_connect - Smack access on UDS * smack_unix_stream_connect - Smack access on UDS
* @sock: one socket * @sock: one sock
* @other: the other socket * @other: the other sock
* @newsk: unused * @newsk: unused
* *
* Return 0 if a subject with the smack of sock could access * Return 0 if a subject with the smack of sock could access
* an object with the smack of other, otherwise an error code * an object with the smack of other, otherwise an error code
*/ */
static int smack_unix_stream_connect(struct socket *sock, static int smack_unix_stream_connect(struct sock *sock,
struct socket *other, struct sock *newsk) struct sock *other, struct sock *newsk)
{ {
struct inode *sp = SOCK_INODE(sock); struct inode *sp = SOCK_INODE(sock->sk_socket);
struct inode *op = SOCK_INODE(other); struct inode *op = SOCK_INODE(other->sk_socket);
struct smk_audit_info ad; struct smk_audit_info ad;
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NET); smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_NET);
smk_ad_setfield_u_net_sk(&ad, other->sk); smk_ad_setfield_u_net_sk(&ad, other);
return smk_access(smk_of_inode(sp), smk_of_inode(op), return smk_access(smk_of_inode(sp), smk_of_inode(op),
MAY_READWRITE, &ad); MAY_READWRITE, &ad);
} }
......
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