Skip to content

Commit d883f52

Browse files
reillyeongregkh
authored andcommitted
usb: devio: Add ioctl to disallow detaching kernel USB drivers.
The new USBDEVFS_DROP_PRIVILEGES ioctl allows a process to voluntarily relinquish the ability to issue other ioctls that may interfere with other processes and drivers that have claimed an interface on the device. This commit also includes a simple utility to be able to test the ioctl, located at Documentation/usb/usbdevfs-drop-permissions.c Example (with qemu-kvm's input device): $ lsusb ... Bus 001 Device 002: ID 0627:0001 Adomax Technology Co., Ltd $ usb-devices ... C: #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr=100mA I: If#= 0 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=00 Prot=02 Driver=usbhid $ sudo ./usbdevfs-drop-permissions /dev/bus/usb/001/002 OK: privileges dropped! Available options: [0] Exit now [1] Reset device. Should fail if device is in use [2] Claim 4 interfaces. Should succeed where not in use [3] Narrow interface permission mask Which option shall I run?: 1 ERROR: USBDEVFS_RESET failed! (1 - Operation not permitted) Which test shall I run next?: 2 ERROR claiming if 0 (1 - Operation not permitted) ERROR claiming if 1 (1 - Operation not permitted) ERROR claiming if 2 (1 - Operation not permitted) ERROR claiming if 3 (1 - Operation not permitted) Which test shall I run next?: 0 After unbinding usbhid: $ usb-devices ... I: If#= 0 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=00 Prot=02 Driver=(none) $ sudo ./usbdevfs-drop-permissions /dev/bus/usb/001/002 ... Which option shall I run?: 2 OK: claimed if 0 ERROR claiming if 1 (1 - Operation not permitted) ERROR claiming if 2 (1 - Operation not permitted) ERROR claiming if 3 (1 - Operation not permitted) Which test shall I run next?: 1 OK: USBDEVFS_RESET succeeded Which test shall I run next?: 0 After unbinding usbhid and restricting the mask: $ sudo ./usbdevfs-drop-permissions /dev/bus/usb/001/002 ... Which option shall I run?: 3 Insert new mask: 0 OK: privileges dropped! Which test shall I run next?: 2 ERROR claiming if 0 (1 - Operation not permitted) ERROR claiming if 1 (1 - Operation not permitted) ERROR claiming if 2 (1 - Operation not permitted) ERROR claiming if 3 (1 - Operation not permitted) Signed-off-by: Reilly Grant <reillyg@chromium.org> Acked-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Emilio López <emilio.lopez@collabora.co.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 3d0712d commit d883f52

File tree

4 files changed

+192
-5
lines changed

4 files changed

+192
-5
lines changed

Documentation/DocBook/usb.tmpl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,18 @@ usbdev_ioctl (int fd, int ifno, unsigned request, void *param)
732732
or SET_INTERFACE.
733733
</para></warning></listitem></varlistentry>
734734

735+
<varlistentry><term>USBDEVFS_DROP_PRIVILEGES</term>
736+
<listitem><para>This is used to relinquish the ability
737+
to do certain operations which are considered to be
738+
privileged on a usbfs file descriptor.
739+
This includes claiming arbitrary interfaces, resetting
740+
a device on which there are currently claimed interfaces
741+
from other users, and issuing USBDEVFS_IOCTL calls.
742+
The ioctl parameter is a 32 bit mask of interfaces
743+
the user is allowed to claim on this file descriptor.
744+
You may issue this ioctl more than one time to narrow
745+
said mask.
746+
</para></listitem></varlistentry>
735747
</variablelist>
736748

737749
</sect2>
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#include <sys/ioctl.h>
2+
#include <sys/types.h>
3+
#include <sys/stat.h>
4+
#include <fcntl.h>
5+
#include <stdio.h>
6+
#include <errno.h>
7+
#include <string.h>
8+
#include <inttypes.h>
9+
#include <unistd.h>
10+
11+
#include <linux/usbdevice_fs.h>
12+
13+
/* For building without an updated set of headers */
14+
#ifndef USBDEVFS_DROP_PRIVILEGES
15+
#define USBDEVFS_DROP_PRIVILEGES _IOW('U', 30, __u32)
16+
#define USBDEVFS_CAP_DROP_PRIVILEGES 0x40
17+
#endif
18+
19+
void drop_privileges(int fd, uint32_t mask)
20+
{
21+
int res;
22+
23+
res = ioctl(fd, USBDEVFS_DROP_PRIVILEGES, &mask);
24+
if (res)
25+
printf("ERROR: USBDEVFS_DROP_PRIVILEGES returned %d\n", res);
26+
else
27+
printf("OK: privileges dropped!\n");
28+
}
29+
30+
void reset_device(int fd)
31+
{
32+
int res;
33+
34+
res = ioctl(fd, USBDEVFS_RESET);
35+
if (!res)
36+
printf("OK: USBDEVFS_RESET succeeded\n");
37+
else
38+
printf("ERROR: reset failed! (%d - %s)\n",
39+
-res, strerror(-res));
40+
}
41+
42+
void claim_some_intf(int fd)
43+
{
44+
int i, res;
45+
46+
for (i = 0; i < 4; i++) {
47+
res = ioctl(fd, USBDEVFS_CLAIMINTERFACE, &i);
48+
if (!res)
49+
printf("OK: claimed if %d\n", i);
50+
else
51+
printf("ERROR claiming if %d (%d - %s)\n",
52+
i, -res, strerror(-res));
53+
}
54+
}
55+
56+
int main(int argc, char *argv[])
57+
{
58+
uint32_t mask, caps;
59+
int c, fd;
60+
61+
fd = open(argv[1], O_RDWR);
62+
if (fd < 0) {
63+
printf("Failed to open file\n");
64+
goto err_fd;
65+
}
66+
67+
/*
68+
* check if dropping privileges is supported,
69+
* bail on systems where the capability is not present
70+
*/
71+
ioctl(fd, USBDEVFS_GET_CAPABILITIES, &caps);
72+
if (!(caps & USBDEVFS_CAP_DROP_PRIVILEGES)) {
73+
printf("DROP_PRIVILEGES not supported\n");
74+
goto err;
75+
}
76+
77+
/*
78+
* Drop privileges but keep the ability to claim all
79+
* free interfaces (i.e., those not used by kernel drivers)
80+
*/
81+
drop_privileges(fd, -1U);
82+
83+
printf("Available options:\n"
84+
"[0] Exit now\n"
85+
"[1] Reset device. Should fail if device is in use\n"
86+
"[2] Claim 4 interfaces. Should succeed where not in use\n"
87+
"[3] Narrow interface permission mask\n"
88+
"Which option shall I run?: ");
89+
90+
while (scanf("%d", &c) == 1) {
91+
switch (c) {
92+
case 0:
93+
goto exit;
94+
case 1:
95+
reset_device(fd);
96+
break;
97+
case 2:
98+
claim_some_intf(fd);
99+
break;
100+
case 3:
101+
printf("Insert new mask: ");
102+
scanf("%x", &mask);
103+
drop_privileges(fd, mask);
104+
break;
105+
default:
106+
printf("I don't recognize that\n");
107+
}
108+
109+
printf("Which test shall I run next?: ");
110+
}
111+
112+
exit:
113+
close(fd);
114+
return 0;
115+
116+
err:
117+
close(fd);
118+
err_fd:
119+
return 1;
120+
}

drivers/usb/core/devio.c

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ struct usb_dev_state {
7979
unsigned long ifclaimed;
8080
u32 secid;
8181
u32 disabled_bulk_eps;
82+
bool privileges_dropped;
83+
unsigned long interface_allowed_mask;
8284
};
8385

8486
struct usb_memory {
@@ -748,6 +750,10 @@ static int claimintf(struct usb_dev_state *ps, unsigned int ifnum)
748750
if (test_bit(ifnum, &ps->ifclaimed))
749751
return 0;
750752

753+
if (ps->privileges_dropped &&
754+
!test_bit(ifnum, &ps->interface_allowed_mask))
755+
return -EACCES;
756+
751757
intf = usb_ifnum_to_if(dev, ifnum);
752758
if (!intf)
753759
err = -ENOENT;
@@ -985,7 +991,7 @@ static int usbdev_open(struct inode *inode, struct file *file)
985991
int ret;
986992

987993
ret = -ENOMEM;
988-
ps = kmalloc(sizeof(struct usb_dev_state), GFP_KERNEL);
994+
ps = kzalloc(sizeof(struct usb_dev_state), GFP_KERNEL);
989995
if (!ps)
990996
goto out_free_ps;
991997

@@ -1013,17 +1019,15 @@ static int usbdev_open(struct inode *inode, struct file *file)
10131019

10141020
ps->dev = dev;
10151021
ps->file = file;
1022+
ps->interface_allowed_mask = 0xFFFFFFFF; /* 32 bits */
10161023
spin_lock_init(&ps->lock);
10171024
INIT_LIST_HEAD(&ps->list);
10181025
INIT_LIST_HEAD(&ps->async_pending);
10191026
INIT_LIST_HEAD(&ps->async_completed);
10201027
INIT_LIST_HEAD(&ps->memory_list);
10211028
init_waitqueue_head(&ps->wait);
1022-
ps->discsignr = 0;
10231029
ps->disc_pid = get_pid(task_pid(current));
10241030
ps->cred = get_current_cred();
1025-
ps->disccontext = NULL;
1026-
ps->ifclaimed = 0;
10271031
security_task_getsecid(current, &ps->secid);
10281032
smp_wmb();
10291033
list_add_tail(&ps->list, &dev->filelist);
@@ -1324,6 +1328,28 @@ static int proc_connectinfo(struct usb_dev_state *ps, void __user *arg)
13241328

13251329
static int proc_resetdevice(struct usb_dev_state *ps)
13261330
{
1331+
struct usb_host_config *actconfig = ps->dev->actconfig;
1332+
struct usb_interface *interface;
1333+
int i, number;
1334+
1335+
/* Don't allow a device reset if the process has dropped the
1336+
* privilege to do such things and any of the interfaces are
1337+
* currently claimed.
1338+
*/
1339+
if (ps->privileges_dropped && actconfig) {
1340+
for (i = 0; i < actconfig->desc.bNumInterfaces; ++i) {
1341+
interface = actconfig->interface[i];
1342+
number = interface->cur_altsetting->desc.bInterfaceNumber;
1343+
if (usb_interface_claimed(interface) &&
1344+
!test_bit(number, &ps->ifclaimed)) {
1345+
dev_warn(&ps->dev->dev,
1346+
"usbfs: interface %d claimed by %s while '%s' resets device\n",
1347+
number, interface->dev.driver->name, current->comm);
1348+
return -EACCES;
1349+
}
1350+
}
1351+
}
1352+
13271353
return usb_reset_device(ps->dev);
13281354
}
13291355

@@ -2090,6 +2116,9 @@ static int proc_ioctl(struct usb_dev_state *ps, struct usbdevfs_ioctl *ctl)
20902116
struct usb_interface *intf = NULL;
20912117
struct usb_driver *driver = NULL;
20922118

2119+
if (ps->privileges_dropped)
2120+
return -EACCES;
2121+
20932122
/* alloc buffer */
20942123
size = _IOC_SIZE(ctl->ioctl_code);
20952124
if (size > 0) {
@@ -2215,7 +2244,8 @@ static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg)
22152244
__u32 caps;
22162245

22172246
caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM |
2218-
USBDEVFS_CAP_REAP_AFTER_DISCONNECT | USBDEVFS_CAP_MMAP;
2247+
USBDEVFS_CAP_REAP_AFTER_DISCONNECT | USBDEVFS_CAP_MMAP |
2248+
USBDEVFS_CAP_DROP_PRIVILEGES;
22192249
if (!ps->dev->bus->no_stop_on_short)
22202250
caps |= USBDEVFS_CAP_BULK_CONTINUATION;
22212251
if (ps->dev->bus->sg_tablesize)
@@ -2242,6 +2272,9 @@ static int proc_disconnect_claim(struct usb_dev_state *ps, void __user *arg)
22422272
if (intf->dev.driver) {
22432273
struct usb_driver *driver = to_usb_driver(intf->dev.driver);
22442274

2275+
if (ps->privileges_dropped)
2276+
return -EACCES;
2277+
22452278
if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_IF_DRIVER) &&
22462279
strncmp(dc.driver, intf->dev.driver->name,
22472280
sizeof(dc.driver)) != 0)
@@ -2298,6 +2331,23 @@ static int proc_free_streams(struct usb_dev_state *ps, void __user *arg)
22982331
return r;
22992332
}
23002333

2334+
static int proc_drop_privileges(struct usb_dev_state *ps, void __user *arg)
2335+
{
2336+
u32 data;
2337+
2338+
if (copy_from_user(&data, arg, sizeof(data)))
2339+
return -EFAULT;
2340+
2341+
/* This is an one way operation. Once privileges are
2342+
* dropped, you cannot regain them. You may however reissue
2343+
* this ioctl to shrink the allowed interfaces mask.
2344+
*/
2345+
ps->interface_allowed_mask &= data;
2346+
ps->privileges_dropped = true;
2347+
2348+
return 0;
2349+
}
2350+
23012351
/*
23022352
* NOTE: All requests here that have interface numbers as parameters
23032353
* are assuming that somehow the configuration has been prevented from
@@ -2486,6 +2536,9 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
24862536
case USBDEVFS_FREE_STREAMS:
24872537
ret = proc_free_streams(ps, p);
24882538
break;
2539+
case USBDEVFS_DROP_PRIVILEGES:
2540+
ret = proc_drop_privileges(ps, p);
2541+
break;
24892542
}
24902543

24912544
done:

include/uapi/linux/usbdevice_fs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ struct usbdevfs_hub_portinfo {
135135
#define USBDEVFS_CAP_BULK_SCATTER_GATHER 0x08
136136
#define USBDEVFS_CAP_REAP_AFTER_DISCONNECT 0x10
137137
#define USBDEVFS_CAP_MMAP 0x20
138+
#define USBDEVFS_CAP_DROP_PRIVILEGES 0x40
138139

139140
/* USBDEVFS_DISCONNECT_CLAIM flags & struct */
140141

@@ -188,5 +189,6 @@ struct usbdevfs_streams {
188189
#define USBDEVFS_DISCONNECT_CLAIM _IOR('U', 27, struct usbdevfs_disconnect_claim)
189190
#define USBDEVFS_ALLOC_STREAMS _IOR('U', 28, struct usbdevfs_streams)
190191
#define USBDEVFS_FREE_STREAMS _IOR('U', 29, struct usbdevfs_streams)
192+
#define USBDEVFS_DROP_PRIVILEGES _IOW('U', 30, __u32)
191193

192194
#endif /* _UAPI_LINUX_USBDEVICE_FS_H */

0 commit comments

Comments
 (0)