Commit 3da88be2 authored by Mika Westerberg's avatar Mika Westerberg

thunderbolt: Add support for de-authorizing devices

In some cases it is useful to be able de-authorize devices. For example
if user logs out the userspace can have a policy that disconnects PCIe
devices until logged in again. This is only possible for software based
connection manager as it directly controls the tunnels.

For this reason make the authorized attribute accept writing 0 which
makes the software connection manager to tear down the corresponding
PCIe tunnel. Userspace can check if this is supported by reading a new
domain attribute deauthorization, that holds 1 in that case.

While there correct tb_domain_approve_switch() kernel-doc and
description of authorized attribute to mention that it is only about
PCIe tunnels.

Cc: Christian Kellner <christian@kellner.me>
Signed-off-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Acked-by: default avatarYehezkel Bernat <YehezkelShB@gmail.com>
parent fdb0887c
...@@ -49,6 +49,15 @@ Description: Holds a comma separated list of device unique_ids that ...@@ -49,6 +49,15 @@ Description: Holds a comma separated list of device unique_ids that
If a device is authorized automatically during boot its If a device is authorized automatically during boot its
boot attribute is set to 1. boot attribute is set to 1.
What: /sys/bus/thunderbolt/devices/.../domainX/deauthorization
Date: May 2021
KernelVersion: 5.12
Contact: Mika Westerberg <mika.westerberg@linux.intel.com>
Description: This attribute tells whether the system supports
de-authorization of devices. Value of 1 means user can
de-authorize PCIe tunnel by writing 0 to authorized
attribute under each device.
What: /sys/bus/thunderbolt/devices/.../domainX/iommu_dma_protection What: /sys/bus/thunderbolt/devices/.../domainX/iommu_dma_protection
Date: Mar 2019 Date: Mar 2019
KernelVersion: 4.21 KernelVersion: 4.21
...@@ -84,22 +93,25 @@ KernelVersion: 4.13 ...@@ -84,22 +93,25 @@ KernelVersion: 4.13
Contact: thunderbolt-software@lists.01.org Contact: thunderbolt-software@lists.01.org
Description: This attribute is used to authorize Thunderbolt devices Description: This attribute is used to authorize Thunderbolt devices
after they have been connected. If the device is not after they have been connected. If the device is not
authorized, no devices such as PCIe and Display port are authorized, no PCIe devices are available to the system.
available to the system.
Contents of this attribute will be 0 when the device is not Contents of this attribute will be 0 when the device is not
yet authorized. yet authorized.
Possible values are supported: Possible values are supported:
== =========================================== == ===================================================
0 The device will be de-authorized (only supported if
deauthorization attribute under domain contains 1)
1 The device will be authorized and connected 1 The device will be authorized and connected
== =========================================== == ===================================================
When key attribute contains 32 byte hex string the possible When key attribute contains 32 byte hex string the possible
values are: values are:
== ======================================================== == ========================================================
0 The device will be de-authorized (only supported if
deauthorization attribute under domain contains 1)
1 The 32 byte hex string is added to the device NVM and 1 The 32 byte hex string is added to the device NVM and
the device is authorized. the device is authorized.
2 Send a challenge based on the 32 byte hex string. If the 2 Send a challenge based on the 32 byte hex string. If the
......
...@@ -153,6 +153,22 @@ If the user still wants to connect the device they can either approve ...@@ -153,6 +153,22 @@ If the user still wants to connect the device they can either approve
the device without a key or write a new key and write 1 to the the device without a key or write a new key and write 1 to the
``authorized`` file to get the new key stored on the device NVM. ``authorized`` file to get the new key stored on the device NVM.
De-authorizing devices
----------------------
It is possible to de-authorize devices by writing ``0`` to their
``authorized`` attribute. This requires support from the connection
manager implementation and can be checked by reading domain
``deauthorization`` attribute. If it reads ``1`` then the feature is
supported.
When a device is de-authorized the PCIe tunnel from the parent device
PCIe downstream (or root) port to the device PCIe upstream port is torn
down. This is essentially the same thing as PCIe hot-remove and the PCIe
toplogy in question will not be accessible anymore until the device is
authorized again. If there is storage such as NVMe or similar involved,
there is a risk for data loss if the filesystem on that storage is not
properly shut down. You have been warned!
DMA protection utilizing IOMMU DMA protection utilizing IOMMU
------------------------------ ------------------------------
Recent systems from 2018 and forward with Thunderbolt ports may natively Recent systems from 2018 and forward with Thunderbolt ports may natively
......
...@@ -238,6 +238,16 @@ static ssize_t boot_acl_store(struct device *dev, struct device_attribute *attr, ...@@ -238,6 +238,16 @@ static ssize_t boot_acl_store(struct device *dev, struct device_attribute *attr,
} }
static DEVICE_ATTR_RW(boot_acl); static DEVICE_ATTR_RW(boot_acl);
static ssize_t deauthorization_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
const struct tb *tb = container_of(dev, struct tb, dev);
return sprintf(buf, "%d\n", !!tb->cm_ops->disapprove_switch);
}
static DEVICE_ATTR_RO(deauthorization);
static ssize_t iommu_dma_protection_show(struct device *dev, static ssize_t iommu_dma_protection_show(struct device *dev,
struct device_attribute *attr, struct device_attribute *attr,
char *buf) char *buf)
...@@ -267,6 +277,7 @@ static DEVICE_ATTR_RO(security); ...@@ -267,6 +277,7 @@ static DEVICE_ATTR_RO(security);
static struct attribute *domain_attrs[] = { static struct attribute *domain_attrs[] = {
&dev_attr_boot_acl.attr, &dev_attr_boot_acl.attr,
&dev_attr_deauthorization.attr,
&dev_attr_iommu_dma_protection.attr, &dev_attr_iommu_dma_protection.attr,
&dev_attr_security.attr, &dev_attr_security.attr,
NULL, NULL,
...@@ -601,14 +612,31 @@ int tb_domain_runtime_resume(struct tb *tb) ...@@ -601,14 +612,31 @@ int tb_domain_runtime_resume(struct tb *tb)
return 0; return 0;
} }
/**
* tb_domain_disapprove_switch() - Disapprove switch
* @tb: Domain the switch belongs to
* @sw: Switch to disapprove
*
* This will disconnect PCIe tunnel from parent to this @sw.
*
* Return: %0 on success and negative errno in case of failure.
*/
int tb_domain_disapprove_switch(struct tb *tb, struct tb_switch *sw)
{
if (!tb->cm_ops->disapprove_switch)
return -EPERM;
return tb->cm_ops->disapprove_switch(tb, sw);
}
/** /**
* tb_domain_approve_switch() - Approve switch * tb_domain_approve_switch() - Approve switch
* @tb: Domain the switch belongs to * @tb: Domain the switch belongs to
* @sw: Switch to approve * @sw: Switch to approve
* *
* This will approve switch by connection manager specific means. In * This will approve switch by connection manager specific means. In
* case of success the connection manager will create tunnels for all * case of success the connection manager will create PCIe tunnel from
* supported protocols. * parent to @sw.
*/ */
int tb_domain_approve_switch(struct tb *tb, struct tb_switch *sw) int tb_domain_approve_switch(struct tb *tb, struct tb_switch *sw)
{ {
......
...@@ -1387,6 +1387,30 @@ static ssize_t authorized_show(struct device *dev, ...@@ -1387,6 +1387,30 @@ static ssize_t authorized_show(struct device *dev,
return sprintf(buf, "%u\n", sw->authorized); return sprintf(buf, "%u\n", sw->authorized);
} }
static int disapprove_switch(struct device *dev, void *not_used)
{
struct tb_switch *sw;
sw = tb_to_switch(dev);
if (sw && sw->authorized) {
int ret;
/* First children */
ret = device_for_each_child_reverse(&sw->dev, NULL, disapprove_switch);
if (ret)
return ret;
ret = tb_domain_disapprove_switch(sw->tb, sw);
if (ret)
return ret;
sw->authorized = 0;
kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE);
}
return 0;
}
static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val) static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
{ {
int ret = -EINVAL; int ret = -EINVAL;
...@@ -1394,10 +1418,18 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val) ...@@ -1394,10 +1418,18 @@ static int tb_switch_set_authorized(struct tb_switch *sw, unsigned int val)
if (!mutex_trylock(&sw->tb->lock)) if (!mutex_trylock(&sw->tb->lock))
return restart_syscall(); return restart_syscall();
if (sw->authorized) if (!!sw->authorized == !!val)
goto unlock; goto unlock;
switch (val) { switch (val) {
/* Disapprove switch */
case 0:
if (tb_route(sw)) {
ret = disapprove_switch(&sw->dev, NULL);
goto unlock;
}
break;
/* Approve switch */ /* Approve switch */
case 1: case 1:
if (sw->key) if (sw->key)
......
...@@ -1002,6 +1002,25 @@ static void tb_disconnect_and_release_dp(struct tb *tb) ...@@ -1002,6 +1002,25 @@ static void tb_disconnect_and_release_dp(struct tb *tb)
} }
} }
static int tb_disconnect_pci(struct tb *tb, struct tb_switch *sw)
{
struct tb_tunnel *tunnel;
struct tb_port *up;
up = tb_switch_find_port(sw, TB_TYPE_PCIE_UP);
if (WARN_ON(!up))
return -ENODEV;
tunnel = tb_find_tunnel(tb, TB_TUNNEL_PCI, NULL, up);
if (WARN_ON(!tunnel))
return -ENODEV;
tb_tunnel_deactivate(tunnel);
list_del(&tunnel->list);
tb_tunnel_free(tunnel);
return 0;
}
static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw) static int tb_tunnel_pci(struct tb *tb, struct tb_switch *sw)
{ {
struct tb_port *up, *down, *port; struct tb_port *up, *down, *port;
...@@ -1512,6 +1531,7 @@ static const struct tb_cm_ops tb_cm_ops = { ...@@ -1512,6 +1531,7 @@ static const struct tb_cm_ops tb_cm_ops = {
.runtime_suspend = tb_runtime_suspend, .runtime_suspend = tb_runtime_suspend,
.runtime_resume = tb_runtime_resume, .runtime_resume = tb_runtime_resume,
.handle_event = tb_handle_event, .handle_event = tb_handle_event,
.disapprove_switch = tb_disconnect_pci,
.approve_switch = tb_tunnel_pci, .approve_switch = tb_tunnel_pci,
.approve_xdomain_paths = tb_approve_xdomain_paths, .approve_xdomain_paths = tb_approve_xdomain_paths,
.disconnect_xdomain_paths = tb_disconnect_xdomain_paths, .disconnect_xdomain_paths = tb_disconnect_xdomain_paths,
......
...@@ -361,6 +361,7 @@ struct tb_path { ...@@ -361,6 +361,7 @@ struct tb_path {
* @handle_event: Handle thunderbolt event * @handle_event: Handle thunderbolt event
* @get_boot_acl: Get boot ACL list * @get_boot_acl: Get boot ACL list
* @set_boot_acl: Set boot ACL list * @set_boot_acl: Set boot ACL list
* @disapprove_switch: Disapprove switch (disconnect PCIe tunnel)
* @approve_switch: Approve switch * @approve_switch: Approve switch
* @add_switch_key: Add key to switch * @add_switch_key: Add key to switch
* @challenge_switch_key: Challenge switch using key * @challenge_switch_key: Challenge switch using key
...@@ -394,6 +395,7 @@ struct tb_cm_ops { ...@@ -394,6 +395,7 @@ struct tb_cm_ops {
const void *buf, size_t size); const void *buf, size_t size);
int (*get_boot_acl)(struct tb *tb, uuid_t *uuids, size_t nuuids); int (*get_boot_acl)(struct tb *tb, uuid_t *uuids, size_t nuuids);
int (*set_boot_acl)(struct tb *tb, const uuid_t *uuids, size_t nuuids); int (*set_boot_acl)(struct tb *tb, const uuid_t *uuids, size_t nuuids);
int (*disapprove_switch)(struct tb *tb, struct tb_switch *sw);
int (*approve_switch)(struct tb *tb, struct tb_switch *sw); int (*approve_switch)(struct tb *tb, struct tb_switch *sw);
int (*add_switch_key)(struct tb *tb, struct tb_switch *sw); int (*add_switch_key)(struct tb *tb, struct tb_switch *sw);
int (*challenge_switch_key)(struct tb *tb, struct tb_switch *sw, int (*challenge_switch_key)(struct tb *tb, struct tb_switch *sw,
...@@ -629,6 +631,7 @@ int tb_domain_thaw_noirq(struct tb *tb); ...@@ -629,6 +631,7 @@ int tb_domain_thaw_noirq(struct tb *tb);
void tb_domain_complete(struct tb *tb); void tb_domain_complete(struct tb *tb);
int tb_domain_runtime_suspend(struct tb *tb); int tb_domain_runtime_suspend(struct tb *tb);
int tb_domain_runtime_resume(struct tb *tb); int tb_domain_runtime_resume(struct tb *tb);
int tb_domain_disapprove_switch(struct tb *tb, struct tb_switch *sw);
int tb_domain_approve_switch(struct tb *tb, struct tb_switch *sw); int tb_domain_approve_switch(struct tb *tb, struct tb_switch *sw);
int tb_domain_approve_switch_key(struct tb *tb, struct tb_switch *sw); int tb_domain_approve_switch_key(struct tb *tb, struct tb_switch *sw);
int tb_domain_challenge_switch_key(struct tb *tb, struct tb_switch *sw); int tb_domain_challenge_switch_key(struct tb *tb, struct tb_switch *sw);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment