diff mbox series

[RFC,v3,3/4] PCI/CMA: Initial support for Component Measurement and Authentication ECN

Message ID 20220906111556.1544-4-Jonathan.Cameron@huawei.com
State New, archived
Headers show
Series PCI/CMA and SPDM Library - Device attestation etc. | expand

Commit Message

Jonathan Cameron Sept. 6, 2022, 11:15 a.m. UTC
This currently very much a PoC.  Currently the SPDM library only provides
a single function to allow a challenge / authentication of the PCI EP.

SPDM exchanges must occur in one of a small set of valid squences over
which the message digest used in authentication is built up.
Placing that complexity in the SPDM library seems like a good way
to enforce that logic, without having to do it for each transport.

Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
---
 drivers/pci/Kconfig     |  13 +++++
 drivers/pci/Makefile    |   1 +
 drivers/pci/cma.c       | 117 ++++++++++++++++++++++++++++++++++++++++
 include/linux/pci-cma.h |  21 ++++++++
 4 files changed, 152 insertions(+)

Comments

Bjorn Helgaas Sept. 23, 2022, 9:36 p.m. UTC | #1
On Tue, Sep 06, 2022 at 12:15:55PM +0100, Jonathan Cameron wrote:
> This currently very much a PoC.  Currently the SPDM library only provides
> a single function to allow a challenge / authentication of the PCI EP.
> 
> SPDM exchanges must occur in one of a small set of valid squences over
> which the message digest used in authentication is built up.
> Placing that complexity in the SPDM library seems like a good way
> to enforce that logic, without having to do it for each transport.
> 
> Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> ---
>  drivers/pci/Kconfig     |  13 +++++
>  drivers/pci/Makefile    |   1 +
>  drivers/pci/cma.c       | 117 ++++++++++++++++++++++++++++++++++++++++
>  include/linux/pci-cma.h |  21 ++++++++
>  4 files changed, 152 insertions(+)
> 
> diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
> index 55c028af4bd9..b25e97a1e771 100644
> --- a/drivers/pci/Kconfig
> +++ b/drivers/pci/Kconfig
> @@ -118,6 +118,19 @@ config XEN_PCIDEV_FRONTEND
>  	  The PCI device frontend driver allows the kernel to import arbitrary
>  	  PCI devices from a PCI backend to support PCI driver domains.
>  
> +config PCI_CMA
> +	tristate "PCI Component Measurement and Authentication"
> +	select PCI_DOE
> +	select ASN1_ENCODER
> +	select SPDM
> +	help
> +	  This enables library support for the PCI Component Measurement and
> +	  Authentication introduce in PCI r6.0 sec 6.31. A PCI DOE mailbox is
> +	  used as the transport for DMTF SPDM based attestation, measurement
> +	  and secure channel establishment.
> +
> +	  If built as a module will be called cma.ko.
> +
>  config PCI_ATS
>  	bool
>  
> diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
> index 2680e4c92f0a..d2e38b2baeae 100644
> --- a/drivers/pci/Makefile
> +++ b/drivers/pci/Makefile
> @@ -32,6 +32,7 @@ obj-$(CONFIG_PCI_P2PDMA)	+= p2pdma.o
>  obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
>  obj-$(CONFIG_VGA_ARB)		+= vgaarb.o
>  obj-$(CONFIG_PCI_DOE)		+= doe.o
> +obj-$(CONFIG_PCI_CMA)		+= cma.o
>  
>  # Endpoint library must be initialized before its users
>  obj-$(CONFIG_PCI_ENDPOINT)	+= endpoint/
> diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c
> new file mode 100644
> index 000000000000..b38b7a688266
> --- /dev/null
> +++ b/drivers/pci/cma.c
> @@ -0,0 +1,117 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Component Measurement and Authentication was added as an ECN to the
> + * PCIe r5.0 spec.

It looks like PCIe r6.0, sec 6.31?  (Oh, I see that's what you mention
above in the Kconfig text :))  I have absolutely no idea what CMA is
about or how it works.  Other than pci_doe_submit_task(), nothing here
is recognizable to me as PCI-related and I can't tell what else, if
anything, is connected to something in the PCIe spec.

