Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dbe9a417 authored by Eric W. Biederman's avatar Eric W. Biederman Committed by David S. Miller
Browse files

scm: Don't use struct ucred in NETLINK_CB and struct scm_cookie.



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: default avatar"Eric W. Biederman" <ebiederm@xmission.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent d679c532
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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;
+19 −4
Original line number Diff line number Diff line
@@ -12,6 +12,12 @@
 */
#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);

+11 −6
Original line number Diff line number Diff line
@@ -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)) {
+1 −1
Original line number Diff line number Diff line
@@ -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)) {