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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to