> + * Copyright (C) 2021 Huawei
> + *     Jonathan Cameron <Jonathan.Cameron@huawei.com>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/pci-cma.h>
> +#include <linux/pci-doe.h>
> +#include <linux/spdm.h>
> +
> +#define PCI_DOE_PROTOCOL_CMA 1
> +/* Keyring that userspace can poke certs into */
> +static struct key *cma_keyring;
> +
> +static void cxl_doe_task_complete(struct pci_doe_task *task)
> +{
> +	complete(task->private);
> +}
> +
> +static int cma_spdm_ex(void *priv, struct spdm_exchange *spdm_ex)
> +{
> +	size_t request_padded_sz, response_padded_sz;
> +	struct completion c = COMPLETION_INITIALIZER_ONSTACK(c);
> +	struct pci_doe_task task = {
> +		.prot = {
> +			.vid = PCI_VENDOR_ID_PCI_SIG,
> +			.type = PCI_DOE_PROTOCOL_CMA,
> +		},
> +		.complete = cxl_doe_task_complete,
> +		.private = &c,
> +	};
> +	struct pci_doe_mb *doe = priv;
> +	int rc;
> +
> +	/* DOE requires that response and request are padded to a multiple of 4 bytes */

Wrap or shorten to fit in 80 columns.

> +	request_padded_sz = ALIGN(spdm_ex->request_sz, sizeof(u32));
> +	if (request_padded_sz != spdm_ex->request_sz) {
> +		task.request_pl = kzalloc(request_padded_sz, GFP_KERNEL);
> +		if (!task.request_pl)
> +			return -ENOMEM;
> +		memcpy(task.request_pl, spdm_ex->request, spdm_ex->request_sz);
> +		task.request_pl_sz = request_padded_sz;
> +	} else {
> +		task.request_pl = (u32 *)spdm_ex->request;
> +		task.request_pl_sz = spdm_ex->request_sz;
> +	}
> +
> +	response_padded_sz = ALIGN(spdm_ex->response_sz, sizeof(u32));
> +	if (response_padded_sz != spdm_ex->response_sz) {
> +		task.response_pl = kzalloc(response_padded_sz, GFP_KERNEL);
> +		if (!task.response_pl) {
> +			rc = -ENOMEM;
> +			goto err_free_req;
> +		}
> +		task.response_pl_sz = response_padded_sz;
> +	} else {
> +		task.response_pl = (u32 *)spdm_ex->response;
> +		task.response_pl_sz = spdm_ex->response_sz;
> +	}
> +
> +	rc = pci_doe_submit_task(doe, &task);
> +	if (rc < 0)
> +		goto err_free_rsp;
> +
> +	wait_for_completion(&c);
> +	if (response_padded_sz != spdm_ex->response_sz)
> +		memcpy(spdm_ex->response, task.response_pl, spdm_ex->response_sz);
> +
> +	rc = task.rv;
> +err_free_rsp:
> +	if (response_padded_sz != spdm_ex->response_sz)
> +		kfree(task.response_pl);
> +err_free_req:
> +	if (request_padded_sz != spdm_ex->request_sz)
> +		kfree(task.request_pl);
> +
> +	return rc;
> +}
> +
> +struct spdm_state *pci_cma_create(struct device *dev, struct pci_doe_mb *doe)
> +{
> +	return spdm_create(cma_spdm_ex, doe, dev, cma_keyring);
> +}
> +EXPORT_SYMBOL_GPL(pci_cma_create);
> +
> +void pci_cma_destroy(struct spdm_state *spdm_state)
> +{
> +	kfree(spdm_state);
> +}
> +EXPORT_SYMBOL_GPL(pci_cma_destroy);
> +
> +int pci_cma_authenticate(struct spdm_state *spdm_state)
> +{
> +	return spdm_authenticate(spdm_state);
> +}
> +EXPORT_SYMBOL_GPL(pci_cma_authenticate);
> +
> +__init static int cma_keyring_init(void)
> +{
> +	cma_keyring = keyring_alloc("_cma",
> +				    KUIDT_INIT(0), KGIDT_INIT(0),
> +				    current_cred(),
> +				    (KEY_POS_ALL & ~KEY_POS_SETATTR) |
> +				    KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH,
> +				    KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_SET_KEEP, NULL, NULL);
> +	if (IS_ERR(cma_keyring))
> +		pr_err("Could not allocate cma keyring\n");
> +
> +	return 0;
> +}
> +device_initcall(cma_keyring_init);
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/pci-cma.h b/include/linux/pci-cma.h
> new file mode 100644
> index 000000000000..d2a0a84973bf
> --- /dev/null
> +++ b/include/linux/pci-cma.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Component Measurement and Authentication was added as an ECN to the
> + * PCIe r5.0 spec.

Update as well?

> + * Copyright (C) 2021 Huawei
> + *     Jonathan Cameron <Jonathan.Cameron@huawei.com>
> + */
> +
> +#ifndef _PCI_CMA_H_
> +#define _PCI_CMA_H_
> +struct pci_doe_mb;
> +struct spdm_state;
> +struct device;
> +
> +struct spdm_state *pci_cma_create(struct device *dev, struct pci_doe_mb *doe);
> +void pci_cma_destroy(struct spdm_state *spdm_state);
> +
> +int pci_cma_authenticate(struct spdm_state *spdm_state);
> +
> +#endif
> -- 
> 2.32.0
>
Lukas Wunner Sept. 24, 2022, 5:39 a.m. UTC | #2
On Fri, Sep 23, 2022 at 04:36:34PM -0500, Bjorn Helgaas wrote:
> On Tue, Sep 06, 2022 at 12:15:55PM +0100, Jonathan Cameron wrote:
> > --- /dev/null
> > +++ b/drivers/pci/cma.c
> > @@ -0,0 +1,117 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Component Measurement and Authentication was added as an ECN to the
> > + * PCIe r5.0 spec.
> 
> It looks like PCIe r6.0, sec 6.31?  (Oh, I see that's what you mention
> above in the Kconfig text :))  I have absolutely no idea what CMA is
> about or how it works.  Other than pci_doe_submit_task(), nothing here
> is recognizable to me as PCI-related and I can't tell what else, if
> anything, is connected to something in the PCIe spec.

CMA is an adaption of the SPDM spec to PCIe.

Basically this is about authenticating PCI devices:
The device presents a certificate chain to the host;
The host needs to trust the root of that certificate chain;
The host sends a nonce to the device;
The device signs the nonce with its private key, sends it back;
The host verifies the signature matches the certificate (= public key).

The protocol to perform this authentication is called SPDM:
https://www.dmtf.org/sites/default/files/standards/documents/DSP0274_1.2.1.pdf

Various other specs besides PCIe have adopted SPDM (e.g. CXL).

One transport over which the SPDM message exchanges are sent is PCI DOE,
which appears in v6.0.

So-called measurements can be retrieved after authentication was
completed successfully:  E.g. a signed hash of the firmware.
Thereby, the host can verify the device is in a trusted state.

"Attestation" appears to be a fancy terminus technicus which encompasses
authentication and validation of measurements.

Authentication forms the basis for IDE (PCI TLP encryption,
PCIe r6.0 sec 6.33).  Encryption is useless without authentication
because it's otherwise susceptible to man-in-the-middle attacks.

Authentication also forms the basis for TDISP (Trusted I/O virtualization,
recently accepted as an ECN).

There was an SPDM BoF at Plumbers last week:
https://lpc.events/event/16/contributions/1304/attachments/1029/1974/LPC2022-SPDM-BoF-v4.pdf
https://lpc.events/event/16/abstracts/1301/

The outcome is that we'll be working towards a minimal CMA implementation
which is capable of authenticating PCI devices and presenting the result in
sysfs.  There might be a global policy knob in sysfs to control handling
of devices for which authentication failed (e.g. forbid binding to
drivers).  Features such as a per-device policy can later be added on top
if need be.  We'll need to rework DOE handling such that the PCI core
scans all DOE mailboxes on device enumeration to look for one capable
of SPDM and perform authentication.  We'll seek to upstream this though
the PCI tree.  That's my summary in brief, Jonathan or Dan may have
amendments or corrections to make. :)

Thanks,

Lukas
Dan Williams Sept. 24, 2022, 11:19 p.m. UTC | #3
Lukas Wunner wrote:
> On Fri, Sep 23, 2022 at 04:36:34PM -0500, Bjorn Helgaas wrote:
> > On Tue, Sep 06, 2022 at 12:15:55PM +0100, Jonathan Cameron wrote:
> > > --- /dev/null
> > > +++ b/drivers/pci/cma.c
> > > @@ -0,0 +1,117 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * Component Measurement and Authentication was added as an ECN to the
> > > + * PCIe r5.0 spec.
> > 
> > It looks like PCIe r6.0, sec 6.31?  (Oh, I see that's what you mention
> > above in the Kconfig text :))  I have absolutely no idea what CMA is
> > about or how it works.  Other than pci_doe_submit_task(), nothing here
> > is recognizable to me as PCI-related and I can't tell what else, if
> > anything, is connected to something in the PCIe spec.
> 
> CMA is an adaption of the SPDM spec to PCIe.
> 
> Basically this is about authenticating PCI devices:
> The device presents a certificate chain to the host;
> The host needs to trust the root of that certificate chain;
> The host sends a nonce to the device;
> The device signs the nonce with its private key, sends it back;
> The host verifies the signature matches the certificate (= public key).
> 
> The protocol to perform this authentication is called SPDM:
> https://www.dmtf.org/sites/default/files/standards/documents/DSP0274_1.2.1.pdf
> 
> Various other specs besides PCIe have adopted SPDM (e.g. CXL).
> 
> One transport over which the SPDM message exchanges are sent is PCI DOE,
> which appears in v6.0.
> 
> So-called measurements can be retrieved after authentication was
> completed successfully:  E.g. a signed hash of the firmware.
> Thereby, the host can verify the device is in a trusted state.
> 
> "Attestation" appears to be a fancy terminus technicus which encompasses
> authentication and validation of measurements.
> 
> Authentication forms the basis for IDE (PCI TLP encryption,
> PCIe r6.0 sec 6.33).  Encryption is useless without authentication
> because it's otherwise susceptible to man-in-the-middle attacks.

I would also add that attestation is useful without encryption. The
Thunderclap [1] class of vulnerabilities is concerned with malicious
devices impersonating legitimate ones. Where validating that the device
you think you are talking to also passes a challenge response with a
certificate chain you trust helps to mitigate that attack vector. IDE is
interesting because it is "persistent attestation". Whereas SPDM is a
point in time validation, IDE helps protect against swapping out the
device after the initial SPDM attestation. That event would be signaled
by error handling, making device swap out and interposer attacks more
difficult. I.e. you would need to silence the device swap out and any
upstream device that flags the error.

If Linux adds SPDM support for the value of attestation then I submit
IDE should be considered next.

[1]: https://thunderclap.io/
diff mbox series

Patch

diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 55c028af4bd9..b25e97a1e771 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -118,6 +118,19 @@  config XEN_PCIDEV_FRONTEND
 	  The PCI device frontend driver allows the kernel to import arbitrary
 	  PCI devices from a PCI backend to support PCI driver domains.
 
+config PCI_CMA
+	tristate "PCI Component Measurement and Authentication"
+	select PCI_DOE
+	select ASN1_ENCODER
+	select SPDM
+	help
+	  This enables library support for the PCI Component Measurement and
+	  Authentication introduce in PCI r6.0 sec 6.31. A PCI DOE mailbox is
+	  used as the transport for DMTF SPDM based attestation, measurement
+	  and secure channel establishment.
+
+	  If built as a module will be called cma.ko.
+
 config PCI_ATS
 	bool
 
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 2680e4c92f0a..d2e38b2baeae 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -32,6 +32,7 @@  obj-$(CONFIG_PCI_P2PDMA)	+= p2pdma.o
 obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
 obj-$(CONFIG_VGA_ARB)		+= vgaarb.o
 obj-$(CONFIG_PCI_DOE)		+= doe.o
+obj-$(CONFIG_PCI_CMA)		+= cma.o
 
 # Endpoint library must be initialized before its users
 obj-$(CONFIG_PCI_ENDPOINT)	+= endpoint/
diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c
new file mode 100644
index 000000000000..b38b7a688266
--- /dev/null
+++ b/drivers/pci/cma.c
@@ -0,0 +1,117 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Component Measurement and Authentication was added as an ECN to the
+ * PCIe r5.0 spec.
+ *
+ * Copyright (C) 2021 Huawei
+ *     Jonathan Cameron <Jonathan.Cameron@huawei.com>
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pci-cma.h>
+#include <linux/pci-doe.h>
+#include <linux/spdm.h>
+
+#define PCI_DOE_PROTOCOL_CMA 1
+/* Keyring that userspace can poke certs into */
+static struct key *cma_keyring;
+
+static void cxl_doe_task_complete(struct pci_doe_task *task)
+{
+	complete(task->private);
+}
+
+static int cma_spdm_ex(void *priv, struct spdm_exchange *spdm_ex)
+{
+	size_t request_padded_sz, response_padded_sz;
+	struct completion c = COMPLETION_INITIALIZER_ONSTACK(c);
+	struct pci_doe_task task = {
+		.prot = {
+			.vid = PCI_VENDOR_ID_PCI_SIG,
+			.type = PCI_DOE_PROTOCOL_CMA,
+		},
+		.complete = cxl_doe_task_complete,
+		.private = &c,
+	};
+	struct pci_doe_mb *doe = priv;
+	int rc;
+
+	/* DOE requires that response and request are padded to a multiple of 4 bytes */
+	request_padded_sz = ALIGN(spdm_ex->request_sz, sizeof(u32));
+	if (request_padded_sz != spdm_ex->request_sz) {
+		task.request_pl = kzalloc(request_padded_sz, GFP_KERNEL);
+		if (!task.request_pl)
+			return -ENOMEM;
+		memcpy(task.request_pl, spdm_ex->request, spdm_ex->request_sz);
+		task.request_pl_sz = request_padded_sz;
+	} else {
+		task.request_pl = (u32 *)spdm_ex->request;
+		task.request_pl_sz = spdm_ex->request_sz;
+	}
+
+	response_padded_sz = ALIGN(spdm_ex->response_sz, sizeof(u32));
+	if (response_padded_sz != spdm_ex->response_sz) {
+		task.response_pl = kzalloc(response_padded_sz, GFP_KERNEL);
+		if (!task.response_pl) {
+			rc = -ENOMEM;
+			goto err_free_req;
+		}
+		task.response_pl_sz = response_padded_sz;
+	} else {
+		task.response_pl = (u32 *)spdm_ex->response;
+		task.response_pl_sz = spdm_ex->response_sz;
+	}
+
+	rc = pci_doe_submit_task(doe, &task);
+	if (rc < 0)
+		goto err_free_rsp;
+
+	wait_for_completion(&c);
+	if (response_padded_sz != spdm_ex->response_sz)
+		memcpy(spdm_ex->response, task.response_pl, spdm_ex->response_sz);
+
+	rc = task.rv;
+err_free_rsp:
+	if (response_padded_sz != spdm_ex->response_sz)
+		kfree(task.response_pl);
+err_free_req:
+	if (request_padded_sz != spdm_ex->request_sz)
+		kfree(task.request_pl);
+
+	return rc;
+}
+
+struct spdm_state *pci_cma_create(struct device *dev, struct pci_doe_mb *doe)
+{
+	return spdm_create(cma_spdm_ex, doe, dev, cma_keyring);
+}
+EXPORT_SYMBOL_GPL(pci_cma_create);
+
+void pci_cma_destroy(struct spdm_state *spdm_state)
+{
+	kfree(spdm_state);
+}
+EXPORT_SYMBOL_GPL(pci_cma_destroy);
+
+int pci_cma_authenticate(struct spdm_state *spdm_state)
+{
+	return spdm_authenticate(spdm_state);
+}
+EXPORT_SYMBOL_GPL(pci_cma_authenticate);
+
+__init static int cma_keyring_init(void)
+{
+	cma_keyring = keyring_alloc("_cma",
+				    KUIDT_INIT(0), KGIDT_INIT(0),
+				    current_cred(),
+				    (KEY_POS_ALL & ~KEY_POS_SETATTR) |
+				    KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH,
+				    KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_SET_KEEP, NULL, NULL);
+	if (IS_ERR(cma_keyring))
+		pr_err("Could not allocate cma keyring\n");
+
+	return 0;
+}
+device_initcall(cma_keyring_init);
+MODULE_LICENSE("GPL");
diff --git a/include/linux/pci-cma.h b/include/linux/pci-cma.h
new file mode 100644
index 000000000000..d2a0a84973bf
--- /dev/null
+++ b/include/linux/pci-cma.h
@@ -0,0 +1,21 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Component Measurement and Authentication was added as an ECN to the
+ * PCIe r5.0 spec.
+ *
+ * Copyright (C) 2021 Huawei
+ *     Jonathan Cameron <Jonathan.Cameron@huawei.com>
+ */
+
+#ifndef _PCI_CMA_H_
+#define _PCI_CMA_H_
+struct pci_doe_mb;
+struct spdm_state;
+struct device;
+
+struct spdm_state *pci_cma_create(struct device *dev, struct pci_doe_mb *doe);
+void pci_cma_destroy(struct spdm_state *spdm_state);
+
+int pci_cma_authenticate(struct spdm_state *spdm_state);
+
+#endif