Re: [PATCH RESEND (1-3)/4] Add usb interface authorization

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

 



Am Montag, den 08.06.2015, 07:40 -0700 schrieb Greg KH:
> On Mon, Jun 08, 2015 at 03:24:26PM +0200, Stefan Koch wrote:
> > 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
> 
> Care to resend this in a format that it could be applied in (i.e. broken
> up into logical chunks with the proper Signed-off-by: lines)?
> 
> As this is, there's nothing we can do with it.
> 
> thanks,
> 
> greg k-h

Hi

I think whitespaces should be ok now. I have tested now three mail
clients...

This patch series enables the interface authorization. The description
is improved now. Is the series correct now?

The first patch allows to handle the probing of drivers
 manually.

The second patch unitizes the usb set configuration process.

Both, the first and the second patch are independent.

And the third patch introduces the usb interface authorization. This is
the main concern of this kernel modification.

The third patch depends on the first and second patch.

Thanks

Stefan Koch

----------------------------------------------------

>From 0e66e17cc446e721f60d1bfe465f08f4368c0957 Mon Sep 17 00:00:00 2001
From: Stefan Koch <skoch@xxxxxxx>
Date: Tue, 9 Jun 2015 14:24:12 +0200
Subject: [PATCH 1/4] This patch allows to handle the probing of drivers
 manually.

Introduces functions with the extension _opt_probe for bus_probe_device(), device_add() and device_register().
The opt probe boolean value is passed through. It deny the autoprobe mechanism although it is enabled.
The three existing functions call these with extension but with TRUE as default to avoid redundant code.
A new function bus_get_drivers_autoprobe() is introduced to return the autoprobe status.

These modifications are needed for the interface authorization.

Signed-off-by: Stefan Koch <skoch@xxxxxxx>
---
 drivers/base/base.h    |  1 +
 drivers/base/bus.c     | 22 +++++++++++++++---
 drivers/base/core.c    | 61 ++++++++++++++++++++++++++++++++++++++++++++++----
 include/linux/device.h |  6 +++++
 4 files changed, 83 insertions(+), 7 deletions(-)

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/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
-- 
2.1.4

>From b78875a6b6f59db425601ba7d7c97efc960ec46a Mon Sep 17 00:00:00 2001
From: Stefan Koch <skoch@xxxxxxx>
Date: Tue, 9 Jun 2015 14:25:07 +0200
Subject: [PATCH 2/4] This patch unitizes the usb set configuration process

The initialization of the usb_interface structure and the interface registration is moved out from usb_set_configurations
to the separate functions usb_interface_init() and usb_interface_register().

So this code can shared with the respective interface authorization code. Furthermore, logical groups are separated.

Signed-off-by: Stefan Koch <skoch@xxxxxxx>
---
 drivers/usb/core/message.c | 131 ++++++++++++++++++++++++++++-----------------
 include/linux/usb.h        |   6 +++
 2 files changed, 88 insertions(+), 49 deletions(-)

diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index f368d20..374ad4a 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,79 @@ 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;
+	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(&intf->dev);
+	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 +1877,11 @@ 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);
-
-		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);
 	}
 	kfree(new_interfaces);
 
@@ -1886,19 +1931,7 @@ 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);
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 447fe29..a92600a 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -1691,6 +1691,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 +1702,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);
-- 
2.1.4

>From 21911dfc3a82f3456574a3fa7713099db444bbe8 Mon Sep 17 00:00:00 2001
From: Stefan Koch <skoch@xxxxxxx>
Date: Tue, 9 Jun 2015 14:25:35 +0200
Subject: [PATCH 3/4] This patch introduces the usb interface authorization.

The kernel supports already a device authorization because of wireless USB. These is usable for wired usb devices, too.
These new interface authorization allows to enable or disable individual interfaces per bitmask instead a whole device.

The authorization is done stepwise in usb_device_set_mask() for each respective interface group (defined by respective mask) to avoid side effects.

At deauthorization the interface group mask would updated and the interface group would unbound.
After that the interface group is disabled, endpoints and the device would deleted.
New interface structures are initialized and registered. The device gets appropriate entrys in the SysFS.
An enabled autoprobe would denied and is done after authorization, first. Also interface claims would rejected.

At authorization the interface group masks would updated.
After that the usb interfaces are enabled.
Finally, the device is probed. Also interface claims would allowed.

Interfaces would allowed per default. This can disabled or enabled by writing 0 or 1 to
/sys/bus/usb/devices/usb*/interface_authorized_default

To allow (1) or deny (0) interfaces it is needed to write to the mask.
As default each bit has the initial value of the default authorization bit.
The value would showed or have to be written in hexadecimal format.
/sys/bus/usb/devices/*-*/interface_authorization_mask

A status bit shows whether the mask was changed manually after usb configuration setting.

Example: Only the interfaces 0 and 2 from device at 3-2 should allowed, the others should be denied.
echo 5 > /sys/bus/usb/devices/3-2/interface_authorization_mask
After this the changed-status-bit is 1.

Signed-off-by: Stefan Koch <skoch@xxxxxxx>
---
 drivers/usb/core/driver.c  |   8 +++
 drivers/usb/core/hcd.c     |  36 +++++++++++++
 drivers/usb/core/hub.c     | 127 +++++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/core/message.c |  13 ++++-
 drivers/usb/core/sysfs.c   |  59 ++++++++++++++++++++-
 drivers/usb/core/usb.c     |  12 ++++-
 drivers/usb/core/usb.h     |   1 +
 include/linux/usb.h        |   6 +++
 include/linux/usb/hcd.h    |   1 +
 9 files changed, 258 insertions(+), 5 deletions(-)

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 374ad4a..eed2204 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1666,6 +1666,7 @@ void usb_interface_init(struct usb_interface *intf, struct usb_device *dev, stru
 	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);
@@ -1712,7 +1713,7 @@ int usb_interface_register(struct usb_device *dev, struct usb_interface *intf, i
 		dev_name(&intf->dev), confNr,
 		intf->cur_altsetting->desc.bInterfaceNumber);
 	device_enable_async_suspend(&intf->dev);
-	ret = device_add(&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);
@@ -1879,9 +1880,16 @@ free_interfaces:
 	for (i = 0; i < nintf; ++i) {
 		struct usb_interface *intf = new_interfaces[i];
 
+		// update device's mask at configuration change
+		if(hcd->interface_authorized_default)
+			dev->mask |= (1 << i);
+		else
+			dev->mask &= ~(1 << i);
+
 		usb_interface_init(intf, dev, cp, configuration, i);
 
-		usb_enable_interface(dev, intf, true);
+		if (intf->authorized)
+			usb_enable_interface(dev, intf, true);
 	}
 	kfree(new_interfaces);
 
@@ -1935,6 +1943,7 @@ free_interfaces:
 	}
 
 	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/include/linux/usb.h b/include/linux/usb.h
index a92600a..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;
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 */
-- 
2.1.4




--
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