Skip to content

Commit f67cf49

Browse files
westerigregkh
authored andcommitted
thunderbolt: Add support for Internal Connection Manager (ICM)
Starting from Intel Falcon Ridge the internal connection manager running on the Thunderbolt host controller has been supporting 4 security levels. One reason for this is to prevent DMA attacks and only allow connecting devices the user trusts. The internal connection manager (ICM) is the preferred way of connecting Thunderbolt devices over software only implementation typically used on Macs. The driver communicates with ICM using special Thunderbolt ring 0 (control channel) messages. In order to handle these messages we add support for the ICM messages to the control channel. The security levels are as follows: none - No security, all tunnels are created automatically user - User needs to approve the device before tunnels are created secure - User need to approve the device before tunnels are created. The device is sent a challenge on future connects to be able to verify it is actually the approved device. dponly - Only Display Port and USB tunnels can be created and those are created automatically. The security levels are typically configurable from the system BIOS and by default it is set to "user" on many systems. In this patch each Thunderbolt device will have either one or two new sysfs attributes: authorized and key. The latter appears for devices that support secure connect. In order to identify the device the user can read identication information, including UUID and name of the device from sysfs and based on that make a decision to authorize the device. The device is authorized by simply writing 1 to the "authorized" sysfs attribute. This is following the USB bus device authorization mechanism. The secure connect requires an additional challenge step (writing 2 to the "authorized" attribute) in future connects when the key has already been stored to the NVM of the device. Non-ICM systems (before Alpine Ridge) continue to use the existing functionality and the security level is set to none. For systems with Alpine Ridge, even on Apple hardware, we will use ICM. This code is based on the work done by Amir Levy and Michael Jamet. Signed-off-by: Michael Jamet <[email protected]> Signed-off-by: Mika Westerberg <[email protected]> Reviewed-by: Yehezkel Bernat <[email protected]> Reviewed-by: Andy Shevchenko <[email protected]> Signed-off-by: Andreas Noever <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent bdccf29 commit f67cf49

File tree

12 files changed

+1805
-12
lines changed

12 files changed

+1805
-12
lines changed

Documentation/ABI/testing/sysfs-bus-thunderbolt

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,51 @@
1+
What: /sys/bus/thunderbolt/devices/.../domainX/security
2+
Date: Sep 2017
3+
KernelVersion: 4.13
4+
5+
Description: This attribute holds current Thunderbolt security level
6+
set by the system BIOS. Possible values are:
7+
8+
none: All devices are automatically authorized
9+
user: Devices are only authorized based on writing
10+
appropriate value to the authorized attribute
11+
secure: Require devices that support secure connect at
12+
minimum. User needs to authorize each device.
13+
dponly: Automatically tunnel Display port (and USB). No
14+
PCIe tunnels are created.
15+
16+
What: /sys/bus/thunderbolt/devices/.../authorized
17+
Date: Sep 2017
18+
KernelVersion: 4.13
19+
20+
Description: This attribute is used to authorize Thunderbolt devices
21+
after they have been connected. If the device is not
22+
authorized, no devices such as PCIe and Display port are
23+
available to the system.
24+
25+
Contents of this attribute will be 0 when the device is not
26+
yet authorized.
27+
28+
Possible values are supported:
29+
1: The device will be authorized and connected
30+
31+
When key attribute contains 32 byte hex string the possible
32+
values are:
33+
1: The 32 byte hex string is added to the device NVM and
34+
the device is authorized.
35+
2: Send a challenge based on the 32 byte hex string. If the
36+
challenge response from device is valid, the device is
37+
authorized. In case of failure errno will be ENOKEY if
38+
the device did not contain a key at all, and
39+
EKEYREJECTED if the challenge response did not match.
40+
41+
What: /sys/bus/thunderbolt/devices/.../key
42+
Date: Sep 2017
43+
KernelVersion: 4.13
44+
45+
Description: When a devices supports Thunderbolt secure connect it will
46+
have this attribute. Writing 32 byte hex string changes
47+
authorization to use the secure connection method instead.
48+
149
What: /sys/bus/thunderbolt/devices/.../device
250
Date: Sep 2017
351
KernelVersion: 4.13

drivers/thunderbolt/Kconfig

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
menuconfig THUNDERBOLT
2-
tristate "Thunderbolt support for Apple devices"
2+
tristate "Thunderbolt support"
33
depends on PCI
44
depends on X86 || COMPILE_TEST
55
select APPLE_PROPERTIES if EFI_STUB && X86
66
select CRC32
7+
select CRYPTO
8+
select CRYPTO_HASH
79
help
8-
Cactus Ridge Thunderbolt Controller driver
9-
This driver is required if you want to hotplug Thunderbolt devices on
10-
Apple hardware.
11-
12-
Device chaining is currently not supported.
10+
Thunderbolt Controller driver. This driver is required if you
11+
want to hotplug Thunderbolt devices on Apple hardware or on PCs
12+
with Intel Falcon Ridge or newer.
1313

1414
To compile this driver a module, choose M here. The module will be
1515
called thunderbolt.

drivers/thunderbolt/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
obj-${CONFIG_THUNDERBOLT} := thunderbolt.o
22
thunderbolt-objs := nhi.o ctl.o tb.o switch.o cap.o path.o tunnel_pci.o eeprom.o
3-
thunderbolt-objs += domain.o dma_port.o
3+
thunderbolt-objs += domain.o dma_port.o icm.o

drivers/thunderbolt/ctl.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,8 @@ static void tb_ctl_rx_callback(struct tb_ring *ring, struct ring_frame *frame,
463463
"RX: checksum mismatch, dropping packet\n");
464464
goto rx;
465465
}
466+
/* Fall through */
467+
case TB_CFG_PKG_ICM_EVENT:
466468
tb_ctl_handle_event(pkg->ctl, frame->eof, pkg, frame->size);
467469
goto rx;
468470

drivers/thunderbolt/domain.c

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,43 @@
1313
#include <linux/idr.h>
1414
#include <linux/module.h>
1515
#include <linux/slab.h>
16+
#include <linux/random.h>
17+
#include <crypto/hash.h>
1618

1719
#include "tb.h"
1820

1921
static DEFINE_IDA(tb_domain_ida);
2022

23+
static const char * const tb_security_names[] = {
24+
[TB_SECURITY_NONE] = "none",
25+
[TB_SECURITY_USER] = "user",
26+
[TB_SECURITY_SECURE] = "secure",
27+
[TB_SECURITY_DPONLY] = "dponly",
28+
};
29+
30+
static ssize_t security_show(struct device *dev, struct device_attribute *attr,
31+
char *buf)
32+
{
33+
struct tb *tb = container_of(dev, struct tb, dev);
34+
35+
return sprintf(buf, "%s\n", tb_security_names[tb->security_level]);
36+
}
37+
static DEVICE_ATTR_RO(security);
38+
39+
static struct attribute *domain_attrs[] = {
40+
&dev_attr_security.attr,
41+
NULL,
42+
};
43+
44+
static struct attribute_group domain_attr_group = {
45+
.attrs = domain_attrs,
46+
};
47+
48+
static const struct attribute_group *domain_attr_groups[] = {
49+
&domain_attr_group,
50+
NULL,
51+
};
52+
2153
struct bus_type tb_bus_type = {
2254
.name = "thunderbolt",
2355
};
@@ -82,6 +114,7 @@ struct tb *tb_domain_alloc(struct tb_nhi *nhi, size_t privsize)
82114
tb->dev.parent = &nhi->pdev->dev;
83115
tb->dev.bus = &tb_bus_type;
84116
tb->dev.type = &tb_domain_type;
117+
tb->dev.groups = domain_attr_groups;
85118
dev_set_name(&tb->dev, "domain%d", tb->index);
86119
device_initialize(&tb->dev);
87120

@@ -140,6 +173,12 @@ int tb_domain_add(struct tb *tb)
140173
*/
141174
tb_ctl_start(tb->ctl);
142175

176+
if (tb->cm_ops->driver_ready) {
177+
ret = tb->cm_ops->driver_ready(tb);
178+
if (ret)
179+
goto err_ctl_stop;
180+
}
181+
143182
ret = device_add(&tb->dev);
144183
if (ret)
145184
goto err_ctl_stop;
@@ -231,6 +270,162 @@ int tb_domain_resume_noirq(struct tb *tb)
231270
return ret;
232271
}
233272

