scm: Don't use struct ucred in NETLINK_CB and struct scm_cookie.
authorEric W. Biederman <ebiederm@xmission.com>
Thu, 6 Sep 2012 18:20:01 +0000 (18:20 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 7 Sep 2012 18:42:05 +0000 (14:42 -0400)
Passing uids and gids on NETLINK_CB from a process in one user
namespace to a process in another user namespace can result in the
wrong uid or gid being presented to userspace.  Avoid that problem by
passing kuids and kgids instead.

- define struct scm_creds for use in scm_cookie and netlink_skb_parms
  that holds uid and gid information in kuid_t and kgid_t.

- Modify scm_set_cred to fill out scm_creds by heand instead of using
  cred_to_ucred to fill out struct ucred.  This conversion ensures
  userspace does not get incorrect uid or gid values to look at.

- Modify scm_recv to convert from struct scm_creds to struct ucred
  before copying credential values to userspace.

- Modify __scm_send to populate struct scm_creds on in the scm_cookie,
  instead of just copying struct ucred from userspace.

- Modify netlink_sendmsg to copy scm_creds instead of struct ucred
  into the NETLINK_CB.

Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/linux/netlink.h
include/net/scm.h
net/core/scm.c
net/netlink/af_netlink.c

index c9fdde2..df73cf4 100644 (file)
@@ -153,6 +153,7 @@ struct nlattr {
 
 #include <linux/capability.h>
 #include <linux/skbuff.h>
+#include <net/scm.h>
 
 struct net;
 
@@ -162,7 +163,7 @@ static inline struct nlmsghdr *nlmsg_hdr(const struct sk_buff *skb)
 }
 
 struct netlink_skb_parms {
-       struct ucred            creds;          /* Skb credentials      */
+       struct scm_creds        creds;          /* Skb credentials      */
        __u32                   pid;
        __u32                   dst_group;
        struct sock             *ssk;
index 7dc0854..456695f 100644 (file)
  */
 #define SCM_MAX_FD     253
 
+struct scm_creds {
+       u32     pid;
+       kuid_t  uid;
+       kgid_t  gid;
+};
+
 struct scm_fp_list {
        short                   count;
        short                   max;
@@ -22,7 +28,7 @@ struct scm_cookie {
        struct pid              *pid;           /* Skb credentials */
        const struct cred       *cred;
        struct scm_fp_list      *fp;            /* Passed files         */
-       struct ucred            creds;          /* Skb credentials      */
+       struct scm_creds        creds;          /* Skb credentials      */
 #ifdef CONFIG_SECURITY_NETWORK
        u32                     secid;          /* Passed security ID   */
 #endif
@@ -49,7 +55,9 @@ static __inline__ void scm_set_cred(struct scm_cookie *scm,
 {
        scm->pid  = get_pid(pid);
        scm->cred = cred ? get_cred(cred) : NULL;
-       cred_to_ucred(pid, cred, &scm->creds);
+       scm->creds.pid = pid_vnr(pid);
+       scm->creds.uid = cred ? cred->euid : INVALID_UID;
+       scm->creds.gid = cred ? cred->egid : INVALID_GID;
 }
 
 static __inline__ void scm_destroy_cred(struct scm_cookie *scm)
@@ -112,8 +120,15 @@ static __inline__ void scm_recv(struct socket *sock, struct msghdr *msg,
                return;
        }
 
-       if (test_bit(SOCK_PASSCRED, &sock->flags))
-               put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(scm->creds), &scm->creds);
+       if (test_bit(SOCK_PASSCRED, &sock->flags)) {
+               struct user_namespace *current_ns = current_user_ns();
+               struct ucred ucreds = {
+                       .pid = scm->creds.pid,
+                       .uid = from_kuid_munged(current_ns, scm->creds.uid),
+                       .gid = from_kgid_munged(current_ns, scm->creds.gid),
+               };
+               put_cmsg(msg, SOL_SOCKET, SCM_CREDENTIALS, sizeof(ucreds), &ucreds);
+       }
 
        scm_destroy_cred(scm);
 
index 6ab491d..9c1c63d 100644 (file)
@@ -155,19 +155,21 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
                        break;
                case SCM_CREDENTIALS:
                {
+                       struct ucred creds;
                        kuid_t uid;
                        kgid_t gid;
                        if (cmsg->cmsg_len != CMSG_LEN(sizeof(struct ucred)))
                                goto error;
-                       memcpy(&p->creds, CMSG_DATA(cmsg), sizeof(struct ucred));
-                       err = scm_check_creds(&p->creds);
+                       memcpy(&creds, CMSG_DATA(cmsg), sizeof(struct ucred));
+                       err = scm_check_creds(&creds);
                        if (err)
                                goto error;
 
-                       if (!p->pid || pid_vnr(p->pid) != p->creds.pid) {
+                       p->creds.pid = creds.pid;
+                       if (!p->pid || pid_vnr(p->pid) != creds.pid) {
                                struct pid *pid;
                                err = -ESRCH;
-                               pid = find_get_pid(p->creds.pid);
+                               pid = find_get_pid(creds.pid);
                                if (!pid)
                                        goto error;
                                put_pid(p->pid);
@@ -175,11 +177,14 @@ int __scm_send(struct socket *sock, struct msghdr *msg, struct scm_cookie *p)
                        }
 
                        err = -EINVAL;
-                       uid = make_kuid(current_user_ns(), p->creds.uid);
-                       gid = make_kgid(current_user_ns(), p->creds.gid);
+                       uid = make_kuid(current_user_ns(), creds.uid);
+                       gid = make_kgid(current_user_ns(), creds.gid);
                        if (!uid_valid(uid) || !gid_valid(gid))
                                goto error;
 
+                       p->creds.uid = uid;
+                       p->creds.gid = gid;
+
                        if (!p->cred ||
                            !uid_eq(p->cred->euid, uid) ||
                            !gid_eq(p->cred->egid, gid)) {
index 3821199..f530b1c 100644 (file)
@@ -1399,7 +1399,7 @@ static int netlink_sendmsg(struct kiocb *kiocb, struct socket *sock,
 
        NETLINK_CB(skb).pid     = nlk->pid;
        NETLINK_CB(skb).dst_group = dst_group;
-       memcpy(NETLINK_CREDS(skb), &siocb->scm->creds, sizeof(struct ucred));
+       NETLINK_CB(skb).creds   = siocb->scm->creds;
 
        err = -EFAULT;
        if (memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len)) {