Re: [PATCH 3/4] Add usb interface authorization

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

 



Am 08.06.2015 um 16:40 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
This third patch implements the interface authorization. It's the main case of implementation.
With a bitmask interfaces could be allowed or denied individually.
This patch depends on patch 1 and patch 2.

---------------------------
>From e337048febfc09d339be7055bf15fc06c20d9e2c Mon Sep 17 00:00:00 2001
From: Stefan Koch <skoch@xxxxxxx>
Date: Mon, 8 Jun 2015 23:28:09 +0200
Subject: [PATCH 3/4] This patch introduces the usb interface authorization

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