Commit 9490f886 authored by Hannes Frederic Sowa's avatar Hannes Frederic Sowa Committed by David S. Miller

af-unix: passcred support for sendpage

sendpage did not care about credentials at all. This could lead to
situations in which because of fd passing between processes we could
append data to skbs with different scm data. It is illegal to splice those
skbs together. Instead we have to allocate a new skb and if requested
fill out the scm details.

Fixes: 869e7c62 ("net: af_unix: implement stream sendpage support")
Reported-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Eric Dumazet <edumazet@google.com>
Signed-off-by: default avatarHannes Frederic Sowa <hannes@stressinduktion.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 6e0f0331
...@@ -1551,6 +1551,14 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen ...@@ -1551,6 +1551,14 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen
return err; return err;
} }
static bool unix_passcred_enabled(const struct socket *sock,
const struct sock *other)
{
return test_bit(SOCK_PASSCRED, &sock->flags) ||
!other->sk_socket ||
test_bit(SOCK_PASSCRED, &other->sk_socket->flags);
}
/* /*
* Some apps rely on write() giving SCM_CREDENTIALS * Some apps rely on write() giving SCM_CREDENTIALS
* We include credentials if source or destination socket * We include credentials if source or destination socket
...@@ -1561,14 +1569,41 @@ static void maybe_add_creds(struct sk_buff *skb, const struct socket *sock, ...@@ -1561,14 +1569,41 @@ static void maybe_add_creds(struct sk_buff *skb, const struct socket *sock,
{ {
if (UNIXCB(skb).pid) if (UNIXCB(skb).pid)
return; return;
if (test_bit(SOCK_PASSCRED, &sock->flags) || if (unix_passcred_enabled(sock, other)) {
!other->sk_socket ||
test_bit(SOCK_PASSCRED, &other->sk_socket->flags)) {
UNIXCB(skb).pid = get_pid(task_tgid(current)); UNIXCB(skb).pid = get_pid(task_tgid(current));
current_uid_gid(&UNIXCB(skb).uid, &UNIXCB(skb).gid); current_uid_gid(&UNIXCB(skb).uid, &UNIXCB(skb).gid);
} }
} }
static int maybe_init_creds(struct scm_cookie *scm,
struct socket *socket,
const struct sock *other)
{
int err;
struct msghdr msg = { .msg_controllen = 0 };
err = scm_send(socket, &msg, scm, false);
if (err)
return err;
if (unix_passcred_enabled(socket, other)) {
scm->pid = get_pid(task_tgid(current));
current_uid_gid(&scm->creds.uid, &scm->creds.gid);
}
return err;
}
static bool unix_skb_scm_eq(struct sk_buff *skb,
struct scm_cookie *scm)
{
const struct unix_skb_parms *u = &UNIXCB(skb);
return u->pid == scm->pid &&
uid_eq(u->uid, scm->creds.uid) &&
gid_eq(u->gid, scm->creds.gid) &&
unix_secdata_eq(scm, skb);
}
/* /*
* Send AF_UNIX data. * Send AF_UNIX data.
*/ */
...@@ -1884,8 +1919,10 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, ...@@ -1884,8 +1919,10 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page, static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page,
int offset, size_t size, int flags) int offset, size_t size, int flags)
{ {
int err = 0; int err;
bool send_sigpipe = true; bool send_sigpipe = false;
bool init_scm = true;
struct scm_cookie scm;
struct sock *other, *sk = socket->sk; struct sock *other, *sk = socket->sk;
struct sk_buff *skb, *newskb = NULL, *tail = NULL; struct sk_buff *skb, *newskb = NULL, *tail = NULL;
...@@ -1903,7 +1940,7 @@ static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page, ...@@ -1903,7 +1940,7 @@ static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page,
newskb = sock_alloc_send_pskb(sk, 0, 0, flags & MSG_DONTWAIT, newskb = sock_alloc_send_pskb(sk, 0, 0, flags & MSG_DONTWAIT,
&err, 0); &err, 0);
if (!newskb) if (!newskb)
return err; goto err;
} }
/* we must acquire readlock as we modify already present /* we must acquire readlock as we modify already present
...@@ -1912,12 +1949,12 @@ static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page, ...@@ -1912,12 +1949,12 @@ static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page,
err = mutex_lock_interruptible(&unix_sk(other)->readlock); err = mutex_lock_interruptible(&unix_sk(other)->readlock);
if (err) { if (err) {
err = flags & MSG_DONTWAIT ? -EAGAIN : -ERESTARTSYS; err = flags & MSG_DONTWAIT ? -EAGAIN : -ERESTARTSYS;
send_sigpipe = false;
goto err; goto err;
} }
if (sk->sk_shutdown & SEND_SHUTDOWN) { if (sk->sk_shutdown & SEND_SHUTDOWN) {
err = -EPIPE; err = -EPIPE;
send_sigpipe = true;
goto err_unlock; goto err_unlock;
} }
...@@ -1926,17 +1963,27 @@ static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page, ...@@ -1926,17 +1963,27 @@ static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page,
if (sock_flag(other, SOCK_DEAD) || if (sock_flag(other, SOCK_DEAD) ||
other->sk_shutdown & RCV_SHUTDOWN) { other->sk_shutdown & RCV_SHUTDOWN) {
err = -EPIPE; err = -EPIPE;
send_sigpipe = true;
goto err_state_unlock;
}
if (init_scm) {
err = maybe_init_creds(&scm, socket, other);
if (err)
goto err_state_unlock; goto err_state_unlock;
init_scm = false;
} }
skb = skb_peek_tail(&other->sk_receive_queue); skb = skb_peek_tail(&other->sk_receive_queue);
if (tail && tail == skb) { if (tail && tail == skb) {
skb = newskb; skb = newskb;
} else if (!skb) { } else if (!skb || !unix_skb_scm_eq(skb, &scm)) {
if (newskb) if (newskb) {
skb = newskb; skb = newskb;
else } else {
tail = skb;
goto alloc_skb; goto alloc_skb;
}
} else if (newskb) { } else if (newskb) {
/* this is fast path, we don't necessarily need to /* this is fast path, we don't necessarily need to
* call to kfree_skb even though with newskb == NULL * call to kfree_skb even though with newskb == NULL
...@@ -1957,6 +2004,9 @@ static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page, ...@@ -1957,6 +2004,9 @@ static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page,
atomic_add(size, &sk->sk_wmem_alloc); atomic_add(size, &sk->sk_wmem_alloc);
if (newskb) { if (newskb) {
err = unix_scm_to_skb(&scm, skb, false);
if (err)
goto err_state_unlock;
spin_lock(&other->sk_receive_queue.lock); spin_lock(&other->sk_receive_queue.lock);
__skb_queue_tail(&other->sk_receive_queue, newskb); __skb_queue_tail(&other->sk_receive_queue, newskb);
spin_unlock(&other->sk_receive_queue.lock); spin_unlock(&other->sk_receive_queue.lock);
...@@ -1966,7 +2016,7 @@ static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page, ...@@ -1966,7 +2016,7 @@ static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page,
mutex_unlock(&unix_sk(other)->readlock); mutex_unlock(&unix_sk(other)->readlock);
other->sk_data_ready(other); other->sk_data_ready(other);
scm_destroy(&scm);
return size; return size;
err_state_unlock: err_state_unlock:
...@@ -1977,6 +2027,8 @@ static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page, ...@@ -1977,6 +2027,8 @@ static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page,
kfree_skb(newskb); kfree_skb(newskb);
if (send_sigpipe && !(flags & MSG_NOSIGNAL)) if (send_sigpipe && !(flags & MSG_NOSIGNAL))
send_sig(SIGPIPE, current, 0); send_sig(SIGPIPE, current, 0);
if (!init_scm)
scm_destroy(&scm);
return err; return err;
} }
...@@ -2280,10 +2332,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state) ...@@ -2280,10 +2332,7 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state)
if (check_creds) { if (check_creds) {
/* Never glue messages from different writers */ /* Never glue messages from different writers */
if ((UNIXCB(skb).pid != scm.pid) || if (!unix_skb_scm_eq(skb, &scm))
!uid_eq(UNIXCB(skb).uid, scm.creds.uid) ||
!gid_eq(UNIXCB(skb).gid, scm.creds.gid) ||
!unix_secdata_eq(&scm, skb))
break; break;
} else if (test_bit(SOCK_PASSCRED, &sock->flags)) { } else if (test_bit(SOCK_PASSCRED, &sock->flags)) {
/* Copy credentials */ /* Copy credentials */
......
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