]> Pileus Git - ~andy/linux/blobdiff - net/unix/af_unix.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc
[~andy/linux] / net / unix / af_unix.c
index e6d9d1014ed25309ffbe85a18c7ab0b8ffc34ab2..466fbcc5cf77a92ef491be50eb836f652165da16 100644 (file)
@@ -1378,17 +1378,13 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
        return max_level;
 }
 
-static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb,
-                          bool send_fds, bool ref)
+static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool send_fds)
 {
        int err = 0;
-       if (ref) {
-               UNIXCB(skb).pid  = get_pid(scm->pid);
+
+       UNIXCB(skb).pid  = get_pid(scm->pid);
+       if (scm->cred)
                UNIXCB(skb).cred = get_cred(scm->cred);
-       } else {
-               UNIXCB(skb).pid  = scm->pid;
-               UNIXCB(skb).cred = scm->cred;
-       }
        UNIXCB(skb).fp = NULL;
        if (scm->fp && send_fds)
                err = unix_attach_fds(scm, skb);
@@ -1397,6 +1393,24 @@ static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb,
        return err;
 }
 
+/*
+ * Some apps rely on write() giving SCM_CREDENTIALS
+ * We include credentials if source or destination socket
+ * asserted SOCK_PASSCRED.
+ */
+static void maybe_add_creds(struct sk_buff *skb, const struct socket *sock,
+                           const struct sock *other)
+{
+       if (UNIXCB(skb).cred)
+               return;
+       if (test_bit(SOCK_PASSCRED, &sock->flags) ||
+           !other->sk_socket ||
+           test_bit(SOCK_PASSCRED, &other->sk_socket->flags)) {
+               UNIXCB(skb).pid  = get_pid(task_tgid(current));
+               UNIXCB(skb).cred = get_current_cred();
+       }
+}
+
 /*
  *     Send AF_UNIX data.
  */
@@ -1413,7 +1427,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
        int namelen = 0; /* fake GCC */
        int err;
        unsigned hash;
-       struct sk_buff *skb = NULL;
+       struct sk_buff *skb;
        long timeo;
        struct scm_cookie tmp_scm;
        int max_level;
@@ -1454,7 +1468,7 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock,
        if (skb == NULL)
                goto out;
 
-       err = unix_scm_to_skb(siocb->scm, skb, true, false);
+       err = unix_scm_to_skb(siocb->scm, skb, true);
        if (err < 0)
                goto out_free;
        max_level = err + 1;
@@ -1544,13 +1558,14 @@ restart:
 
        if (sock_flag(other, SOCK_RCVTSTAMP))
                __net_timestamp(skb);
+       maybe_add_creds(skb, sock, other);
        skb_queue_tail(&other->sk_receive_queue, skb);
        if (max_level > unix_sk(other)->recursion_level)
                unix_sk(other)->recursion_level = max_level;
        unix_state_unlock(other);
        other->sk_data_ready(other, len);
        sock_put(other);
-       scm_release(siocb->scm);
+       scm_destroy(siocb->scm);
        return len;
 
 out_unlock:
@@ -1560,8 +1575,7 @@ out_free:
 out:
        if (other)
                sock_put(other);
-       if (skb == NULL)
-               scm_destroy(siocb->scm);
+       scm_destroy(siocb->scm);
        return err;
 }
 
@@ -1573,7 +1587,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
        struct sock *sk = sock->sk;
        struct sock *other = NULL;
        int err, size;
-       struct sk_buff *skb = NULL;
+       struct sk_buff *skb;
        int sent = 0;
        struct scm_cookie tmp_scm;
        bool fds_sent = false;
@@ -1638,11 +1652,11 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
                size = min_t(int, size, skb_tailroom(skb));
 
 
-               /* Only send the fds and no ref to pid in the first buffer */
-               err = unix_scm_to_skb(siocb->scm, skb, !fds_sent, fds_sent);
+               /* Only send the fds in the first buffer */
+               err = unix_scm_to_skb(siocb->scm, skb, !fds_sent);
                if (err < 0) {
                        kfree_skb(skb);
-                       goto out;
+                       goto out_err;
                }
                max_level = err + 1;
                fds_sent = true;
@@ -1650,7 +1664,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
                err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
                if (err) {
                        kfree_skb(skb);
-                       goto out;
+                       goto out_err;
                }
 
                unix_state_lock(other);
@@ -1659,6 +1673,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
                    (other->sk_shutdown & RCV_SHUTDOWN))
                        goto pipe_err_free;
 
+               maybe_add_creds(skb, sock, other);
                skb_queue_tail(&other->sk_receive_queue, skb);
                if (max_level > unix_sk(other)->recursion_level)
                        unix_sk(other)->recursion_level = max_level;
@@ -1667,10 +1682,7 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
                sent += size;
        }
 
-       if (skb)
-               scm_release(siocb->scm);
-       else
-               scm_destroy(siocb->scm);
+       scm_destroy(siocb->scm);
        siocb->scm = NULL;
 
        return sent;
@@ -1683,9 +1695,7 @@ pipe_err:
                send_sig(SIGPIPE, current, 0);
        err = -EPIPE;
 out_err:
-       if (skb == NULL)
-               scm_destroy(siocb->scm);
-out:
+       scm_destroy(siocb->scm);
        siocb->scm = NULL;
        return sent ? : err;
 }
@@ -1789,7 +1799,7 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock,
                siocb->scm = &tmp_scm;
                memset(&tmp_scm, 0, sizeof(tmp_scm));
        }
-       scm_set_cred_noref(siocb->scm, UNIXCB(skb).pid, UNIXCB(skb).cred);
+       scm_set_cred(siocb->scm, UNIXCB(skb).pid, UNIXCB(skb).cred);
        unix_set_secdata(siocb->scm, skb);
 
        if (!(flags & MSG_PEEK)) {
@@ -1951,8 +1961,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock,
                        }
                } else {
                        /* Copy credentials */
-                       scm_set_cred_noref(siocb->scm, UNIXCB(skb).pid,
-                                          UNIXCB(skb).cred);
+                       scm_set_cred(siocb->scm, UNIXCB(skb).pid, UNIXCB(skb).cred);
                        check_creds = 1;
                }