diff options
Diffstat (limited to 'drivers/usb/core/devio.c')
| -rw-r--r-- | drivers/usb/core/devio.c | 57 | 
1 files changed, 35 insertions, 22 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 37518dfdeb9..e3beaf229ee 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -46,6 +46,7 @@  #include <linux/cdev.h>  #include <linux/notifier.h>  #include <linux/security.h> +#include <linux/user_namespace.h>  #include <asm/uaccess.h>  #include <asm/byteorder.h>  #include <linux/moduleparam.h> @@ -68,7 +69,7 @@ struct dev_state {  	wait_queue_head_t wait;     /* wake up if a request completed */  	unsigned int discsignr;  	struct pid *disc_pid; -	uid_t disc_uid, disc_euid; +	const struct cred *cred;  	void __user *disccontext;  	unsigned long ifclaimed;  	u32 secid; @@ -79,7 +80,7 @@ struct async {  	struct list_head asynclist;  	struct dev_state *ps;  	struct pid *pid; -	uid_t uid, euid; +	const struct cred *cred;  	unsigned int signr;  	unsigned int ifnum;  	void __user *userbuffer; @@ -248,6 +249,7 @@ static struct async *alloc_async(unsigned int numisoframes)  static void free_async(struct async *as)  {  	put_pid(as->pid); +	put_cred(as->cred);  	kfree(as->urb->transfer_buffer);  	kfree(as->urb->setup_packet);  	usb_free_urb(as->urb); @@ -393,9 +395,8 @@ static void async_completed(struct urb *urb)  	struct dev_state *ps = as->ps;  	struct siginfo sinfo;  	struct pid *pid = NULL; -	uid_t uid = 0; -	uid_t euid = 0;  	u32 secid = 0; +	const struct cred *cred = NULL;  	int signr;  	spin_lock(&ps->lock); @@ -407,9 +408,8 @@ static void async_completed(struct urb *urb)  		sinfo.si_errno = as->status;  		sinfo.si_code = SI_ASYNCIO;  		sinfo.si_addr = as->userurb; -		pid = as->pid; -		uid = as->uid; -		euid = as->euid; +		pid = get_pid(as->pid); +		cred = get_cred(as->cred);  		secid = as->secid;  	}  	snoop(&urb->dev->dev, "urb complete\n"); @@ -422,9 +422,11 @@ static void async_completed(struct urb *urb)  		cancel_bulk_urbs(ps, as->bulk_addr);  	spin_unlock(&ps->lock); -	if (signr) -		kill_pid_info_as_uid(sinfo.si_signo, &sinfo, pid, uid, -				      euid, secid); +	if (signr) { +		kill_pid_info_as_cred(sinfo.si_signo, &sinfo, pid, cred, secid); +		put_pid(pid); +		put_cred(cred); +	}  	wake_up(&ps->wait);  } @@ -607,9 +609,10 @@ static int findintfep(struct usb_device *dev, unsigned int ep)  }  static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, -			   unsigned int index) +			   unsigned int request, unsigned int index)  {  	int ret = 0; +	struct usb_host_interface *alt_setting;  	if (ps->dev->state != USB_STATE_UNAUTHENTICATED  	 && ps->dev->state != USB_STATE_ADDRESS @@ -618,6 +621,19 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype,  	if (USB_TYPE_VENDOR == (USB_TYPE_MASK & requesttype))  		return 0; +	/* +	 * check for the special corner case 'get_device_id' in the printer +	 * class specification, where wIndex is (interface << 8 | altsetting) +	 * instead of just interface +	 */ +	if (requesttype == 0xa1 && request == 0) { +		alt_setting = usb_find_alt_setting(ps->dev->actconfig, +						   index >> 8, index & 0xff); +		if (alt_setting +		 && alt_setting->desc.bInterfaceClass == USB_CLASS_PRINTER) +			index >>= 8; +	} +  	index &= 0xff;  	switch (requesttype & USB_RECIP_MASK) {  	case USB_RECIP_ENDPOINT: @@ -656,7 +672,6 @@ static int usbdev_open(struct inode *inode, struct file *file)  {  	struct usb_device *dev = NULL;  	struct dev_state *ps; -	const struct cred *cred = current_cred();  	int ret;  	ret = -ENOMEM; @@ -706,8 +721,7 @@ static int usbdev_open(struct inode *inode, struct file *file)  	init_waitqueue_head(&ps->wait);  	ps->discsignr = 0;  	ps->disc_pid = get_pid(task_pid(current)); -	ps->disc_uid = cred->uid; -	ps->disc_euid = cred->euid; +	ps->cred = get_current_cred();  	ps->disccontext = NULL;  	ps->ifclaimed = 0;  	security_task_getsecid(current, &ps->secid); @@ -749,6 +763,7 @@ static int usbdev_release(struct inode *inode, struct file *file)  	usb_unlock_device(dev);  	usb_put_dev(dev);  	put_pid(ps->disc_pid); +	put_cred(ps->cred);  	as = async_getcompleted(ps);  	while (as) { @@ -770,7 +785,8 @@ static int proc_control(struct dev_state *ps, void __user *arg)  	if (copy_from_user(&ctrl, arg, sizeof(ctrl)))  		return -EFAULT; -	ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.wIndex); +	ret = check_ctrlrecip(ps, ctrl.bRequestType, ctrl.bRequest, +			      ctrl.wIndex);  	if (ret)  		return ret;  	wLength = ctrl.wLength;		/* To suppress 64k PAGE_SIZE warning */ @@ -1048,7 +1064,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,  	struct usb_host_endpoint *ep;  	struct async *as;  	struct usb_ctrlrequest *dr = NULL; -	const struct cred *cred = current_cred();  	unsigned int u, totlen, isofrmlen;  	int ret, ifnum = -1;  	int is_in; @@ -1100,7 +1115,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,  			kfree(dr);  			return -EINVAL;  		} -		ret = check_ctrlrecip(ps, dr->bRequestType, +		ret = check_ctrlrecip(ps, dr->bRequestType, dr->bRequest,  				      le16_to_cpup(&dr->wIndex));  		if (ret) {  			kfree(dr); @@ -1262,8 +1277,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,  	as->signr = uurb->signr;  	as->ifnum = ifnum;  	as->pid = get_pid(task_pid(current)); -	as->uid = cred->uid; -	as->euid = cred->euid; +	as->cred = get_current_cred();  	security_task_getsecid(current, &as->secid);  	if (!is_in && uurb->buffer_length > 0) {  		if (copy_from_user(as->urb->transfer_buffer, uurb->buffer, @@ -1981,9 +1995,8 @@ static void usbdev_remove(struct usb_device *udev)  			sinfo.si_errno = EPIPE;  			sinfo.si_code = SI_ASYNCIO;  			sinfo.si_addr = ps->disccontext; -			kill_pid_info_as_uid(ps->discsignr, &sinfo, -					ps->disc_pid, ps->disc_uid, -					ps->disc_euid, ps->secid); +			kill_pid_info_as_cred(ps->discsignr, &sinfo, +					ps->disc_pid, ps->cred, ps->secid);  		}  	}  }  | 