273+
int tb_domain_suspend(struct tb *tb)
274+
{
275+
int ret;
276+
277+
mutex_lock(&tb->lock);
278+
if (tb->cm_ops->suspend) {
279+
ret = tb->cm_ops->suspend(tb);
280+
if (ret) {
281+
mutex_unlock(&tb->lock);
282+
return ret;
283+
}
284+
}
285+
mutex_unlock(&tb->lock);
286+
return 0;
287+
}
288+
289+
void tb_domain_complete(struct tb *tb)
290+
{
291+
mutex_lock(&tb->lock);
292+
if (tb->cm_ops->complete)
293+
tb->cm_ops->complete(tb);
294+
mutex_unlock(&tb->lock);
295+
}
296+
297+
/**
298+
* tb_domain_approve_switch() - Approve switch
299+
* @tb: Domain the switch belongs to
300+
* @sw: Switch to approve
301+
*
302+
* This will approve switch by connection manager specific means. In
303+
* case of success the connection manager will create tunnels for all
304+
* supported protocols.
305+
*/
306+
int tb_domain_approve_switch(struct tb *tb, struct tb_switch *sw)
307+
{
308+
struct tb_switch *parent_sw;
309+
310+
if (!tb->cm_ops->approve_switch)
311+
return -EPERM;
312+
313+
/* The parent switch must be authorized before this one */
314+
parent_sw = tb_to_switch(sw->dev.parent);
315+
if (!parent_sw || !parent_sw->authorized)
316+
return -EINVAL;
317+
318+
return tb->cm_ops->approve_switch(tb, sw);
319+
}
320+
321+
/**
322+
* tb_domain_approve_switch_key() - Approve switch and add key
323+
* @tb: Domain the switch belongs to
324+
* @sw: Switch to approve
325+
*
326+
* For switches that support secure connect, this function first adds
327+
* key to the switch NVM using connection manager specific means. If
328+
* adding the key is successful, the switch is approved and connected.
329+
*
330+
* Return: %0 on success and negative errno in case of failure.
331+
*/
332+
int tb_domain_approve_switch_key(struct tb *tb, struct tb_switch *sw)
333+
{
334+
struct tb_switch *parent_sw;
335+
int ret;
336+
337+
if (!tb->cm_ops->approve_switch || !tb->cm_ops->add_switch_key)
338+
return -EPERM;
339+
340+
/* The parent switch must be authorized before this one */
341+
parent_sw = tb_to_switch(sw->dev.parent);
342+
if (!parent_sw || !parent_sw->authorized)
343+
return -EINVAL;
344+
345+
ret = tb->cm_ops->add_switch_key(tb, sw);
346+
if (ret)
347+
return ret;
348+
349+
return tb->cm_ops->approve_switch(tb, sw);
350+
}
351+
352+
/**
353+
* tb_domain_challenge_switch_key() - Challenge and approve switch
354+
* @tb: Domain the switch belongs to
355+
* @sw: Switch to approve
356+
*
357+
* For switches that support secure connect, this function generates
358+
* random challenge and sends it to the switch. The switch responds to
359+
* this and if the response matches our random challenge, the switch is
360+
* approved and connected.
361+
*
362+
* Return: %0 on success and negative errno in case of failure.
363+
*/
364+
int tb_domain_challenge_switch_key(struct tb *tb, struct tb_switch *sw)
365+
{
366+
u8 challenge[TB_SWITCH_KEY_SIZE];
367+
u8 response[TB_SWITCH_KEY_SIZE];
368+
u8 hmac[TB_SWITCH_KEY_SIZE];
369+
struct tb_switch *parent_sw;
370+
struct crypto_shash *tfm;
371+
struct shash_desc *shash;
372+
int ret;
373+
374+
if (!tb->cm_ops->approve_switch || !tb->cm_ops->challenge_switch_key)
375+
return -EPERM;
376+
377+
/* The parent switch must be authorized before this one */
378+
parent_sw = tb_to_switch(sw->dev.parent);
379+
if (!parent_sw || !parent_sw->authorized)
380+
return -EINVAL;
381+
382+
get_random_bytes(challenge, sizeof(challenge));
383+
ret = tb->cm_ops->challenge_switch_key(tb, sw, challenge, response);
384+
if (ret)
385+
return ret;
386+
387+
tfm = crypto_alloc_shash("hmac(sha256)", 0, 0);
388+
if (IS_ERR(tfm))
389+
return PTR_ERR(tfm);
390+
391+
ret = crypto_shash_setkey(tfm, sw->key, TB_SWITCH_KEY_SIZE);
392+
if (ret)
393+
goto err_free_tfm;
394+
395+
shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(tfm),
396+
GFP_KERNEL);
397+
if (!shash) {
398+
ret = -ENOMEM;
399+
goto err_free_tfm;
400+
}
401+
402+
shash->tfm = tfm;
403+
shash->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
404+
405+
memset(hmac, 0, sizeof(hmac));
406+
ret = crypto_shash_digest(shash, challenge, sizeof(hmac), hmac);
407+
if (ret)
408+
goto err_free_shash;
409+
410+
/* The returned HMAC must match the one we calculated */
411+
if (memcmp(response, hmac, sizeof(hmac))) {
412+
ret = -EKEYREJECTED;
413+
goto err_free_shash;
414+
}
415+
416+
crypto_free_shash(tfm);
417+
kfree(shash);
418+
419+
return tb->cm_ops->approve_switch(tb, sw);
420+
421+
err_free_shash:
422+
kfree(shash);
423+
err_free_tfm:
424+
crypto_free_shash(tfm);
425+
426+
return ret;
427+
}
428+
234429
int tb_domain_init(void)
235430
{
236431
return bus_register(&tb_bus_type);

0 commit comments

Comments
 (0)