[PATCH] Add usb interface authorization

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux