Linux CXL
 help / color / mirror / Atom feed
From: Lukas Wunner <lukas@wunner.de>
To: Bjorn Helgaas <helgaas@kernel.org>,
	David Howells <dhowells@redhat.com>,
	David Woodhouse <dwmw2@infradead.org>,
	Herbert Xu <herbert@gondor.apana.org.au>,
	"David S. Miller" <davem@davemloft.net>,
	Alex Williamson <alex.williamson@redhat.com>,
	<linux-pci@vger.kernel.org>, <linux-cxl@vger.kernel.org>,
	<linux-coco@lists.linux.dev>, <keyrings@vger.kernel.org>,
	<linux-crypto@vger.kernel.org>, <kvm@vger.kernel.org>
Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>,
	<linuxarm@huawei.com>, David Box <david.e.box@intel.com>,
	Dan Williams <dan.j.williams@intel.com>,
	Dave Jiang <dave.jiang@intel.com>,
	"Li, Ming" <ming4.li@intel.com>, Zhi Wang <zhi.a.wang@intel.com>,
	Alistair Francis <alistair.francis@wdc.com>,
	Wilfred Mallawa <wilfred.mallawa@wdc.com>,
	Alexey Kardashevskiy <aik@amd.com>,
	Tom Lendacky <thomas.lendacky@amd.com>,
	Sean Christopherson <seanjc@google.com>,
	Alexander Graf <graf@amazon.com>
Subject: [PATCH 09/12] PCI/CMA: Validate Subject Alternative Name in certificates
Date: Thu, 28 Sep 2023 19:32:39 +0200	[thread overview]
Message-ID: <bc1efd945f5d76587787f8351199e1ea45eaf2ef.1695921657.git.lukas@wunner.de> (raw)
In-Reply-To: <cover.1695921656.git.lukas@wunner.de>

PCIe r6.1 sec 6.31.3 stipulates requirements for X.509 Leaf Certificates
presented by devices, in particular the presence of a Subject Alternative
Name extension with a name that encodes the Vendor ID, Device ID, Device
Serial Number, etc.

This prevents a mismatch between the device identity in Config Space and
the certificate.  A device cannot misappropriate a certificate from a
different device without also spoofing Config Space.  As a corollary,
it cannot dupe an arbitrary driver into binding to it.  (Only those
which bind to the device identity in the Subject Alternative Name work.)

Parse the Subject Alternative Name using a small ASN.1 module and
validate its contents.  The theory of operation is explained in a code
comment at the top of the newly added cma-x509.c.

This functionality is introduced in a separate commit on top of basic
CMA-SPDM support to split the code into digestible, reviewable chunks.

The CMA OID added here is taken from the official OID Repository
(it's not documented in the PCIe Base Spec):
https://oid-rep.orange-labs.fr/get/2.23.147

Signed-off-by: Lukas Wunner <lukas@wunner.de>
---
 drivers/pci/Makefile         |   4 +-
 drivers/pci/cma-x509.c       | 119 +++++++++++++++++++++++++++++++++++
 drivers/pci/cma.asn1         |  36 +++++++++++
 drivers/pci/cma.c            |   3 +-
 drivers/pci/pci.h            |   2 +
 include/linux/oid_registry.h |   3 +
 include/linux/spdm.h         |   6 +-
 lib/spdm_requester.c         |  14 ++++-
 8 files changed, 183 insertions(+), 4 deletions(-)
 create mode 100644 drivers/pci/cma-x509.c
 create mode 100644 drivers/pci/cma.asn1

diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index e0705b82690b..a18812b8832b 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -34,7 +34,9 @@ obj-$(CONFIG_VGA_ARB)		+= vgaarb.o
 obj-$(CONFIG_PCI_DOE)		+= doe.o
 obj-$(CONFIG_PCI_DYNAMIC_OF_NODES) += of_property.o
 
-obj-$(CONFIG_PCI_CMA)		+= cma.o
+obj-$(CONFIG_PCI_CMA)		+= cma.o cma-x509.o cma.asn1.o
+$(obj)/cma-x509.o:		$(obj)/cma.asn1.h
+$(obj)/cma.asn1.o:		$(obj)/cma.asn1.c $(obj)/cma.asn1.h
 
 # Endpoint library must be initialized before its users
 obj-$(CONFIG_PCI_ENDPOINT)	+= endpoint/
diff --git a/drivers/pci/cma-x509.c b/drivers/pci/cma-x509.c
new file mode 100644
index 000000000000..614590303b38
--- /dev/null
+++ b/drivers/pci/cma-x509.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Component Measurement and Authentication (CMA-SPDM, PCIe r6.1 sec 6.31)
+ *
+ * The spdm_requester.c library calls pci_cma_validate() to check requirements
+ * for X.509 Leaf Certificates per PCIe r6.1 sec 6.31.3.
+ *
+ * It parses the Subject Alternative Name using the ASN.1 module cma.asn1,
+ * which calls pci_cma_note_oid() and pci_cma_note_san() to compare an
+ * OtherName against the expected name.
+ *
+ * The expected name is constructed beforehand by pci_cma_construct_san().
+ *
+ * Copyright (C) 2023 Intel Corporation
+ */
+
+#define dev_fmt(fmt) "CMA: " fmt
+
+#include <keys/x509-parser.h>
+#include <linux/asn1_decoder.h>
+#include <linux/oid_registry.h>
+#include <linux/pci.h>
+
+#include "cma.asn1.h"
+#include "pci.h"
+
+#define CMA_NAME_MAX sizeof("Vendor=1234:Device=1234:CC=123456:"	  \
+			    "REV=12:SSVID=1234:SSID=1234:1234567890123456")
+
+struct pci_cma_x509_context {
+	struct pci_dev *pdev;
+	enum OID last_oid;
+	char expected_name[CMA_NAME_MAX];
+	unsigned int expected_len;
+	unsigned int found:1;
+};
+
+int pci_cma_note_oid(void *context, size_t hdrlen, unsigned char tag,
+		     const void *value, size_t vlen)
+{
+	struct pci_cma_x509_context *ctx = context;
+
+	ctx->last_oid = look_up_OID(value, vlen);
+
+	return 0;
+}
+
+int pci_cma_note_san(void *context, size_t hdrlen, unsigned char tag,
+		     const void *value, size_t vlen)
+{
+	struct pci_cma_x509_context *ctx = context;
+
+	/* These aren't the drOIDs we're looking for. */
+	if (ctx->last_oid != OID_CMA)
+		return 0;
+
+	if (tag != ASN1_UTF8STR ||
+	    vlen != ctx->expected_len ||
+	    memcmp(value, ctx->expected_name, vlen) != 0) {
+		pci_err(ctx->pdev, "Invalid X.509 Subject Alternative Name\n");
+		return -EINVAL;
+	}
+
+	ctx->found = true;
+
+	return 0;
+}
+
+static unsigned int pci_cma_construct_san(struct pci_dev *pdev, char *name)
+{
+	unsigned int len;
+	u64 serial;
+
+	len = snprintf(name, CMA_NAME_MAX,
+		       "Vendor=%04hx:Device=%04hx:CC=%06x:REV=%02hhx",
+		       pdev->vendor, pdev->device, pdev->class, pdev->revision);
+
+	if (pdev->hdr_type == PCI_HEADER_TYPE_NORMAL)
+		len += snprintf(name + len, CMA_NAME_MAX - len,
+				":SSVID=%04hx:SSID=%04hx",
+				pdev->subsystem_vendor, pdev->subsystem_device);
+
+	serial = pci_get_dsn(pdev);
+	if (serial)
+		len += snprintf(name + len, CMA_NAME_MAX - len,
+				":%016llx", serial);
+
+	return len;
+}
+
+int pci_cma_validate(struct device *dev, struct x509_certificate *leaf_cert)
+{
+	struct pci_dev *pdev = to_pci_dev(dev);
+	struct pci_cma_x509_context ctx;
+	int ret;
+
+	if (!leaf_cert->raw_san) {
+		pci_err(pdev, "Missing X.509 Subject Alternative Name\n");
+		return -EINVAL;
+	}
+
+	ctx.pdev = pdev;
+	ctx.found = false;
+	ctx.expected_len = pci_cma_construct_san(pdev, ctx.expected_name);
+
+	ret = asn1_ber_decoder(&cma_decoder, &ctx, leaf_cert->raw_san,
+			       leaf_cert->raw_san_size);
+	if (ret == -EBADMSG || ret == -EMSGSIZE)
+		pci_err(pdev, "Malformed X.509 Subject Alternative Name\n");
+	if (ret < 0)
+		return ret;
+
+	if (!ctx.found) {
+		pci_err(pdev, "Missing X.509 OtherName with CMA OID\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
diff --git a/drivers/pci/cma.asn1 b/drivers/pci/cma.asn1
new file mode 100644
index 000000000000..10f90e107009
--- /dev/null
+++ b/drivers/pci/cma.asn1
@@ -0,0 +1,36 @@
+-- Component Measurement and Authentication (CMA-SPDM, PCIe r6.1 sec 6.31.3)
+-- X.509 Subject Alternative Name (RFC 5280 sec 4.2.1.6)
+--
+-- https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.6
+--
+-- The ASN.1 module in RFC 5280 appendix A.1 uses EXPLICIT TAGS whereas the one
+-- in appendix A.2 uses IMPLICIT TAGS.  The kernel's simplified asn1_compiler.c
+-- always uses EXPLICIT TAGS, hence this ASN.1 module differs from RFC 5280 in
+-- that it adds IMPLICIT to definitions from appendix A.2 (such as OtherName)
+-- and omits EXPLICIT in those definitions.
+
+SubjectAltName ::= GeneralNames
+
+GeneralNames ::= SEQUENCE OF GeneralName
+
+GeneralName ::= CHOICE {
+	otherName			[0] IMPLICIT OtherName,
+	rfc822Name			[1] IMPLICIT IA5String,
+	dNSName				[2] IMPLICIT IA5String,
+	x400Address			[3] ANY,
+	directoryName			[4] ANY,
+	ediPartyName			[5] IMPLICIT EDIPartyName,
+	uniformResourceIdentifier	[6] IMPLICIT IA5String,
+	iPAddress			[7] IMPLICIT OCTET STRING,
+	registeredID			[8] IMPLICIT OBJECT IDENTIFIER
+	}
+
+OtherName ::= SEQUENCE {
+	type-id			OBJECT IDENTIFIER ({ pci_cma_note_oid }),
+	value			[0] ANY ({ pci_cma_note_san })
+	}
+
+EDIPartyName ::= SEQUENCE {
+	nameAssigner		[0] ANY OPTIONAL,
+	partyName		[1] ANY
+	}
diff --git a/drivers/pci/cma.c b/drivers/pci/cma.c
index 06e5846325e3..012190c54ab6 100644
--- a/drivers/pci/cma.c
+++ b/drivers/pci/cma.c
@@ -64,7 +64,8 @@ void pci_cma_init(struct pci_dev *pdev)
 		return;
 
 	pdev->spdm_state = spdm_create(&pdev->dev, pci_doe_transport, doe,
-				       PCI_DOE_MAX_PAYLOAD, pci_cma_keyring);
+				       PCI_DOE_MAX_PAYLOAD, pci_cma_keyring,
+				       pci_cma_validate);
 	if (!pdev->spdm_state) {
 		return;
 	}
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index bd80a0369c9c..6c4755a2c91c 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -325,6 +325,8 @@ static inline void pci_doe_disconnected(struct pci_dev *pdev) { }
 #ifdef CONFIG_PCI_CMA
 void pci_cma_init(struct pci_dev *pdev);
 void pci_cma_destroy(struct pci_dev *pdev);
+struct x509_certificate;
+int pci_cma_validate(struct device *dev, struct x509_certificate *leaf_cert);
 #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/oid_registry.h b/include/linux/oid_registry.h
index f86a08ba0207..cafec7111473 100644
--- a/include/linux/oid_registry.h
+++ b/include/linux/oid_registry.h
@@ -141,6 +141,9 @@ enum OID {
 	OID_TPMImportableKey,		/* 2.23.133.10.1.4 */
 	OID_TPMSealedData,		/* 2.23.133.10.1.5 */
 
+	/* PCI */
+	OID_CMA,			/* 2.23.147 */
+
 	OID__NR
 };
 
diff --git a/include/linux/spdm.h b/include/linux/spdm.h
index e824063793a7..69a83bc2eb41 100644
--- a/include/linux/spdm.h
+++ b/include/linux/spdm.h
@@ -17,14 +17,18 @@
 struct key;
 struct device;
 struct spdm_state;
+struct x509_certificate;
 
 typedef int (spdm_transport)(void *priv, struct device *dev,
 			     const void *request, size_t request_sz,
 			     void *response, size_t response_sz);
 
+typedef int (spdm_validate)(struct device *dev,
+			    struct x509_certificate *leaf_cert);
+
 struct spdm_state *spdm_create(struct device *dev, spdm_transport *transport,
 			       void *transport_priv, u32 transport_sz,
-			       struct key *keyring);
+			       struct key *keyring, spdm_validate *validate);
 
 int spdm_authenticate(struct spdm_state *spdm_state);
 
diff --git a/lib/spdm_requester.c b/lib/spdm_requester.c
index 407041036599..b2af2074ba6f 100644
--- a/lib/spdm_requester.c
+++ b/lib/spdm_requester.c
@@ -489,6 +489,7 @@ static int spdm_err(struct device *dev, struct spdm_error_rsp *rsp)
  *	responder's signatures.
  * @root_keyring: Keyring against which to check the first certificate in
  *	responder's certificate chain.
+ * @validate: Function to validate additional leaf certificate requirements.
  */
 struct spdm_state {
 	struct mutex lock;
@@ -520,6 +521,7 @@ struct spdm_state {
 	/* Certificates */
 	struct public_key *leaf_key;
 	struct key *root_keyring;
+	spdm_validate *validate;
 };
 
 static int __spdm_exchange(struct spdm_state *spdm_state,
@@ -1003,6 +1005,13 @@ static int spdm_validate_cert_chain(struct spdm_state *spdm_state, u8 slot,
 	}
 
 	prev = NULL;
+
+	if (spdm_state->validate) {
+		rc = spdm_state->validate(spdm_state->dev, cert);
+		if (rc)
+			goto err_free_cert;
+	}
+
 	spdm_state->leaf_key = cert->pub;
 	cert->pub = NULL;
 
@@ -1447,12 +1456,14 @@ EXPORT_SYMBOL_GPL(spdm_authenticated);
  * @transport_priv: Transport private data
  * @transport_sz: Maximum message size the transport is capable of (in bytes)
  * @keyring: Trusted root certificates
+ * @validate: Function to validate additional leaf certificate requirements
+ *	(optional, may be %NULL)
  *
  * Returns a pointer to the allocated SPDM session state or NULL on error.
  */
 struct spdm_state *spdm_create(struct device *dev, spdm_transport *transport,
 			       void *transport_priv, u32 transport_sz,
-			       struct key *keyring)
+			       struct key *keyring, spdm_validate *validate)
 {
 	struct spdm_state *spdm_state = kzalloc(sizeof(*spdm_state), GFP_KERNEL);
 
@@ -1464,6 +1475,7 @@ struct spdm_state *spdm_create(struct device *dev, spdm_transport *transport,
 	spdm_state->transport_priv = transport_priv;
 	spdm_state->transport_sz = transport_sz;
 	spdm_state->root_keyring = keyring;
+	spdm_state->validate = validate;
 
 	mutex_init(&spdm_state->lock);
 
-- 
2.40.1


  parent reply	other threads:[~2023-09-28 18:07 UTC|newest]

Thread overview: 83+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-09-28 17:32 [PATCH 00/12] PCI device authentication Lukas Wunner
2023-09-28 17:32 ` [PATCH 04/12] certs: Create blacklist keyring earlier Lukas Wunner
2023-10-03  8:37   ` Ilpo Järvinen
2023-10-03 22:53     ` Wilfred Mallawa
2023-10-03  9:10   ` Jonathan Cameron
2023-10-06 19:19   ` Dan Williams
2023-10-12  2:20   ` Alistair Francis
2023-09-28 17:32 ` [PATCH 02/12] X.509: Parse Subject Alternative Name in certificates Lukas Wunner
2023-10-03  8:31   ` Ilpo Järvinen
2023-10-03 22:52     ` Wilfred Mallawa
2023-10-03 15:14   ` Jonathan Cameron
2023-10-06 19:09   ` Dan Williams
2023-09-28 17:32 ` [PATCH 03/12] X.509: Move certificate length retrieval into new helper Lukas Wunner
2023-10-02 16:44   ` Jonathan Cameron
2023-10-03  8:31   ` Ilpo Järvinen
2023-10-06 19:15   ` Dan Williams
2024-03-04  6:57     ` Lukas Wunner
2024-03-04 19:19       ` Dan Williams
2023-09-28 17:32 ` [PATCH 01/12] X.509: Make certificate parser public Lukas Wunner
2023-10-03  7:57   ` Ilpo Järvinen
2023-10-03 15:13   ` Jonathan Cameron
2023-10-06 18:47   ` Dan Williams
2023-09-28 17:32 ` [PATCH 05/12] crypto: akcipher - Support more than one signature encoding Lukas Wunner
2023-10-02 16:59   ` Jonathan Cameron
2023-10-06 19:23   ` Dan Williams
2023-10-07 14:46     ` Lukas Wunner
2023-09-28 17:32 ` [PATCH 06/12] crypto: ecdsa - Support P1363 " Lukas Wunner
2023-10-02 16:57   ` Jonathan Cameron
2023-09-28 17:32 ` [PATCH 07/12] spdm: Introduce library to authenticate devices Lukas Wunner
2023-10-03 10:35   ` Ilpo Järvinen
2024-02-09 20:32     ` Lukas Wunner
2024-02-12 11:47       ` Ilpo Järvinen
2024-03-20  8:33       ` Lukas Wunner
2023-10-03 14:39   ` Jonathan Cameron
2023-10-12  3:26     ` Alistair Francis
2023-10-12  4:37       ` Damien Le Moal
2023-10-12  7:16       ` Lukas Wunner
2023-10-12 15:09         ` Jonathan Cameron
2024-02-04 17:25     ` Lukas Wunner
2024-02-05 10:07       ` Jonathan Cameron
2023-10-06 20:34   ` Dan Williams
2023-09-28 17:32 ` [PATCH 08/12] PCI/CMA: Authenticate devices on enumeration Lukas Wunner
2023-10-03 14:47   ` Jonathan Cameron
2023-10-05 20:10   ` Bjorn Helgaas
2023-09-28 17:32 ` Lukas Wunner [this message]
2023-10-03 15:04   ` [PATCH 09/12] PCI/CMA: Validate Subject Alternative Name in certificates Jonathan Cameron
2023-10-05 14:04     ` Lukas Wunner
2023-10-05 20:09       ` Bjorn Helgaas
2023-09-28 17:32 ` [PATCH 10/12] PCI/CMA: Reauthenticate devices on reset and resume Lukas Wunner
2023-10-03 15:10   ` Jonathan Cameron
2023-09-28 17:32 ` [PATCH 11/12] PCI/CMA: Expose in sysfs whether devices are authenticated Lukas Wunner
2023-10-03  9:04   ` Ilpo Järvinen
2023-10-03 15:28   ` Jonathan Cameron
2023-10-05 20:20   ` Bjorn Helgaas
2023-09-28 17:32 ` [PATCH 12/12] PCI/CMA: Grant guests exclusive control of authentication Lukas Wunner
2023-10-03  9:12   ` Ilpo Järvinen
2023-10-03 15:40   ` Jonathan Cameron
2023-10-03 19:30     ` Lukas Wunner
2023-10-05 20:34       ` Bjorn Helgaas
2023-10-06  9:30       ` Jonathan Cameron
2023-10-18 19:58         ` Dan Williams
2023-10-19  7:58           ` Alexey Kardashevskiy
2023-10-24 17:04             ` Dan Williams
2023-10-09 10:52   ` Alexey Kardashevskiy
2023-10-09 14:02     ` Lukas Wunner
2023-10-06 16:06 ` [PATCH 00/12] PCI device authentication Dan Williams
2023-10-07 10:04   ` Lukas Wunner
2023-10-09 11:33     ` Jonathan Cameron
2023-10-09 13:49       ` Lukas Wunner
2023-10-10  4:07         ` Alexey Kardashevskiy
2023-10-10  8:19           ` Lukas Wunner
2023-10-10 12:53             ` Alexey Kardashevskiy
2023-10-11 16:57               ` Jonathan Cameron
2023-10-12  3:00                 ` Alexey Kardashevskiy
2023-10-12 15:15                   ` Jonathan Cameron
2023-10-11 16:42           ` Jonathan Cameron
2023-10-12  9:15           ` Lukas Wunner
2023-10-12 11:18             ` Alexey Kardashevskiy
2023-10-12 15:25               ` Jonathan Cameron
2023-10-12 13:13             ` Samuel Ortiz
2023-10-12 15:32               ` Jonathan Cameron
2023-10-13  5:03                 ` Samuel Ortiz
2023-10-13 11:45                   ` Alexey Kardashevskiy

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=bc1efd945f5d76587787f8351199e1ea45eaf2ef.1695921657.git.lukas@wunner.de \
    --to=lukas@wunner.de \
    --cc=Jonathan.Cameron@huawei.com \
    --cc=aik@amd.com \
    --cc=alex.williamson@redhat.com \
    --cc=alistair.francis@wdc.com \
    --cc=dan.j.williams@intel.com \
    --cc=dave.jiang@intel.com \
    --cc=davem@davemloft.net \
    --cc=david.e.box@intel.com \
    --cc=dhowells@redhat.com \
    --cc=dwmw2@infradead.org \
    --cc=graf@amazon.com \
    --cc=helgaas@kernel.org \
    --cc=herbert@gondor.apana.org.au \
    --cc=keyrings@vger.kernel.org \
    --cc=kvm@vger.kernel.org \
    --cc=linux-coco@lists.linux.dev \
    --cc=linux-crypto@vger.kernel.org \
    --cc=linux-cxl@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=linuxarm@huawei.com \
    --cc=ming4.li@intel.com \
    --cc=seanjc@google.com \
    --cc=thomas.lendacky@amd.com \
    --cc=wilfred.mallawa@wdc.com \
    --cc=zhi.a.wang@intel.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox