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: Jeremy Fitzhardinge <jeremy@goop.org> Reported-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
44b8288308
commit
3610cda53f
|
@ -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 {
|
||||||
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
|
||||||
|
|
||||||
#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);
|
||||||
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 @@ restart:
|
||||||
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,
|
||||||
}
|
}
|
||||||
|
|
||||||
#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);
|
||||||
|
|
||||||
#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)
|
||||||
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,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue