Hi This is a patch that introduces an interface authorization for USB devices. The kernel supports already a device authorization bacause of wireless USB. But the new interface authorization allows to enable or disable individual interfaces per bitmask instead allow or deny a whole device. As example you can allow the interface for a TV signal from a USB TV card, but deny a HID for the remote control. This was added against BadUSB attacks. Refer to: https://srlabs.de/badusb/ The interface authorization is used by an usb firewall named "usbauth". The code and binaries for openSUSE 13.2 can be found here: https://build.opensuse.org/project/show/home:skoch_suse The patch was tested with Linux 4.1-rc3. The functionality is oriented at existing kernel code like usb_set_configuration(), the device authorization, etc. If the interface authorization is not used, the kernel behavior is the same as without the patch. Best regards Stefan Koch --------------------------- diff --git a/drivers/base/base.h b/drivers/base/base.h index 251c5d3..6bbedda 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -102,6 +102,7 @@ extern void container_dev_init(void); struct kobject *virtual_device_parent(struct device *dev); extern int bus_add_device(struct device *dev); +extern void bus_probe_device_opt_probe(struct device *dev, bool probe); extern void bus_probe_device(struct device *dev); extern void bus_remove_device(struct device *dev); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 79bc203..e9e4c61 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -235,6 +235,11 @@ static ssize_t bind_store(struct device_driver *drv, const char *buf, } static DRIVER_ATTR_WO(bind); +unsigned int bus_get_drivers_autoprobe(struct bus_type *bus) { + return bus->p->drivers_autoprobe; +} +EXPORT_SYMBOL(bus_get_drivers_autoprobe); + static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf) { return sprintf(buf, "%d\n", bus->p->drivers_autoprobe); @@ -540,12 +545,13 @@ out_put: } /** - * bus_probe_device - probe drivers for a new device + * bus_probe_device_opt_probe - optional probing of drivers for a new device * @dev: device to probe + * @probe: if true probe as autoprobe is set, if false do not probe as no autoprobe is set * * - Automatically probe for a driver if the bus allows it. */ -void bus_probe_device(struct device *dev) +void bus_probe_device_opt_probe(struct device *dev, bool probe) { struct bus_type *bus = dev->bus; struct subsys_interface *sif; @@ -554,7 +560,7 @@ void bus_probe_device(struct device *dev) if (!bus) return; - if (bus->p->drivers_autoprobe) { + if (bus->p->drivers_autoprobe && probe) { ret = device_attach(dev); WARN_ON(ret < 0); } @@ -567,6 +573,16 @@ void bus_probe_device(struct device *dev) } /** + * bus_probe_device - probe drivers for a new device + * @dev: device to probe + * + * - Automatically probe for a driver if the bus allows it. + */ +void bus_probe_device(struct device *dev) { + bus_probe_device_opt_probe(dev, true); +} + +/** * bus_remove_device - remove device from bus * @dev: device to be removed * diff --git a/drivers/base/core.c b/drivers/base/core.c index 21d1303..6dec2f2 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -954,6 +954,7 @@ int device_private_init(struct device *dev) /** * device_add - add device to device hierarchy. * @dev: device. + * @probe: if false probing is off, if true probing is on * * This is part 2 of device_register(), though may be called * separately _iff_ device_initialize() has been called separately. @@ -973,7 +974,7 @@ int device_private_init(struct device *dev) * if it returned an error! Always use put_device() to give up your * reference instead. */ -int device_add(struct device *dev) +int device_add_opt_probe(struct device *dev, bool probe) { struct device *parent = NULL; struct kobject *kobj; @@ -1068,7 +1069,7 @@ int device_add(struct device *dev) BUS_NOTIFY_ADD_DEVICE, dev); kobject_uevent(&dev->kobj, KOBJ_ADD); - bus_probe_device(dev); + bus_probe_device_opt_probe(dev, probe); if (parent) klist_add_tail(&dev->p->knode_parent, &parent->p->klist_children); @@ -1114,9 +1115,62 @@ name_error: dev->p = NULL; goto done; } +EXPORT_SYMBOL_GPL(device_add_opt_probe); + +/** + * device_add - add device to device hierarchy. + * @dev: device. + * + * This is part 2 of device_register(), though may be called + * separately _iff_ device_initialize() has been called separately. + * + * This adds @dev to the kobject hierarchy via kobject_add(), adds it + * to the global and sibling lists for the device, then + * adds it to the other relevant subsystems of the driver model. + * + * Do not call this routine or device_register() more than once for + * any device structure. The driver model core is not designed to work + * with devices that get unregistered and then spring back to life. + * (Among other things, it's very hard to guarantee that all references + * to the previous incarnation of @dev have been dropped.) Allocate + * and register a fresh new struct device instead. + * + * NOTE: _Never_ directly free @dev after calling this function, even + * if it returned an error! Always use put_device() to give up your + * reference instead. + */ +int device_add(struct device *dev) { + return device_add_opt_probe(dev, true); +} EXPORT_SYMBOL_GPL(device_add); /** + * device_register_opt_probe - register a device with the system. + * @dev: pointer to the device structure + * @probe: if false probing is off, if true probing is on + * + * This happens in two clean steps - initialize the device + * and add it to the system. The two steps can be called + * separately, but this is the easiest and most common. + * I.e. you should only call the two helpers separately if + * have a clearly defined need to use and refcount the device + * before it is added to the hierarchy. + * + * For more information, see the kerneldoc for device_initialize() + * and device_add(). + * + * NOTE: _Never_ directly free @dev after calling this function, even + * if it returned an error! Always use put_device() to give up the + * reference initialized in this function instead. + */ +int device_register_opt_probe(struct device *dev, bool probe) +{ + device_initialize(dev); + return device_add_opt_probe(dev, probe); +} +EXPORT_SYMBOL_GPL(device_register_opt_probe); + +/** * device_register - register a device with the system. * @dev: pointer to the device structure * @@ -1136,8 +1190,7 @@ EXPORT_SYMBOL_GPL(device_add); */ int device_register(struct device *dev) { - device_initialize(dev); - return device_add(dev); + return device_register_opt_probe(dev, true); } EXPORT_SYMBOL_GPL(device_register); diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 818369a..cf2c94c 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -295,6 +295,10 @@ static int usb_probe_interface(struct device *dev) if (udev->authorized == 0) { dev_err(&intf->dev, "Device is not authorized for usage\n"); return error; + } else if (intf->authorized == 0) { + unsigned intfNr = intf->altsetting->desc.bInterfaceNumber; + dev_err(&intf->dev, "Interface 0x%02x is not authorized for usage\n", intfNr); + return error; } id = usb_match_dynamic_id(intf, driver); @@ -507,6 +511,10 @@ int usb_driver_claim_interface(struct usb_driver *driver, if (dev->driver) return -EBUSY; + /* reject claim if not iterface is not authorized */ + if (!iface->authorized) + return -ENODEV; + udev = interface_to_usbdev(iface); dev->driver = &driver->drvwrap.driver; diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 45a915c..2f9d087 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -882,9 +882,42 @@ static ssize_t authorized_default_store(struct device *dev, } static DEVICE_ATTR_RW(authorized_default); +/* + * show default authorization status of usb interface + * note: interface_auhorized_default is the default value for initialising interface_authorized + */ +static ssize_t interface_authorized_default_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct usb_device *usb_dev = to_usb_device(dev); + struct usb_hcd *hcd = bus_to_hcd(usb_dev->bus); + unsigned def = hcd->interface_authorized_default; + return sprintf(buf, "%u\n", def); +} + +/* + * store default authorization status of usb interface + * note: interface_auhorized_default is the default value for initialising interface_authorized + */ +static ssize_t interface_authorized_default_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct usb_device *usb_dev = to_usb_device(dev); + struct usb_hcd *hcd = bus_to_hcd(usb_dev->bus); + int rc = count; + unsigned val; + + if (!usb_dev || !hcd) + rc = -ENODEV; + else if (sscanf(buf, "%u\n", &val) != 1) + rc = -EINVAL; + else + hcd->interface_authorized_default = val ? 1 : 0; + + return rc; +} +static DEVICE_ATTR_RW(interface_authorized_default); + /* Group all the USB bus attributes */ static struct attribute *usb_bus_attrs[] = { &dev_attr_authorized_default.attr, + &dev_attr_interface_authorized_default.attr, NULL, }; @@ -2679,6 +2712,9 @@ int usb_add_hcd(struct usb_hcd *hcd, hcd->authorized_default = authorized_default; set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + /* per default all interfaces are authorized */ + hcd->interface_authorized_default = 1; + /* HC is in reset state, but accessible. Now do the one-time init, * bottom up so that hcds can customize the root hubs before hub_wq * starts talking to them. (Note, bus id is assigned early too.) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3b71516..f3831bf 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -2577,6 +2577,133 @@ out_authorized: return result; } +/* + * authorize or deauthorize an usb interface per bitmask + * + * @dev: device structure + * @mask: authorization mask + * 1 is to authorize, 0 is not to authorize + * example: 0b00000101 authorizes interface 1 and 3 and deauthorizes all other interfaces + * + * Returns: 0 at success, value<0 at failure + */ +int usb_device_set_mask(struct device *dev, u32 mask) { + struct usb_device *usb_dev = NULL; + struct usb_host_config *actconfig = NULL; + u32 oldmask = 0; + unsigned i, n, confNr; + + /* get device lock */ + device_lock(dev); + + usb_dev = to_usb_device(dev); + + if(usb_dev) + actconfig = usb_dev->actconfig; + + if (!usb_dev || !actconfig) { + device_unlock(dev); + return -ENODEV; + } + + usb_dev->mask_changed = 1; + + /* get current configuration number */ + confNr = actconfig->desc.bConfigurationValue; + + /* number of device's interfaces */ + n = actconfig->desc.bNumInterfaces; + + oldmask = usb_dev->mask; + + /* set the flags first and unbind interface if applicable */ + for (i = 0; i < n; i++) { + bool oldauth = oldmask & (1 << i) ? true : false; + bool auth = mask & (1 << i) ? true : false; + struct usb_interface *intf = actconfig->interface[i]; + struct device *intf_dev = NULL; + struct usb_device *intf_usb_dev = NULL; + + if(!intf) { + device_unlock(dev); + return -ENODEV; + } + + intf_dev = &intf->dev; + + if (intf_dev) + intf_usb_dev = to_usb_device(intf_dev); + + if (!intf_dev || !intf_usb_dev) { + device_unlock(dev); + return -ENODEV; + } + + if (!auth && oldauth) { + usb_dev->mask &= ~(1 << i); /* update mask */ + intf->authorized = 0; /* update flag */ + intf->unregistering = 1; + usb_forced_unbind_intf(intf); + } else if (auth && !oldauth) { + usb_dev->mask |= (1 << i); /* update mask */ + intf->authorized = 1; /* update flag */ + } + } + + /* disable or enable interfaces and delete device if applicable */ + for (i = 0; i < n; i++) { + bool oldauth = oldmask & (1 << i) ? true : false; + bool auth = mask & (1 << i) ? true : false; + struct usb_interface *intf = actconfig->interface[i]; + + if(!intf) { + device_unlock(dev); + return -ENODEV; + } + + if (!auth && oldauth) { + /* disable interface's endpoints for usage */ + usb_disable_interface(usb_dev, intf, true); + + /* delete interface device */ + if(device_is_registered(&intf->dev)) { + remove_intf_ep_devs(intf); + device_del(&intf->dev); + } + } else if (auth && !oldauth) { + /* enable interface's endpoints for usage */ + usb_enable_interface(usb_dev, intf, true); + } + } + + /* delete interface device and create new interface structure or register previous created interface */ + for (i = 0; i < n; i++) { + bool oldauth = oldmask & (1 << i) ? true : false; + bool auth = mask & (1 << i) ? true : false; + struct usb_interface *intf = actconfig->interface[i]; + + if(!intf) { + device_unlock(dev); + return -ENODEV; + } + + if (!auth && oldauth) { + /* allocate memory for new structure */ + struct usb_interface *newintf = kzalloc(sizeof(struct usb_interface), GFP_NOIO); + put_device(&intf->dev); /* last step of device deletion */ + usb_interface_init(newintf, usb_dev, actconfig, confNr, i); /* initialize allocated structure */ + usb_interface_register(usb_dev, newintf, confNr); /* register interface */ + } + else if (auth && !oldauth && bus_get_drivers_autoprobe(dev->bus)) { + bus_probe_device(&intf->dev); /* bind driver */ + } + } + + /* release device lock */ + device_unlock(dev); + + return 0; +} /* Returns 1 if @hub is a WUSB root hub, 0 otherwise */ static unsigned hub_is_wusb(struct usb_hub *hub) diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index f368d20..eed2204 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1035,7 +1035,7 @@ static int create_intf_ep_devs(struct usb_interface *intf) return 0; } -static void remove_intf_ep_devs(struct usb_interface *intf) +void remove_intf_ep_devs(struct usb_interface *intf) { struct usb_host_interface *alt = intf->cur_altsetting; int i; @@ -1069,6 +1069,8 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr, if (!dev) return; + dev_dbg(&dev->dev, "Disable endpoint with addr 0x%02x and reset flag %s\n", epaddr, reset_hardware ? "TRUE" : "FALSE"); + if (usb_endpoint_out(epaddr)) { ep = dev->ep_out[epnum]; if (reset_hardware) @@ -1225,6 +1227,9 @@ void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep, int epnum = usb_endpoint_num(&ep->desc); int is_out = usb_endpoint_dir_out(&ep->desc); int is_control = usb_endpoint_xfer_control(&ep->desc); + unsigned epaddr = ep->desc.bEndpointAddress; + + dev_dbg(&dev->dev, "Enable endpoint with addr 0x%02x and reset flag %s\n", epaddr, reset_ep ? "TRUE" : "FALSE"); if (reset_ep) usb_hcd_reset_endpoint(dev, ep); @@ -1644,6 +1649,80 @@ static void __usb_queue_reset_device(struct work_struct *ws) usb_put_intf(iface); /* Undo _get_ in usb_queue_reset_device() */ } +/* + * Initialize the usb_interface structure + * + * @dev: usb parent of usb intf + * @cp: usb configuration to update + * @intf: structure to initialize + * @confNr: number of desired configuration + * @intfNr: interface number + */ +void usb_interface_init(struct usb_interface *intf, struct usb_device *dev, struct usb_host_config *cp, unsigned confNr, unsigned intfNr) { + struct usb_interface_cache *intfc; + struct usb_host_interface *alt; + + cp->interface[intfNr] = intf; + intfc = cp->intf_cache[intfNr]; + intf->altsetting = intfc->altsetting; + intf->num_altsetting = intfc->num_altsetting; + intf->authorized = dev->mask & (1 << intfNr) ? true : false; + kref_get(&intfc->ref); + + alt = usb_altnum_to_altsetting(intf, 0); + + /* No altsetting 0? We'll assume the first altsetting. + * We could use a GetInterface call, but if a device is + * so non-compliant that it doesn't have altsetting 0 + * then I wouldn't trust its reply anyway. + */ + if (!alt) + alt = &intf->altsetting[0]; + + intf->intf_assoc = + find_iad(dev, cp, alt->desc.bInterfaceNumber); + intf->cur_altsetting = alt; + + intf->dev.parent = &dev->dev; + intf->dev.driver = NULL; + intf->dev.bus = &usb_bus_type; + intf->dev.type = &usb_if_device_type; + intf->dev.groups = usb_interface_groups; + intf->dev.dma_mask = dev->dev.dma_mask; + INIT_WORK(&intf->reset_ws, __usb_queue_reset_device); + intf->minor = -1; + device_initialize(&intf->dev); + pm_runtime_no_callbacks(&intf->dev); + dev_set_name(&intf->dev, "%d-%s:%d.%d", + dev->bus->busnum, dev->devpath, + confNr, alt->desc.bInterfaceNumber); + usb_get_dev(dev); +} + +/* + * Register interfaces to trigger driver binding + * Note: if the interface is not authorized drives will not probed + * + * Returns: 0 at success + */ +int usb_interface_register(struct usb_device *dev, struct usb_interface *intf, int confNr) { + int ret = 0; + + dev_dbg(&dev->dev, + "adding %s (config #%d, interface %d)\n", + dev_name(&intf->dev), confNr, + intf->cur_altsetting->desc.bInterfaceNumber); + device_enable_async_suspend(&intf->dev); + ret = device_add_opt_probe(&intf->dev, intf->authorized); /* probe if authorized */ + if (ret != 0) { + dev_err(&dev->dev, "device_add(%s) --> %d\n", + dev_name(&intf->dev), ret); + return ret; + } else + create_intf_ep_devs(intf); + + return ret; +} /* * usb_set_configuration - Makes a particular device setting be current @@ -1799,44 +1878,18 @@ free_interfaces: * hc/hcd/usbcore interface/endpoint state. */ for (i = 0; i < nintf; ++i) { - struct usb_interface_cache *intfc; - struct usb_interface *intf; - struct usb_host_interface *alt; + struct usb_interface *intf = new_interfaces[i]; - cp->interface[i] = intf = new_interfaces[i]; - intfc = cp->intf_cache[i]; - intf->altsetting = intfc->altsetting; - intf->num_altsetting = intfc->num_altsetting; - kref_get(&intfc->ref); + // update device's mask at configuration change + if(hcd->interface_authorized_default) + dev->mask |= (1 << i); + else + dev->mask &= ~(1 << i); - alt = usb_altnum_to_altsetting(intf, 0); + usb_interface_init(intf, dev, cp, configuration, i); - /* No altsetting 0? We'll assume the first altsetting. - * We could use a GetInterface call, but if a device is - * so non-compliant that it doesn't have altsetting 0 - * then I wouldn't trust its reply anyway. - */ - if (!alt) - alt = &intf->altsetting[0]; - - intf->intf_assoc = - find_iad(dev, cp, alt->desc.bInterfaceNumber); - intf->cur_altsetting = alt; - usb_enable_interface(dev, intf, true); - intf->dev.parent = &dev->dev; - intf->dev.driver = NULL; - intf->dev.bus = &usb_bus_type; - intf->dev.type = &usb_if_device_type; - intf->dev.groups = usb_interface_groups; - intf->dev.dma_mask = dev->dev.dma_mask; - INIT_WORK(&intf->reset_ws, __usb_queue_reset_device); - intf->minor = -1; - device_initialize(&intf->dev); - pm_runtime_no_callbacks(&intf->dev); - dev_set_name(&intf->dev, "%d-%s:%d.%d", - dev->bus->busnum, dev->devpath, - configuration, alt->desc.bInterfaceNumber); - usb_get_dev(dev); + if (intf->authorized) + usb_enable_interface(dev, intf, true); } kfree(new_interfaces); @@ -1886,22 +1939,11 @@ free_interfaces: */ for (i = 0; i < nintf; ++i) { struct usb_interface *intf = cp->interface[i]; - - dev_dbg(&dev->dev, - "adding %s (config #%d, interface %d)\n", - dev_name(&intf->dev), configuration, - intf->cur_altsetting->desc.bInterfaceNumber); - device_enable_async_suspend(&intf->dev); - ret = device_add(&intf->dev); - if (ret != 0) { - dev_err(&dev->dev, "device_add(%s) --> %d\n", - dev_name(&intf->dev), ret); - continue; - } - create_intf_ep_devs(intf); + ret = usb_interface_register(dev, intf, configuration); } usb_autosuspend_device(dev); + dev->mask_changed = 0; // reset mask change status return 0; } EXPORT_SYMBOL_GPL(usb_set_configuration); diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index d269738..e796443 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -622,7 +622,6 @@ usb_descriptor_attr(bDeviceProtocol, "%02x\n"); usb_descriptor_attr(bNumConfigurations, "%d\n"); usb_descriptor_attr(bMaxPacketSize0, "%d\n"); - /* show if the device is authorized (1) or not (0) */ static ssize_t authorized_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -655,6 +654,62 @@ static ssize_t authorized_store(struct device *dev, static DEVICE_ATTR_IGNORE_LOCKDEP(authorized, S_IRUGO | S_IWUSR, authorized_show, authorized_store); +/* + * show authorization status of usb interface as bitmask + * 1 is authorized, 0 is not authorized + * example: 0b00000101 interfaces 1 and 3 are authorized, the others ar not authorized + */ +static ssize_t interface_authorization_mask_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct usb_device *usb_dev = to_usb_device(dev); + __u32 val = 0; + unsigned i, n; + + if (!usb_dev->actconfig) + return -ENODEV; + + /* number of device's interfaces */ + n = usb_dev->actconfig->desc.bNumInterfaces; + for (i = 0; i < n; i++) { + val |= usb_dev->actconfig->interface[i]->authorized << i; + } + + return sprintf(buf, "%02x\n", val); +} + +/* + * authorize or deauthorize an usb interface per bitmask + * 1 is to authorize, 0 is not to authorize + * example: 0b00000101 authorizes interface 1 and 3 and deauthorizes all other interfaces + */ +static ssize_t interface_authorization_mask_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct usb_device *usb_dev = to_usb_device(dev); + int rc = 0; + u32 val = 0; + unsigned i, n, configuration; + + if (!usb_dev->actconfig) + return -ENODEV; + else if (sscanf(buf, "%x\n", &val) != 1) + return -EINVAL; + + rc = usb_device_set_mask(dev, val); + + return rc < 0 ? rc : count; +} +static DEVICE_ATTR_RW(interface_authorization_mask); + +/* + * show authorization change status of usb interfaces bitmask + * if 1 the mask has modified if 0 is was not modified + * note: would be set after mask_store was called, also if the mask has the same value as the old + */ +static ssize_t interface_authorization_mask_changed_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct usb_device *usb_dev = to_usb_device(dev); + + return sprintf(buf, "%u\n", usb_dev->mask_changed); +} +static DEVICE_ATTR_RO(interface_authorization_mask_changed); + /* "Safely remove a device" */ static ssize_t remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -703,6 +758,8 @@ static struct attribute *dev_attrs[] = { &dev_attr_quirks.attr, &dev_attr_avoid_reset_quirk.attr, &dev_attr_authorized.attr, + &dev_attr_interface_authorization_mask.attr, + &dev_attr_interface_authorization_mask_changed.attr, &dev_attr_remove.attr, &dev_attr_removable.attr, &dev_attr_ltm_capable.attr, diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 8d5b2f4..f4d9d8f 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -507,12 +507,20 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, dev->connect_time = jiffies; dev->active_duration = -jiffies; #endif - if (root_hub) /* Root hub always ok [and always wired] */ + if (root_hub) { /* Root hub always ok [and always wired] */ dev->authorized = 1; - else { + dev->mask = 0; + // invert the mask. each bit of the mask is now TRUE. all interfaces should be allowed. + dev->mask = ~dev->mask; + } else { dev->authorized = usb_hcd->authorized_default; dev->wusb = usb_bus_is_wusb(bus) ? 1 : 0; + dev->mask = 0; + // invert the mask if interface_authorized_default is TRUE. each bit of the mask is now TRUE. all interfaces should be allowed. + // do not invert the mask if interface_authorized_default is FALSE. each bit of the mask is now FALSE. no interface should be allowed. + dev->mask = usb_hcd->interface_authorized_default ? ~dev->mask : dev->mask; } + dev->mask_changed = 0; // changed during usb_device_set_mask() return dev; } EXPORT_SYMBOL_GPL(usb_alloc_dev); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 7eb1e26..994b0c3 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -27,6 +27,7 @@ extern void usb_release_interface_cache(struct kref *ref); extern void usb_disable_device(struct usb_device *dev, int skip_ep0); extern int usb_deauthorize_device(struct usb_device *); extern int usb_authorize_device(struct usb_device *); +extern int usb_device_set_mask(struct device *dev, u32 mask); extern void usb_detect_quirks(struct usb_device *udev); extern void usb_detect_interface_quirks(struct usb_device *udev); extern int usb_remove_device(struct usb_device *udev); diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index ec8ac16..ca82bec 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -2380,7 +2380,7 @@ void xhci_drop_ep_from_interval_table(struct xhci_hcd *xhci, struct xhci_interval_bw *interval_bw; int normalized_interval; - if (xhci_is_async_ep(ep_bw->type)) + if (!ep_bw || xhci_is_async_ep(ep_bw->type)) return; if (udev->speed == USB_SPEED_SUPER) { @@ -2444,7 +2444,7 @@ static void xhci_add_ep_to_interval_table(struct xhci_hcd *xhci, struct xhci_virt_ep *smaller_ep; int normalized_interval; - if (xhci_is_async_ep(ep_bw->type)) + if (!ep_bw || xhci_is_async_ep(ep_bw->type)) return; if (udev->speed == USB_SPEED_SUPER) { diff --git a/include/linux/device.h b/include/linux/device.h index 6558af9..874f4f3 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -137,6 +137,8 @@ extern void bus_unregister(struct bus_type *bus); extern int __must_check bus_rescan_devices(struct bus_type *bus); +extern unsigned int bus_get_drivers_autoprobe(struct bus_type *bus); + /* iterator helpers for buses */ struct subsys_dev_iter { struct klist_iter ki; @@ -921,10 +923,12 @@ void driver_init(void); /* * High level routines for use by the bus drivers */ +extern int device_register_opt_probe(struct device *dev, bool probe); extern int __must_check device_register(struct device *dev); extern void device_unregister(struct device *dev); extern void device_initialize(struct device *dev); extern int __must_check device_add(struct device *dev); +extern int __must_check device_add_opt_probe(struct device *dev, bool probe); extern void device_del(struct device *dev); extern int device_for_each_child(struct device *dev, void *data, int (*fn)(struct device *dev, void *data)); @@ -976,6 +980,8 @@ extern void device_release_driver(struct device *dev); extern int __must_check device_attach(struct device *dev); extern int __must_check driver_attach(struct device_driver *drv); extern int __must_check device_reprobe(struct device *dev); +extern void bus_probe_device(struct device *dev); +extern void bus_probe_device_opt_probe(struct device *dev, bool probe); /* * Easy functions for dynamically creating devices on the fly diff --git a/include/linux/usb.h b/include/linux/usb.h index 447fe29..0b1e854 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -171,6 +171,7 @@ struct usb_interface { int minor; /* minor number this interface is * bound to */ enum usb_interface_condition condition; /* state of binding */ + unsigned authorized:1; /* for policy that allows using the interface */ unsigned sysfs_files_created:1; /* the sysfs attributes exist */ unsigned ep_devs_created:1; /* endpoint "devices" exist */ unsigned unregistering:1; /* unregistration is in progress */ @@ -502,6 +503,7 @@ struct usb3_lpm_parameters { * @authenticated: Crypto authentication passed * @wusb: device is Wireless USB * @lpm_capable: device supports LPM + * @mask_changed: true if @mask was changed since configuration setup * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM * @usb2_hw_lpm_besl_capable: device can perform USB2 hardware BESL LPM * @usb2_hw_lpm_enabled: USB2 hardware LPM is enabled @@ -511,6 +513,7 @@ struct usb3_lpm_parameters { * @product: iProduct string, if present (static) * @manufacturer: iManufacturer string, if present (static) * @serial: iSerialNumber string, if present (static) + * @mask: interface authorization mask to allow or denz specific usb interfaces * @filelist: usbfs files that are open to this device * @maxchild: number of ports if hub * @quirks: quirks of the whole device @@ -575,6 +578,7 @@ struct usb_device { unsigned authenticated:1; unsigned wusb:1; unsigned lpm_capable:1; + unsigned mask_changed:1; unsigned usb2_hw_lpm_capable:1; unsigned usb2_hw_lpm_besl_capable:1; unsigned usb2_hw_lpm_enabled:1; @@ -587,6 +591,8 @@ struct usb_device { char *manufacturer; char *serial; + u32 mask; + struct list_head filelist; int maxchild; @@ -1691,6 +1697,8 @@ extern int usb_get_status(struct usb_device *dev, extern int usb_string(struct usb_device *dev, int index, char *buf, size_t size); +extern void remove_intf_ep_devs(struct usb_interface *intf); + /* wrappers that also update important state inside usbcore */ extern int usb_clear_halt(struct usb_device *dev, int pipe); extern int usb_reset_configuration(struct usb_device *dev); @@ -1700,6 +1708,10 @@ extern void usb_reset_endpoint(struct usb_device *dev, unsigned int epaddr); /* this request isn't really synchronous, but it belongs with the others */ extern int usb_driver_set_configuration(struct usb_device *udev, int config); +/* usb_set_configuration helper functions */ +extern void usb_interface_init(struct usb_interface *intf, struct usb_device *dev, struct usb_host_config *cp, unsigned confNr, unsigned intfNr); +extern int usb_interface_register(struct usb_device *dev, struct usb_interface *intf, int configuration); + /* choose and set configuration for device */ extern int usb_choose_configuration(struct usb_device *udev); extern int usb_set_configuration(struct usb_device *dev, int configuration); diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 68b1e83..85eff49 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -142,6 +142,7 @@ struct usb_hcd { unsigned uses_new_polling:1; unsigned wireless:1; /* Wireless USB HCD */ unsigned authorized_default:1; + unsigned interface_authorized_default:1; unsigned has_tt:1; /* Integrated TT in root hub */ unsigned amd_resume_bug:1; /* AMD remote wakeup quirk */ unsigned can_do_streams:1; /* HC supports streams */ -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html