|
|
@@ -1551,6 +1551,14 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool sen
|
|
|
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
|
|
|
* 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,
|
|
|
{
|
|
|
if (UNIXCB(skb).pid)
|
|
|
return;
|
|
|
- if (test_bit(SOCK_PASSCRED, &sock->flags) ||
|
|
|
- !other->sk_socket ||
|
|
|
- test_bit(SOCK_PASSCRED, &other->sk_socket->flags)) {
|
|
|
+ if (unix_passcred_enabled(sock, other)) {
|
|
|
UNIXCB(skb).pid = get_pid(task_tgid(current));
|
|
|
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.
|
|
|
*/
|
|
|
@@ -1884,8 +1919,10 @@ out_err:
|
|
|
static ssize_t unix_stream_sendpage(struct socket *socket, struct page *page,
|
|
|
int offset, size_t size, int flags)
|
|
|
{
|
|
|
- int err = 0;
|
|
|
- bool send_sigpipe = true;
|
|
|
+ int err;
|
|
|
+ bool send_sigpipe = false;
|
|
|
+ bool init_scm = true;
|
|
|
+ struct scm_cookie scm;
|
|
|
struct sock *other, *sk = socket->sk;
|
|
|
struct sk_buff *skb, *newskb = NULL, *tail = NULL;
|
|
|
|
|
|
@@ -1903,7 +1940,7 @@ alloc_skb:
|
|
|
newskb = sock_alloc_send_pskb(sk, 0, 0, flags & MSG_DONTWAIT,
|
|
|
&err, 0);
|
|
|
if (!newskb)
|
|
|
- return err;
|
|
|
+ goto err;
|
|
|
}
|
|
|
|
|
|
/* we must acquire readlock as we modify already present
|
|
|
@@ -1912,12 +1949,12 @@ alloc_skb:
|
|
|
err = mutex_lock_interruptible(&unix_sk(other)->readlock);
|
|
|
if (err) {
|
|
|
err = flags & MSG_DONTWAIT ? -EAGAIN : -ERESTARTSYS;
|
|
|
- send_sigpipe = false;
|
|
|
goto err;
|
|
|
}
|
|
|
|
|
|
if (sk->sk_shutdown & SEND_SHUTDOWN) {
|
|
|
err = -EPIPE;
|
|
|
+ send_sigpipe = true;
|
|
|
goto err_unlock;
|
|
|
}
|
|
|
|
|
|
@@ -1926,17 +1963,27 @@ alloc_skb:
|
|
|
if (sock_flag(other, SOCK_DEAD) ||
|
|
|
other->sk_shutdown & RCV_SHUTDOWN) {
|
|
|
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;
|
|
|
+ init_scm = false;
|
|
|
+ }
|
|
|
+
|
|
|
skb = skb_peek_tail(&other->sk_receive_queue);
|
|
|
if (tail && tail == skb) {
|
|
|
skb = newskb;
|
|
|
- } else if (!skb) {
|
|
|
- if (newskb)
|
|
|
+ } else if (!skb || !unix_skb_scm_eq(skb, &scm)) {
|
|
|
+ if (newskb) {
|
|
|
skb = newskb;
|
|
|
- else
|
|
|
+ } else {
|
|
|
+ tail = skb;
|
|
|
goto alloc_skb;
|
|
|
+ }
|
|
|
} else if (newskb) {
|
|
|
/* this is fast path, we don't necessarily need to
|
|
|
* call to kfree_skb even though with newskb == NULL
|
|
|
@@ -1957,6 +2004,9 @@ alloc_skb:
|
|
|
atomic_add(size, &sk->sk_wmem_alloc);
|
|
|
|
|
|
if (newskb) {
|
|
|
+ err = unix_scm_to_skb(&scm, skb, false);
|
|
|
+ if (err)
|
|
|
+ goto err_state_unlock;
|
|
|
spin_lock(&other->sk_receive_queue.lock);
|
|
|
__skb_queue_tail(&other->sk_receive_queue, newskb);
|
|
|
spin_unlock(&other->sk_receive_queue.lock);
|
|
|
@@ -1966,7 +2016,7 @@ alloc_skb:
|
|
|
mutex_unlock(&unix_sk(other)->readlock);
|
|
|
|
|
|
other->sk_data_ready(other);
|
|
|
-
|
|
|
+ scm_destroy(&scm);
|
|
|
return size;
|
|
|
|
|
|
err_state_unlock:
|
|
|
@@ -1977,6 +2027,8 @@ err:
|
|
|
kfree_skb(newskb);
|
|
|
if (send_sigpipe && !(flags & MSG_NOSIGNAL))
|
|
|
send_sig(SIGPIPE, current, 0);
|
|
|
+ if (!init_scm)
|
|
|
+ scm_destroy(&scm);
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
@@ -2280,10 +2332,7 @@ unlock:
|
|
|
|
|
|
if (check_creds) {
|
|
|
/* Never glue messages from different writers */
|
|
|
- if ((UNIXCB(skb).pid != scm.pid) ||
|
|
|
- !uid_eq(UNIXCB(skb).uid, scm.creds.uid) ||
|
|
|
- !gid_eq(UNIXCB(skb).gid, scm.creds.gid) ||
|
|
|
- !unix_secdata_eq(&scm, skb))
|
|
|
+ if (!unix_skb_scm_eq(skb, &scm))
|
|
|
break;
|
|
|
} else if (test_bit(SOCK_PASSCRED, &sock->flags)) {
|
|
|
/* Copy credentials */
|