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

Commit d178bc3a authored by Serge Hallyn's avatar Serge Hallyn Committed by Greg Kroah-Hartman
Browse files

user namespace: usb: make usb urbs user namespace aware (v2)



Add to the dev_state and alloc_async structures the user namespace
corresponding to the uid and euid.  Pass these to kill_pid_info_as_uid(),
which can then implement a proper, user-namespace-aware uid check.

Changelog:
Sep 20: Per Oleg's suggestion: Instead of caching and passing user namespace,
	uid, and euid each separately, pass a struct cred.
Sep 26: Address Alan Stern's comments: don't define a struct cred at
	usbdev_open(), and take and put a cred at async_completed() to
	ensure it lasts for the duration of kill_pid_info_as_cred().

Signed-off-by: default avatarSerge Hallyn <serge.hallyn@canonical.com>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent edb2b255
Loading
Loading
Loading
Loading
+13 −17
Original line number Original line Diff line number Diff line
@@ -46,6 +46,7 @@
#include <linux/cdev.h>
#include <linux/cdev.h>
#include <linux/notifier.h>
#include <linux/notifier.h>
#include <linux/security.h>
#include <linux/security.h>
#include <linux/user_namespace.h>
#include <asm/uaccess.h>
#include <asm/uaccess.h>
#include <asm/byteorder.h>
#include <asm/byteorder.h>
#include <linux/moduleparam.h>
#include <linux/moduleparam.h>
@@ -68,7 +69,7 @@ struct dev_state {
	wait_queue_head_t wait;     /* wake up if a request completed */
	wait_queue_head_t wait;     /* wake up if a request completed */
	unsigned int discsignr;
	unsigned int discsignr;
	struct pid *disc_pid;
	struct pid *disc_pid;
	uid_t disc_uid, disc_euid;
	const struct cred *cred;
	void __user *disccontext;
	void __user *disccontext;
	unsigned long ifclaimed;
	unsigned long ifclaimed;
	u32 secid;
	u32 secid;
@@ -79,7 +80,7 @@ struct async {
	struct list_head asynclist;
	struct list_head asynclist;
	struct dev_state *ps;
	struct dev_state *ps;
	struct pid *pid;
	struct pid *pid;
	uid_t uid, euid;
	const struct cred *cred;
	unsigned int signr;
	unsigned int signr;
	unsigned int ifnum;
	unsigned int ifnum;
	void __user *userbuffer;
	void __user *userbuffer;
@@ -248,6 +249,7 @@ static struct async *alloc_async(unsigned int numisoframes)
static void free_async(struct async *as)
static void free_async(struct async *as)
{
{
	put_pid(as->pid);
	put_pid(as->pid);
	put_cred(as->cred);
	kfree(as->urb->transfer_buffer);
	kfree(as->urb->transfer_buffer);
	kfree(as->urb->setup_packet);
	kfree(as->urb->setup_packet);
	usb_free_urb(as->urb);
	usb_free_urb(as->urb);
@@ -393,9 +395,8 @@ static void async_completed(struct urb *urb)
	struct dev_state *ps = as->ps;
	struct dev_state *ps = as->ps;
	struct siginfo sinfo;
	struct siginfo sinfo;
	struct pid *pid = NULL;
	struct pid *pid = NULL;
	uid_t uid = 0;
	uid_t euid = 0;
	u32 secid = 0;
	u32 secid = 0;
	const struct cred *cred = NULL;
	int signr;
	int signr;


	spin_lock(&ps->lock);
	spin_lock(&ps->lock);
@@ -408,8 +409,7 @@ static void async_completed(struct urb *urb)
		sinfo.si_code = SI_ASYNCIO;
		sinfo.si_code = SI_ASYNCIO;
		sinfo.si_addr = as->userurb;
		sinfo.si_addr = as->userurb;
		pid = get_pid(as->pid);
		pid = get_pid(as->pid);
		uid = as->uid;
		cred = get_cred(as->cred);
		euid = as->euid;
		secid = as->secid;
		secid = as->secid;
	}
	}
	snoop(&urb->dev->dev, "urb complete\n");
	snoop(&urb->dev->dev, "urb complete\n");
@@ -423,9 +423,9 @@ static void async_completed(struct urb *urb)
	spin_unlock(&ps->lock);
	spin_unlock(&ps->lock);


	if (signr) {
	if (signr) {
		kill_pid_info_as_uid(sinfo.si_signo, &sinfo, pid, uid,
		kill_pid_info_as_cred(sinfo.si_signo, &sinfo, pid, cred, secid);
				      euid, secid);
		put_pid(pid);
		put_pid(pid);
		put_cred(cred);
	}
	}


	wake_up(&ps->wait);
	wake_up(&ps->wait);
@@ -672,7 +672,6 @@ static int usbdev_open(struct inode *inode, struct file *file)
{
{
	struct usb_device *dev = NULL;
	struct usb_device *dev = NULL;
	struct dev_state *ps;
	struct dev_state *ps;
	const struct cred *cred = current_cred();
	int ret;
	int ret;


	ret = -ENOMEM;
	ret = -ENOMEM;
@@ -722,8 +721,7 @@ static int usbdev_open(struct inode *inode, struct file *file)
	init_waitqueue_head(&ps->wait);
	init_waitqueue_head(&ps->wait);
	ps->discsignr = 0;
	ps->discsignr = 0;
	ps->disc_pid = get_pid(task_pid(current));
	ps->disc_pid = get_pid(task_pid(current));
	ps->disc_uid = cred->uid;
	ps->cred = get_current_cred();
	ps->disc_euid = cred->euid;
	ps->disccontext = NULL;
	ps->disccontext = NULL;
	ps->ifclaimed = 0;
	ps->ifclaimed = 0;
	security_task_getsecid(current, &ps->secid);
	security_task_getsecid(current, &ps->secid);
@@ -765,6 +763,7 @@ static int usbdev_release(struct inode *inode, struct file *file)
	usb_unlock_device(dev);
	usb_unlock_device(dev);
	usb_put_dev(dev);
	usb_put_dev(dev);
	put_pid(ps->disc_pid);
	put_pid(ps->disc_pid);
	put_cred(ps->cred);


	as = async_getcompleted(ps);
	as = async_getcompleted(ps);
	while (as) {
	while (as) {
@@ -1065,7 +1064,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
	struct usb_host_endpoint *ep;
	struct usb_host_endpoint *ep;
	struct async *as;
	struct async *as;
	struct usb_ctrlrequest *dr = NULL;
	struct usb_ctrlrequest *dr = NULL;
	const struct cred *cred = current_cred();
	unsigned int u, totlen, isofrmlen;
	unsigned int u, totlen, isofrmlen;
	int ret, ifnum = -1;
	int ret, ifnum = -1;
	int is_in;
	int is_in;
@@ -1279,8 +1277,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
	as->signr = uurb->signr;
	as->signr = uurb->signr;
	as->ifnum = ifnum;
	as->ifnum = ifnum;
	as->pid = get_pid(task_pid(current));
	as->pid = get_pid(task_pid(current));
	as->uid = cred->uid;
	as->cred = get_current_cred();
	as->euid = cred->euid;
	security_task_getsecid(current, &as->secid);
	security_task_getsecid(current, &as->secid);
	if (!is_in && uurb->buffer_length > 0) {
	if (!is_in && uurb->buffer_length > 0) {
		if (copy_from_user(as->urb->transfer_buffer, uurb->buffer,
		if (copy_from_user(as->urb->transfer_buffer, uurb->buffer,
@@ -1998,9 +1995,8 @@ static void usbdev_remove(struct usb_device *udev)
			sinfo.si_errno = EPIPE;
			sinfo.si_errno = EPIPE;
			sinfo.si_code = SI_ASYNCIO;
			sinfo.si_code = SI_ASYNCIO;
			sinfo.si_addr = ps->disccontext;
			sinfo.si_addr = ps->disccontext;
			kill_pid_info_as_uid(ps->discsignr, &sinfo,
			kill_pid_info_as_cred(ps->discsignr, &sinfo,
					ps->disc_pid, ps->disc_uid,
					ps->disc_pid, ps->cred, ps->secid);
					ps->disc_euid, ps->secid);
		}
		}
	}
	}
}
}
+2 −1
Original line number Original line Diff line number Diff line
@@ -2166,7 +2166,8 @@ extern int force_sigsegv(int, struct task_struct *);
extern int force_sig_info(int, struct siginfo *, struct task_struct *);
extern int force_sig_info(int, struct siginfo *, struct task_struct *);
extern int __kill_pgrp_info(int sig, struct siginfo *info, struct pid *pgrp);
extern int __kill_pgrp_info(int sig, struct siginfo *info, struct pid *pgrp);
extern int kill_pid_info(int sig, struct siginfo *info, struct pid *pid);
extern int kill_pid_info(int sig, struct siginfo *info, struct pid *pid);
extern int kill_pid_info_as_uid(int, struct siginfo *, struct pid *, uid_t, uid_t, u32);
extern int kill_pid_info_as_cred(int, struct siginfo *, struct pid *,
				const struct cred *, u32);
extern int kill_pgrp(struct pid *pid, int sig, int priv);
extern int kill_pgrp(struct pid *pid, int sig, int priv);
extern int kill_pid(struct pid *pid, int sig, int priv);
extern int kill_pid(struct pid *pid, int sig, int priv);
extern int kill_proc_info(int, struct siginfo *, pid_t);
extern int kill_proc_info(int, struct siginfo *, pid_t);
+16 −8
Original line number Original line Diff line number Diff line
@@ -1344,13 +1344,24 @@ int kill_proc_info(int sig, struct siginfo *info, pid_t pid)
	return error;
	return error;
}
}


static int kill_as_cred_perm(const struct cred *cred,
			     struct task_struct *target)
{
	const struct cred *pcred = __task_cred(target);
	if (cred->user_ns != pcred->user_ns)
		return 0;
	if (cred->euid != pcred->suid && cred->euid != pcred->uid &&
	    cred->uid  != pcred->suid && cred->uid  != pcred->uid)
		return 0;
	return 1;
}

/* like kill_pid_info(), but doesn't use uid/euid of "current" */
/* like kill_pid_info(), but doesn't use uid/euid of "current" */
int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid,
int kill_pid_info_as_cred(int sig, struct siginfo *info, struct pid *pid,
		      uid_t uid, uid_t euid, u32 secid)
			 const struct cred *cred, u32 secid)
{
{
	int ret = -EINVAL;
	int ret = -EINVAL;
	struct task_struct *p;
	struct task_struct *p;
	const struct cred *pcred;
	unsigned long flags;
	unsigned long flags;


	if (!valid_signal(sig))
	if (!valid_signal(sig))
@@ -1362,10 +1373,7 @@ int kill_pid_info_as_uid(int sig, struct siginfo *info, struct pid *pid,
		ret = -ESRCH;
		ret = -ESRCH;
		goto out_unlock;
		goto out_unlock;
	}
	}
	pcred = __task_cred(p);
	if (si_fromuser(info) && !kill_as_cred_perm(cred, p)) {
	if (si_fromuser(info) &&
	    euid != pcred->suid && euid != pcred->uid &&
	    uid  != pcred->suid && uid  != pcred->uid) {
		ret = -EPERM;
		ret = -EPERM;
		goto out_unlock;
		goto out_unlock;
	}
	}
@@ -1384,7 +1392,7 @@ out_unlock:
	rcu_read_unlock();
	rcu_read_unlock();
	return ret;
	return ret;
}
}
EXPORT_SYMBOL_GPL(kill_pid_info_as_uid);
EXPORT_SYMBOL_GPL(kill_pid_info_as_cred);


/*
/*
 * kill_something_info() interprets pid in interesting ways just like kill(2).
 * kill_something_info() interprets pid in interesting ways just like kill(2).