The PCI core has just been amended to authenticate CMA-capable devices on enumeration and store the result in an "authenticated" bit in struct pci_dev->spdm_state. Expose the bit to user space through an eponymous sysfs attribute. Allow user space to trigger reauthentication (e.g. after it has updated the CMA keyring) by writing to the sysfs attribute. Subject to further discussion, a future commit might add a user-defined policy to forbid driver binding to devices which failed authentication, similar to the "authorized" attribute for USB. Alternatively, authentication success might be signaled to user space through a uevent, whereupon it may bind a (blacklisted) driver. A uevent signaling authentication failure might similarly cause user space to unbind or outright remove the potentially malicious device. Traffic from devices which failed authentication could also be filtered through ACS I/O Request Blocking Enable (PCIe r6.1 sec 7.7.11.3) or through Link Disable (PCIe r6.1 sec 7.5.3.7). Unlike an IOMMU, that will not only protect the host, but also prevent malicious peer-to-peer traffic to other devices. Signed-off-by: Lukas Wunner <lukas@xxxxxxxxx> --- Documentation/ABI/testing/sysfs-bus-pci | 27 +++++++++ drivers/pci/Kconfig | 3 + drivers/pci/Makefile | 1 + drivers/pci/cma-sysfs.c | 73 +++++++++++++++++++++++++ drivers/pci/cma.c | 2 + drivers/pci/doe.c | 2 + drivers/pci/pci-sysfs.c | 3 + drivers/pci/pci.h | 1 + include/linux/pci.h | 2 + 9 files changed, 114 insertions(+) create mode 100644 drivers/pci/cma-sysfs.c diff --git a/Documentation/ABI/testing/sysfs-bus-pci b/Documentation/ABI/testing/sysfs-bus-pci index ecf47559f495..2ea9b8deffcc 100644 --- a/Documentation/ABI/testing/sysfs-bus-pci +++ b/Documentation/ABI/testing/sysfs-bus-pci @@ -500,3 +500,30 @@ Description: console drivers from the device. Raw users of pci-sysfs resourceN attributes must be terminated prior to resizing. Success of the resizing operation is not guaranteed. + +What: /sys/bus/pci/devices/.../authenticated +Date: September 2023 +Contact: Lukas Wunner <lukas@xxxxxxxxx> +Description: + This file contains 1 if the device authenticated successfully + with CMA-SPDM (PCIe r6.1 sec 6.31). It contains 0 if the + device failed authentication (and may thus be malicious). + + Writing anything to this file causes reauthentication. + That may be opportune after updating the .cma keyring. + + The file is not visible if authentication is unsupported + by the device. + + If the kernel could not determine whether authentication is + supported because memory was low or DOE communication with + the device was not working, the file is visible but accessing + it fails with error code ENOTTY. + + This prevents downgrade attacks where an attacker consumes + memory or disturbs DOE communication in order to create the + appearance that a device does not support authentication. + + The reason why authentication support could not be determined + is apparent from "dmesg". To probe for authentication support + again, exercise the "remove" and "rescan" attributes. diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index c9aa5253ac1f..51df3be3438e 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -129,6 +129,9 @@ config PCI_CMA A PCI DOE mailbox is used as transport for DMTF SPDM based attestation, measurement and secure channel establishment. +config PCI_CMA_SYSFS + def_bool PCI_CMA && SYSFS + config PCI_DOE bool diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index a18812b8832b..612ae724cd2d 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -35,6 +35,7 @@ obj-$(CONFIG_PCI_DOE) += doe.o obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o obj-$(CONFIG_PCI_CMA) += cma.o cma-x509.o cma.asn1.o +obj-$(CONFIG_PCI_CMA_SYSFS) += cma-sysfs.o $(obj)/cma-x509.o: $(obj)/cma.asn1.h $(obj)/cma.asn1.o: $(obj)/cma.asn1.c $(obj)/cma.asn1.h diff --git a/drivers/pci/cma-sysfs.c b/drivers/pci/cma-sysfs.c new file mode 100644 index 000000000000..b2d45f96601a --- /dev/null +++ b/drivers/pci/cma-sysfs.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Component Measurement and Authentication (CMA-SPDM, PCIe r6.1 sec 6.31) + * + * Copyright (C) 2023 Intel Corporation + */ + +#include <linux/pci.h> +#include <linux/spdm.h> +#include <linux/sysfs.h> + +#include "pci.h" + +static ssize_t authenticated_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pci_dev *pdev = to_pci_dev(dev); + ssize_t rc; + + if (!pdev->cma_capable && + (pdev->cma_init_failed || pdev->doe_init_failed)) + return -ENOTTY; + + rc = pci_cma_reauthenticate(pdev); + if (rc) + return rc; + + return count; +} + +static ssize_t authenticated_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + if (!pdev->cma_capable && + (pdev->cma_init_failed || pdev->doe_init_failed)) + return -ENOTTY; + + return sysfs_emit(buf, "%u\n", spdm_authenticated(pdev->spdm_state)); +} +static DEVICE_ATTR_RW(authenticated); + +static struct attribute *pci_cma_attrs[] = { + &dev_attr_authenticated.attr, + NULL +}; + +static umode_t pci_cma_attrs_are_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct pci_dev *pdev = to_pci_dev(dev); + + /* + * If CMA or DOE initialization failed, CMA attributes must be visible + * and return an error on access. This prevents downgrade attacks + * where an attacker disturbs memory allocation or DOE communication + * in order to create the appearance that CMA is unsupported. + * The attacker may achieve that by simply hogging memory. + */ + if (!pdev->cma_capable && + !pdev->cma_init_failed && !pdev->doe_init_failed) + return 0; + + return a->mode; +} + +const struct attribute_group pci_cma_attr_group = { + .attrs = pci_cma_attrs, + .is_visible = pci_cma_attrs_are_visible, +}; diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c index 89d23fdc37ec..c539ad85a28f 100644 --- a/drivers/pci/cma.c +++ b/drivers/pci/cma.c @@ -52,6 +52,7 @@ void pci_cma_init(struct pci_dev *pdev) int rc; if (!pci_cma_keyring) { + pdev->cma_init_failed = true; return; } @@ -67,6 +68,7 @@ void pci_cma_init(struct pci_dev *pdev) PCI_DOE_MAX_PAYLOAD, pci_cma_keyring, pci_cma_validate); if (!pdev->spdm_state) { + pdev->cma_init_failed = true; return; } diff --git a/drivers/pci/doe.c b/drivers/pci/doe.c index 79f0336eb0c3..fabbda68edac 100644 --- a/drivers/pci/doe.c +++ b/drivers/pci/doe.c @@ -686,6 +686,7 @@ void pci_doe_init(struct pci_dev *pdev) PCI_EXT_CAP_ID_DOE))) { doe_mb = pci_doe_create_mb(pdev, offset); if (IS_ERR(doe_mb)) { + pdev->doe_init_failed = true; pci_err(pdev, "[%x] failed to create mailbox: %ld\n", offset, PTR_ERR(doe_mb)); continue; @@ -693,6 +694,7 @@ void pci_doe_init(struct pci_dev *pdev) rc = xa_insert(&pdev->doe_mbs, offset, doe_mb, GFP_KERNEL); if (rc) { + pdev->doe_init_failed = true; pci_err(pdev, "[%x] failed to insert mailbox: %d\n", offset, rc); pci_doe_destroy_mb(doe_mb); diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index d9eede2dbc0e..7024e08e1b9a 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1655,6 +1655,9 @@ static const struct attribute_group *pci_dev_attr_groups[] = { #endif #ifdef CONFIG_PCIEASPM &aspm_ctrl_attr_group, +#endif +#ifdef CONFIG_PCI_CMA_SYSFS + &pci_cma_attr_group, #endif NULL, }; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 71092ccf4fbd..d80cc06be0cc 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -328,6 +328,7 @@ void pci_cma_destroy(struct pci_dev *pdev); int pci_cma_reauthenticate(struct pci_dev *pdev); struct x509_certificate; int pci_cma_validate(struct device *dev, struct x509_certificate *leaf_cert); +extern const struct attribute_group pci_cma_attr_group; #else static inline void pci_cma_init(struct pci_dev *pdev) { } static inline void pci_cma_destroy(struct pci_dev *pdev) { } diff --git a/include/linux/pci.h b/include/linux/pci.h index 2bc11d8b567e..2c5fde81bb85 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -516,10 +516,12 @@ struct pci_dev { #endif #ifdef CONFIG_PCI_DOE struct xarray doe_mbs; /* Data Object Exchange mailboxes */ + unsigned int doe_init_failed:1; #endif #ifdef CONFIG_PCI_CMA struct spdm_state *spdm_state; /* Security Protocol and Data Model */ unsigned int cma_capable:1; /* Authentication supported */ + unsigned int cma_init_failed:1; #endif u16 acs_cap; /* ACS Capability offset */ phys_addr_t rom; /* Physical address if not from BAR */ -- 2.40.1