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