summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-05-24 13:16:50 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2022-05-24 13:16:50 -0700
commit7cf6a8a17f5b134b7e783c2d45c53298faef82a7 (patch)
treee5a6346abf5d9efbe49b91e6291349afcacfb7d3
parenta9d1046a846571422a92d2b8fbf8a8b24221b9a3 (diff)
parent7f3113e3b9f7207f0bd57b5fdae1a1b9c8215e08 (diff)
Merge tag 'tpmdd-next-v5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd
Pull tpm updates from Jarkko Sakkinen: - Tightened validation of key hashes for SYSTEM_BLACKLIST_HASH_LIST. An invalid hash format causes a compilation error. Previously, they got included to the kernel binary but were silently ignored at run-time. - Allow root user to append new hashes to the blacklist keyring. - Trusted keys backed with Cryptographic Acceleration and Assurance Module (CAAM), which part of some of the new NXP's SoC's. Now there is total three hardware backends for trusted keys: TPM, ARM TEE and CAAM. - A scattered set of fixes and small improvements for the TPM driver. * tag 'tpmdd-next-v5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jarkko/linux-tpmdd: MAINTAINERS: add KEYS-TRUSTED-CAAM doc: trusted-encrypted: describe new CAAM trust source KEYS: trusted: Introduce support for NXP CAAM-based trusted keys crypto: caam - add in-kernel interface for blob generator crypto: caam - determine whether CAAM supports blob encap/decap KEYS: trusted: allow use of kernel RNG for key material KEYS: trusted: allow use of TEE as backend without TCG_TPM support tpm: Add field upgrade mode support for Infineon TPM2 modules tpm: Fix buffer access in tpm2_get_tpm_pt() char: tpm: cr50_i2c: Suppress duplicated error message in .remove() tpm: cr50: Add new device/vendor ID 0x504a6666 tpm: Remove read16/read32/write32 calls from tpm_tis_phy_ops tpm: ibmvtpm: Correct the return value in tpm_ibmvtpm_probe() tpm/tpm_ftpm_tee: Return true/false (not 1/0) from bool functions certs: Explain the rationale to call panic() certs: Allow root user to append signed hashes to the blacklist keyring certs: Check that builtin blacklist hashes are valid certs: Make blacklist_vet_description() more strict certs: Factor out the blacklist hash creation tools/certs: Add print-cert-tbs-hash.sh
-rw-r--r--Documentation/admin-guide/kernel-parameters.txt11
-rw-r--r--Documentation/security/keys/trusted-encrypted.rst60
-rw-r--r--MAINTAINERS11
-rw-r--r--certs/.gitignore1
-rw-r--r--certs/Kconfig17
-rw-r--r--certs/Makefile14
-rw-r--r--certs/blacklist.c227
-rw-r--r--crypto/asymmetric_keys/x509_public_key.c3
-rw-r--r--drivers/char/tpm/tpm2-cmd.c17
-rw-r--r--drivers/char/tpm/tpm_ftpm_tee.c2
-rw-r--r--drivers/char/tpm/tpm_ibmvtpm.c1
-rw-r--r--drivers/char/tpm/tpm_tis.c67
-rw-r--r--drivers/char/tpm/tpm_tis_core.h58
-rw-r--r--drivers/char/tpm/tpm_tis_i2c_cr50.c11
-rw-r--r--drivers/char/tpm/tpm_tis_spi.h4
-rw-r--r--drivers/char/tpm/tpm_tis_spi_cr50.c7
-rw-r--r--drivers/char/tpm/tpm_tis_spi_main.c45
-rw-r--r--drivers/char/tpm/tpm_tis_synquacer.c98
-rw-r--r--drivers/crypto/caam/Kconfig3
-rw-r--r--drivers/crypto/caam/Makefile1
-rw-r--r--drivers/crypto/caam/blob_gen.c182
-rw-r--r--drivers/crypto/caam/ctrl.c17
-rw-r--r--drivers/crypto/caam/intern.h1
-rw-r--r--drivers/crypto/caam/regs.h4
-rw-r--r--include/keys/system_keyring.h14
-rw-r--r--include/keys/trusted-type.h2
-rw-r--r--include/keys/trusted_caam.h11
-rw-r--r--include/soc/fsl/caam-blob.h103
-rwxr-xr-xscripts/check-blacklist-hashes.awk37
-rw-r--r--security/integrity/platform_certs/keyring_handler.c26
-rw-r--r--security/keys/Kconfig18
-rw-r--r--security/keys/trusted-keys/Kconfig38
-rw-r--r--security/keys/trusted-keys/Makefile10
-rw-r--r--security/keys/trusted-keys/trusted_caam.c80
-rw-r--r--security/keys/trusted-keys/trusted_core.c45
-rwxr-xr-xtools/certs/print-cert-tbs-hash.sh91
36 files changed, 1056 insertions, 281 deletions
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 9336d98fc670..a1a0eba0a2e5 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -6081,11 +6081,22 @@
sources:
- "tpm"
- "tee"
+ - "caam"
If not specified then it defaults to iterating through
the trust source list starting with TPM and assigns the
first trust source as a backend which is initialized
successfully during iteration.
+ trusted.rng= [KEYS]
+ Format: <string>
+ The RNG used to generate key material for trusted keys.
+ Can be one of:
+ - "kernel"
+ - the same value as trusted.source: "tpm" or "tee"
+ - "default"
+ If not specified, "default" is used. In this case,
+ the RNG's choice is left to each individual trust source.
+
tsc= Disable clocksource stability checks for TSC.
Format: <string>
[x86] reliable: mark tsc clocksource as reliable, this
diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst
index f614dad7de12..0bfb4c339748 100644
--- a/Documentation/security/keys/trusted-encrypted.rst
+++ b/Documentation/security/keys/trusted-encrypted.rst
@@ -35,6 +35,13 @@ safe.
Rooted to Hardware Unique Key (HUK) which is generally burnt in on-chip
fuses and is accessible to TEE only.
+ (3) CAAM (Cryptographic Acceleration and Assurance Module: IP on NXP SoCs)
+
+ When High Assurance Boot (HAB) is enabled and the CAAM is in secure
+ mode, trust is rooted to the OTPMK, a never-disclosed 256-bit key
+ randomly generated and fused into each SoC at manufacturing time.
+ Otherwise, a common fixed test key is used instead.
+
* Execution isolation
(1) TPM
@@ -46,6 +53,10 @@ safe.
Customizable set of operations running in isolated execution
environment verified via Secure/Trusted boot process.
+ (3) CAAM
+
+ Fixed set of operations running in isolated execution environment.
+
* Optional binding to platform integrity state
(1) TPM
@@ -63,6 +74,11 @@ safe.
Relies on Secure/Trusted boot process for platform integrity. It can
be extended with TEE based measured boot process.
+ (3) CAAM
+
+ Relies on the High Assurance Boot (HAB) mechanism of NXP SoCs
+ for platform integrity.
+
* Interfaces and APIs
(1) TPM
@@ -74,10 +90,13 @@ safe.
TEEs have well-documented, standardized client interface and APIs. For
more details refer to ``Documentation/staging/tee.rst``.
+ (3) CAAM
+
+ Interface is specific to silicon vendor.
* Threat model
- The strength and appropriateness of a particular TPM or TEE for a given
+ The strength and appropriateness of a particular trust source for a given
purpose must be assessed when using them to protect security-relevant data.
@@ -87,22 +106,32 @@ Key Generation
Trusted Keys
------------
-New keys are created from random numbers generated in the trust source. They
-are encrypted/decrypted using a child key in the storage key hierarchy.
-Encryption and decryption of the child key must be protected by a strong
-access control policy within the trust source.
+New keys are created from random numbers. They are encrypted/decrypted using
+a child key in the storage key hierarchy. Encryption and decryption of the
+child key must be protected by a strong access control policy within the
+trust source. The random number generator in use differs according to the
+selected trust source:
- * TPM (hardware device) based RNG
+ * TPM: hardware device based RNG
- Strength of random numbers may vary from one device manufacturer to
- another.
+ Keys are generated within the TPM. Strength of random numbers may vary
+ from one device manufacturer to another.
- * TEE (OP-TEE based on Arm TrustZone) based RNG
+ * TEE: OP-TEE based on Arm TrustZone based RNG
RNG is customizable as per platform needs. It can either be direct output
from platform specific hardware RNG or a software based Fortuna CSPRNG
which can be seeded via multiple entropy sources.
+ * CAAM: Kernel RNG
+
+ The normal kernel random number generator is used. To seed it from the
+ CAAM HWRNG, enable CRYPTO_DEV_FSL_CAAM_RNG_API and ensure the device
+ is probed.
+
+Users may override this by specifying ``trusted.rng=kernel`` on the kernel
+command-line to override the used RNG with the kernel's random number pool.
+
Encrypted Keys
--------------
@@ -189,6 +218,19 @@ Usage::
specific to TEE device implementation. The key length for new keys is always
in bytes. Trusted Keys can be 32 - 128 bytes (256 - 1024 bits).
+Trusted Keys usage: CAAM
+------------------------
+
+Usage::
+
+ keyctl add trusted name "new keylen" ring
+ keyctl add trusted name "load hex_blob" ring
+ keyctl print keyid
+
+"keyctl print" returns an ASCII hex copy of the sealed key, which is in a
+CAAM-specific format. The key length for new keys is always in bytes.
+Trusted Keys can be 32 - 128 bytes (256 - 1024 bits).
+
Encrypted Keys usage
--------------------
diff --git a/MAINTAINERS b/MAINTAINERS
index 413dc6a4db3c..3bf9805b10aa 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4575,7 +4575,9 @@ L: keyrings@vger.kernel.org
S: Maintained
F: Documentation/admin-guide/module-signing.rst
F: certs/
+F: scripts/check-blacklist-hashes.awk
F: scripts/sign-file.c
+F: tools/certs/
CFAG12864B LCD DRIVER
M: Miguel Ojeda <ojeda@kernel.org>
@@ -10865,6 +10867,15 @@ S: Supported
F: include/keys/trusted_tee.h
F: security/keys/trusted-keys/trusted_tee.c
+KEYS-TRUSTED-CAAM
+M: Ahmad Fatoum <a.fatoum@pengutronix.de>
+R: Pengutronix Kernel Team <kernel@pengutronix.de>
+L: linux-integrity@vger.kernel.org
+L: keyrings@vger.kernel.org
+S: Maintained
+F: include/keys/trusted_caam.h
+F: security/keys/trusted-keys/trusted_caam.c
+
KEYS/KEYRINGS
M: David Howells <dhowells@redhat.com>
M: Jarkko Sakkinen <jarkko@kernel.org>
diff --git a/certs/.gitignore b/certs/.gitignore
index 9e42fe3e02f5..56637aceaf81 100644
--- a/certs/.gitignore
+++ b/certs/.gitignore
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
+/blacklist_hashes_checked
/extract-cert
/x509_certificate_list
/x509_revocation_list
diff --git a/certs/Kconfig b/certs/Kconfig
index 73d1350c223a..476755703cf8 100644
--- a/certs/Kconfig
+++ b/certs/Kconfig
@@ -104,8 +104,11 @@ config SYSTEM_BLACKLIST_HASH_LIST
help
If set, this option should be the filename of a list of hashes in the
form "<hash>", "<hash>", ... . This will be included into a C
- wrapper to incorporate the list into the kernel. Each <hash> should
- be a string of hex digits.
+ wrapper to incorporate the list into the kernel. Each <hash> must be a
+ string starting with a prefix ("tbs" or "bin"), then a colon (":"), and
+ finally an even number of hexadecimal lowercase characters (up to 128).
+ Certificate hashes can be generated with
+ tools/certs/print-cert-tbs-hash.sh .
config SYSTEM_REVOCATION_LIST
bool "Provide system-wide ring of revocation certificates"
@@ -124,4 +127,14 @@ config SYSTEM_REVOCATION_KEYS
containing X.509 certificates to be included in the default blacklist
keyring.
+config SYSTEM_BLACKLIST_AUTH_UPDATE
+ bool "Allow root to add signed blacklist keys"
+ depends on SYSTEM_BLACKLIST_KEYRING
+ depends on SYSTEM_DATA_VERIFICATION
+ help
+ If set, provide the ability to load new blacklist keys at run time if
+ they are signed and vouched by a certificate from the builtin trusted
+ keyring. The PKCS#7 signature of the description is set in the key
+ payload. Blacklist keys cannot be removed.
+
endmenu
diff --git a/certs/Makefile b/certs/Makefile
index d8443cfb1c40..1d26ae36af20 100644
--- a/certs/Makefile
+++ b/certs/Makefile
@@ -7,6 +7,18 @@ obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o c
obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist.o common.o
obj-$(CONFIG_SYSTEM_REVOCATION_LIST) += revocation_certificates.o
ifneq ($(CONFIG_SYSTEM_BLACKLIST_HASH_LIST),)
+quiet_cmd_check_blacklist_hashes = CHECK $(patsubst "%",%,$(2))
+ cmd_check_blacklist_hashes = $(AWK) -f $(srctree)/scripts/check-blacklist-hashes.awk $(2); touch $@
+
+$(eval $(call config_filename,SYSTEM_BLACKLIST_HASH_LIST))
+
+$(obj)/blacklist_hashes.o: $(obj)/blacklist_hashes_checked
+
+CFLAGS_blacklist_hashes.o += -I$(srctree)
+
+targets += blacklist_hashes_checked
+$(obj)/blacklist_hashes_checked: $(SYSTEM_BLACKLIST_HASH_LIST_SRCPREFIX)$(SYSTEM_BLACKLIST_HASH_LIST_FILENAME) scripts/check-blacklist-hashes.awk FORCE
+ $(call if_changed,check_blacklist_hashes,$(SYSTEM_BLACKLIST_HASH_LIST_SRCPREFIX)$(CONFIG_SYSTEM_BLACKLIST_HASH_LIST))
obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_hashes.o
else
obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_nohashes.o
@@ -21,7 +33,7 @@ $(obj)/system_certificates.o: $(obj)/x509_certificate_list
$(obj)/x509_certificate_list: $(CONFIG_SYSTEM_TRUSTED_KEYS) $(obj)/extract-cert FORCE
$(call if_changed,extract_certs)
-targets += x509_certificate_list
+targets += x509_certificate_list blacklist_hashes_checked
# If module signing is requested, say by allyesconfig, but a key has not been
# supplied, then one will need to be generated to make sure the build does not
diff --git a/certs/blacklist.c b/certs/blacklist.c
index c9a435b15af4..25094ea73600 100644
--- a/certs/blacklist.c
+++ b/certs/blacklist.c
@@ -15,10 +15,24 @@
#include <linux/err.h>
#include <linux/seq_file.h>
#include <linux/uidgid.h>
+#include <linux/verification.h>
#include <keys/system_keyring.h>
#include "blacklist.h"
#include "common.h"
+/*
+ * According to crypto/asymmetric_keys/x509_cert_parser.c:x509_note_pkey_algo(),
+ * the size of the currently longest supported hash algorithm is 512 bits,
+ * which translates into 128 hex characters.
+ */
+#define MAX_HASH_LEN 128
+
+#define BLACKLIST_KEY_PERM (KEY_POS_SEARCH | KEY_POS_VIEW | \
+ KEY_USR_SEARCH | KEY_USR_VIEW)
+
+static const char tbs_prefix[] = "tbs";
+static const char bin_prefix[] = "bin";
+
static struct key *blacklist_keyring;
#ifdef CONFIG_SYSTEM_REVOCATION_LIST
@@ -32,41 +46,89 @@ extern __initconst const unsigned long revocation_certificate_list_size;
*/
static int blacklist_vet_description(const char *desc)
{
- int n = 0;
-
- if (*desc == ':')
- return -EINVAL;
- for (; *desc; desc++)
- if (*desc == ':')
- goto found_colon;
+ int i, prefix_len, tbs_step = 0, bin_step = 0;
+
+ /* The following algorithm only works if prefix lengths match. */
+ BUILD_BUG_ON(sizeof(tbs_prefix) != sizeof(bin_prefix));
+ prefix_len = sizeof(tbs_prefix) - 1;
+ for (i = 0; *desc; desc++, i++) {
+ if (*desc == ':') {
+ if (tbs_step == prefix_len)
+ goto found_colon;
+ if (bin_step == prefix_len)
+ goto found_colon;
+ return -EINVAL;
+ }
+ if (i >= prefix_len)
+ return -EINVAL;
+ if (*desc == tbs_prefix[i])
+ tbs_step++;
+ if (*desc == bin_prefix[i])
+ bin_step++;
+ }
return -EINVAL;
found_colon:
desc++;
- for (; *desc; desc++) {
+ for (i = 0; *desc && i < MAX_HASH_LEN; desc++, i++) {
if (!isxdigit(*desc) || isupper(*desc))
return -EINVAL;
- n++;
}
+ if (*desc)
+ /* The hash is greater than MAX_HASH_LEN. */
+ return -ENOPKG;
- if (n == 0 || n & 1)
+ /* Checks for an even number of hexadecimal characters. */
+ if (i == 0 || i & 1)
return -EINVAL;
return 0;
}
-/*
- * The hash to be blacklisted is expected to be in the description. There will
- * be no payload.
- */
-static int blacklist_preparse(struct key_preparsed_payload *prep)
+static int blacklist_key_instantiate(struct key *key,
+ struct key_preparsed_payload *prep)
{
- if (prep->datalen > 0)
- return -EINVAL;
- return 0;
+#ifdef CONFIG_SYSTEM_BLACKLIST_AUTH_UPDATE
+ int err;
+#endif
+
+ /* Sets safe default permissions for keys loaded by user space. */
+ key->perm = BLACKLIST_KEY_PERM;
+
+ /*
+ * Skips the authentication step for builtin hashes, they are not
+ * signed but still trusted.
+ */
+ if (key->flags & (1 << KEY_FLAG_BUILTIN))
+ goto out;
+
+#ifdef CONFIG_SYSTEM_BLACKLIST_AUTH_UPDATE
+ /*
+ * Verifies the description's PKCS#7 signature against the builtin
+ * trusted keyring.
+ */
+ err = verify_pkcs7_signature(key->description,
+ strlen(key->description), prep->data, prep->datalen,
+ NULL, VERIFYING_UNSPECIFIED_SIGNATURE, NULL, NULL);
+ if (err)
+ return err;
+#else
+ /*
+ * It should not be possible to come here because the keyring doesn't
+ * have KEY_USR_WRITE and the only other way to call this function is
+ * for builtin hashes.
+ */
+ WARN_ON_ONCE(1);
+ return -EPERM;
+#endif
+
+out:
+ return generic_key_instantiate(key, prep);
}
-static void blacklist_free_preparse(struct key_preparsed_payload *prep)
+static int blacklist_key_update(struct key *key,
+ struct key_preparsed_payload *prep)
{
+ return -EPERM;
}
static void blacklist_describe(const struct key *key, struct seq_file *m)
@@ -77,17 +139,48 @@ static void blacklist_describe(const struct key *key, struct seq_file *m)
static struct key_type key_type_blacklist = {
.name = "blacklist",
.vet_description = blacklist_vet_description,
- .preparse = blacklist_preparse,
- .free_preparse = blacklist_free_preparse,
- .instantiate = generic_key_instantiate,
+ .instantiate = blacklist_key_instantiate,
+ .update = blacklist_key_update,
.describe = blacklist_describe,
};
+static char *get_raw_hash(const u8 *hash, size_t hash_len,
+ enum blacklist_hash_type hash_type)
+{
+ size_t type_len;
+ const char *type_prefix;
+ char *buffer, *p;
+
+ switch (hash_type) {
+ case BLACKLIST_HASH_X509_TBS:
+ type_len = sizeof(tbs_prefix) - 1;
+ type_prefix = tbs_prefix;
+ break;
+ case BLACKLIST_HASH_BINARY:
+ type_len = sizeof(bin_prefix) - 1;
+ type_prefix = bin_prefix;
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ return ERR_PTR(-EINVAL);
+ }
+ buffer = kmalloc(type_len + 1 + hash_len * 2 + 1, GFP_KERNEL);
+ if (!buffer)
+ return ERR_PTR(-ENOMEM);
+ p = memcpy(buffer, type_prefix, type_len);
+ p += type_len;
+ *p++ = ':';
+ bin2hex(p, hash, hash_len);
+ p += hash_len * 2;
+ *p = '\0';
+ return buffer;
+}
+
/**
- * mark_hash_blacklisted - Add a hash to the system blacklist
+ * mark_raw_hash_blacklisted - Add a hash to the system blacklist
* @hash: The hash as a hex string with a type prefix (eg. "tbs:23aa429783")
*/
-int mark_hash_blacklisted(const char *hash)
+static int mark_raw_hash_blacklisted(const char *hash)
{
key_ref_t key;
@@ -96,8 +189,7 @@ int mark_hash_blacklisted(const char *hash)
hash,
NULL,
0,
- ((KEY_POS_ALL & ~KEY_POS_SETATTR) |
- KEY_USR_VIEW),
+ BLACKLIST_KEY_PERM,
KEY_ALLOC_NOT_IN_QUOTA |
KEY_ALLOC_BUILT_IN);
if (IS_ERR(key)) {
@@ -107,29 +199,36 @@ int mark_hash_blacklisted(const char *hash)
return 0;
}
+int mark_hash_blacklisted(const u8 *hash, size_t hash_len,
+ enum blacklist_hash_type hash_type)
+{
+ const char *buffer;
+ int err;
+
+ buffer = get_raw_hash(hash, hash_len, hash_type);
+ if (IS_ERR(buffer))
+ return PTR_ERR(buffer);
+ err = mark_raw_hash_blacklisted(buffer);
+ kfree(buffer);
+ return err;
+}
+
/**
* is_hash_blacklisted - Determine if a hash is blacklisted
* @hash: The hash to be checked as a binary blob
* @hash_len: The length of the binary hash
- * @type: Type of hash
+ * @hash_type: Type of hash
*/
-int is_hash_blacklisted(const u8 *hash, size_t hash_len, const char *type)
+int is_hash_blacklisted(const u8 *hash, size_t hash_len,
+ enum blacklist_hash_type hash_type)
{
key_ref_t kref;
- size_t type_len = strlen(type);
- char *buffer, *p;
+ const char *buffer;
int ret = 0;
- buffer = kmalloc(type_len + 1 + hash_len * 2 + 1, GFP_KERNEL);
- if (!buffer)
- return -ENOMEM;
- p = memcpy(buffer, type, type_len);
- p += type_len;
- *p++ = ':';
- bin2hex(p, hash, hash_len);
- p += hash_len * 2;
- *p = 0;
-
+ buffer = get_raw_hash(hash, hash_len, hash_type);
+ if (IS_ERR(buffer))
+ return PTR_ERR(buffer);
kref = keyring_search(make_key_ref(blacklist_keyring, true),
&key_type_blacklist, buffer, false);
if (!IS_ERR(kref)) {
@@ -144,7 +243,8 @@ EXPORT_SYMBOL_GPL(is_hash_blacklisted);
int is_binary_blacklisted(const u8 *hash, size_t hash_len)
{
- if (is_hash_blacklisted(hash, hash_len, "bin") == -EKEYREJECTED)
+ if (is_hash_blacklisted(hash, hash_len, BLACKLIST_HASH_BINARY) ==
+ -EKEYREJECTED)
return -EPERM;
return 0;
@@ -166,8 +266,10 @@ int add_key_to_revocation_list(const char *data, size_t size)
NULL,
data,
size,
- ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW),
- KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_BUILT_IN);
+ KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH
+ | KEY_USR_VIEW,
+ KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_BUILT_IN
+ | KEY_ALLOC_BYPASS_RESTRICTION);
if (IS_ERR(key)) {
pr_err("Problem with revocation key (%ld)\n", PTR_ERR(key));
@@ -194,30 +296,57 @@ int is_key_on_revocation_list(struct pkcs7_message *pkcs7)
}
#endif
+static int restrict_link_for_blacklist(struct key *dest_keyring,
+ const struct key_type *type, const union key_payload *payload,
+ struct key *restrict_key)
+{
+ if (type == &key_type_blacklist)
+ return 0;
+ return -EOPNOTSUPP;
+}
+
/*
* Initialise the blacklist
+ *
+ * The blacklist_init() function is registered as an initcall via
+ * device_initcall(). As a result if the blacklist_init() function fails for
+ * any reason the kernel continues to execute. While cleanly returning -ENODEV
+ * could be acceptable for some non-critical kernel parts, if the blacklist
+ * keyring fails to load it defeats the certificate/key based deny list for
+ * signed modules. If a critical piece of security functionality that users
+ * expect to be present fails to initialize, panic()ing is likely the right
+ * thing to do.
*/
static int __init blacklist_init(void)
{
const char *const *bl;
+ struct key_restriction *restriction;
if (register_key_type(&key_type_blacklist) < 0)
panic("Can't allocate system blacklist key type\n");
+ restriction = kzalloc(sizeof(*restriction), GFP_KERNEL);
+ if (!restriction)
+ panic("Can't allocate blacklist keyring restriction\n");
+ restriction->check = restrict_link_for_blacklist;
+
blacklist_keyring =
keyring_alloc(".blacklist",
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(),
- (KEY_POS_ALL & ~KEY_POS_SETATTR) |
- KEY_USR_VIEW | KEY_USR_READ |
- KEY_USR_SEARCH,
- KEY_ALLOC_NOT_IN_QUOTA |
+ KEY_POS_VIEW | KEY_POS_READ | KEY_POS_SEARCH |
+ KEY_POS_WRITE |
+ KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH
+#ifdef CONFIG_SYSTEM_BLACKLIST_AUTH_UPDATE
+ | KEY_USR_WRITE
+#endif
+ , KEY_ALLOC_NOT_IN_QUOTA |
KEY_ALLOC_SET_KEEP,
- NULL, NULL);
+ restriction, NULL);
if (IS_ERR(blacklist_keyring))
panic("Can't allocate system blacklist keyring\n");
for (bl = blacklist_hashes; *bl; bl++)
- if (mark_hash_blacklisted(*bl) < 0)
+ if (mark_raw_hash_blacklisted(*bl) < 0)
pr_err("- blacklisting failed\n");
return 0;
}
diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c
index 91a4ad50dea2..77ed4e93ad56 100644
--- a/crypto/asymmetric_keys/x509_public_key.c
+++ b/crypto/asymmetric_keys/x509_public_key.c
@@ -69,7 +69,8 @@ int x509_get_sig_params(struct x509_certificate *cert)
if (ret < 0)
goto error_2;
- ret = is_hash_blacklisted(sig->digest, sig->digest_size, "tbs");
+ ret = is_hash_blacklisted(sig->digest, sig->digest_size,
+ BLACKLIST_HASH_X509_TBS);
if (ret == -EKEYREJECTED) {
pr_err("Cert %*phN is blacklisted\n",
sig->digest_size, sig->digest);
diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c
index 4704fa553098..c1eb5d223839 100644
--- a/drivers/char/tpm/tpm2-cmd.c
+++ b/drivers/char/tpm/tpm2-cmd.c
@@ -400,7 +400,16 @@ ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value,
if (!rc) {
out = (struct tpm2_get_cap_out *)
&buf.data[TPM_HEADER_SIZE];
- *value = be32_to_cpu(out->value);
+ /*
+ * To prevent failing boot up of some systems, Infineon TPM2.0
+ * returns SUCCESS on TPM2_Startup in field upgrade mode. Also
+ * the TPM2_Getcapability command returns a zero length list
+ * in field upgrade mode.
+ */
+ if (be32_to_cpu(out->property_cnt) > 0)
+ *value = be32_to_cpu(out->value);
+ else
+ rc = -ENODATA;
}
tpm_buf_destroy(&buf);
return rc;
@@ -745,7 +754,11 @@ int tpm2_auto_startup(struct tpm_chip *chip)
rc = tpm2_get_cc_attrs_tbl(chip);
out:
- if (rc == TPM2_RC_UPGRADE) {
+ /*
+ * Infineon TPM in field upgrade mode will return no data for the number
+ * of supported commands.
+ */
+ if (rc == TPM2_RC_UPGRADE || rc == -ENODATA) {
dev_info(&chip->dev, "TPM in field upgrade mode, requires firmware upgrade\n");
chip->flags |= TPM_CHIP_FLAG_FIRMWARE_UPGRADE;
rc = 0;
diff --git a/drivers/char/tpm/tpm_ftpm_tee.c b/drivers/char/tpm/tpm_ftpm_tee.c
index 6e3235565a4d..5c233423c56f 100644
--- a/drivers/char/tpm/tpm_ftpm_tee.c
+++ b/drivers/char/tpm/tpm_ftpm_tee.c
@@ -177,7 +177,7 @@ static u8 ftpm_tee_tpm_op_status(struct tpm_chip *chip)
static bool ftpm_tee_tpm_req_canceled(struct tpm_chip *chip, u8 status)
{
- return 0;
+ return false;
}
static const struct tpm_class_ops ftpm_tee_tpm_ops = {
diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c
index 3af4c07a9342..d3989b257f42 100644
--- a/drivers/char/tpm/tpm_ibmvtpm.c
+++ b/drivers/char/tpm/tpm_ibmvtpm.c
@@ -681,6 +681,7 @@ static int tpm_ibmvtpm_probe(struct vio_dev *vio_dev,
if (!wait_event_timeout(ibmvtpm->crq_queue.wq,
ibmvtpm->rtce_buf != NULL,
HZ)) {
+ rc = -ENODEV;
dev_err(dev, "CRQ response timed out\n");
goto init_irq_cleanup;
}
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index d3f2e5364c27..bcff6429e0b4 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -153,50 +153,46 @@ static int check_acpi_tpm2(struct device *dev)
#endif
static int tpm_tcg_read_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
- u8 *result)
+ u8 *result, enum tpm_tis_io_mode io_mode)
{
struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
-
- while (len--)
- *result++ = ioread8(phy->iobase + addr);
+ __le16 result_le16;
+ __le32 result_le32;
+
+ switch (io_mode) {
+ case TPM_TIS_PHYS_8:
+ while (len--)
+ *result++ = ioread8(phy->iobase + addr);
+ break;
+ case TPM_TIS_PHYS_16:
+ result_le16 = cpu_to_le16(ioread16(phy->iobase + addr));
+ memcpy(result, &result_le16, sizeof(u16));
+ break;
+ case TPM_TIS_PHYS_32:
+ result_le32 = cpu_to_le32(ioread32(phy->iobase + addr));
+ memcpy(result, &result_le32, sizeof(u32));
+ break;
+ }
return 0;
}
static int tpm_tcg_write_bytes(struct tpm_tis_data *data, u32 addr, u16 len,
- const u8 *value)
+ const u8 *value, enum tpm_tis_io_mode io_mode)
{
struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
- while (len--)
- iowrite8(*value++, phy->iobase + addr);
-
- return 0;
-}
-
-static int tpm_tcg_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
-{
- struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
-
- *result = ioread16(phy->iobase + addr);
-
- return 0;
-}
-
-static int tpm_tcg_read32(struct tpm_tis_data *data, u32 addr, u32 *result)
-{
- struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
-
- *result = ioread32(phy->iobase + addr);
-
- return 0;
-}
-
-static int tpm_tcg_write32(struct tpm_tis_data *data, u32 addr, u32 value)
-{
- struct tpm_tis_tcg_phy *phy = to_tpm_tis_tcg_phy(data);
-
- iowrite32(value, phy->iobase + addr);
+ switch (io_mode) {
+ case TPM_TIS_PHYS_8:
+ while (len--)
+ iowrite8(*value++, phy->iobase + addr);
+ break;
+ case TPM_TIS_PHYS_16:
+ return -EINVAL;
+ case TPM_TIS_PHYS_32:
+ iowrite32(le32_to_cpu(*((__le32 *)value)), phy->iobase + addr);
+ break;
+ }
return 0;
}
@@ -204,9 +200,6 @@ static int tpm_tcg_write32(struct tpm_tis_data *data, u32 addr, u32 value)
static const struct tpm_tis_phy_ops tpm_tcg = {
.read_bytes = tpm_tcg_read_bytes,
.write_bytes = tpm_tcg_write_bytes,
- .read16 = tpm_tcg_read16,
- .read32 = tpm_tcg_read32,
- .write32 = tpm_tcg_write32,
};
static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info)
diff --git a/drivers/char/tpm/tpm_tis_core.h b/drivers/char/tpm/tpm_tis_core.h
index 3be24f221e32..6c203f36b8a1 100644
--- a/drivers/char/tpm/tpm_tis_core.h
+++ b/drivers/char/tpm/tpm_tis_core.h
@@ -104,54 +104,88 @@ struct tpm_tis_data {
unsigned int timeout_max; /* usecs */
};
+/*
+ * IO modes to indicate how many bytes should be read/written at once in the
+ * tpm_tis_phy_ops read_bytes/write_bytes calls. Use TPM_TIS_PHYS_8 to
+ * receive/transmit byte-wise, TPM_TIS_PHYS_16 for two bytes etc.
+ */
+enum tpm_tis_io_mode {
+ TPM_TIS_PHYS_8,
+ TPM_TIS_PHYS_16,
+ TPM_TIS_PHYS_32,
+};
+
struct tpm_tis_phy_ops {
+ /* data is passed in little endian */
int (*read_bytes)(struct tpm_tis_data *data, u32 addr, u16 len,
- u8 *result);
+ u8 *result, enum tpm_tis_io_mode mode);
int (*write_bytes)(struct tpm_tis_data *data, u32 addr, u16 len,
- const u8 *value);
- int (*read16)(struct tpm_tis_data *data, u32 addr, u16 *result);
- int (*read32)(struct tpm_tis_data *data, u32 addr, u32 *result);
- int (*write32)(struct tpm_tis_data *data, u32 addr, u32 src);
+ const u8 *value, enum tpm_tis_io_mode mode);
};
static inline int tpm_tis_read_bytes(struct tpm_tis_data *data, u32 addr,
u16 len, u8 *result)
{
- return data->phy_ops->read_bytes(data, addr, len, result);
+ return data->phy_ops->read_bytes(data, addr, len, result,
+ TPM_TIS_PHYS_8);
}
static inline int tpm_tis_read8(struct tpm_tis_data *data, u32 addr, u8 *result)
{
- return data->phy_ops->read_bytes(data, addr, 1, result);
+ return data->phy_ops->read_bytes(data, addr, 1, result, TPM_TIS_PHYS_8);
}
static inline int tpm_tis_read16(struct tpm_tis_data *data, u32 addr,
u16 *result)
{
- return data->phy_ops->read16(data, addr, result);
+ __le16 result_le;
+ int rc;
+
+ rc = data->phy_ops->read_bytes(data, addr, sizeof(u16),
+ (u8 *)&result_le, TPM_TIS_PHYS_16);
+ if (!rc)
+ *result = le16_to_cpu(result_le);
+
+ return rc;
}
static inline int tpm_tis_read32(struct tpm_tis_data *data, u32 addr,
u32 *result)
{
- return data->phy_ops->read32(data, addr, result);
+ __le32 result_le;
+ int rc;
+
+ rc = data->phy_ops->read_bytes(data, addr, sizeof(u32),
+ (u8 *)&result_le, TPM_TIS_PHYS_32);
+ if (!rc)
+ *result = le32_to_cpu(result_le);
+
+ return rc;
}
static inline int tpm_tis_write_bytes(struct tpm_tis_data *data, u32 addr,
u16 len, const u8 *value)
{
- return data->phy_ops->write_bytes(data, addr, len, value);
+ return data->phy_ops->write_bytes(data, addr, len, value,
+ TPM_TIS_PHYS_8);
}
static inline int tpm_tis_write8(struct tpm_tis_data *data, u32 addr, u8 value)
{
- return data->phy_ops->write_bytes(data, addr, 1, &value);
+ return data->phy_ops->write_bytes(data, addr, 1, &value,
+ TPM_TIS_PHYS_8);
}
static inline int tpm_tis_write32(struct tpm_tis_data *data, u32 addr,
u32 value)
{
- return data->phy_ops->write32(data, addr, value);
+ __le32 value_le;
+ int rc;
+
+ value_le = cpu_to_le32(value);
+ rc = data->phy_ops->write_bytes(data, addr, sizeof(u32),
+ (u8 *)&value_le, TPM_TIS_PHYS_32);
+ return rc;
}
static inline bool is_bsw(void)
diff --git a/drivers/char/tpm/tpm_tis_i2c_cr50.c b/drivers/char/tpm/tpm_tis_i2c_cr50.c
index f6c0affbb456..974479a1ec5a 100644
--- a/drivers/char/tpm/tpm_tis_i2c_cr50.c
+++ b/drivers/char/tpm/tpm_tis_i2c_cr50.c
@@ -31,6 +31,7 @@
#define TPM_CR50_TIMEOUT_SHORT_MS 2 /* Short timeout during transactions */
#define TPM_CR50_TIMEOUT_NOIRQ_MS 20 /* Timeout for TPM ready without IRQ */
#define TPM_CR50_I2C_DID_VID 0x00281ae0L /* Device and vendor ID reg value */
+#define TPM_TI50_I2C_DID_VID 0x504a6666L /* Device and vendor ID reg value */
#define TPM_CR50_I2C_MAX_RETRIES 3 /* Max retries due to I2C errors */
#define TPM_CR50_I2C_RETRY_DELAY_LO 55 /* Min usecs between retries on I2C */
#define TPM_CR50_I2C_RETRY_DELAY_HI 65 /* Max usecs between retries on I2C */
@@ -742,15 +743,15 @@ static int tpm_cr50_i2c_probe(struct i2c_client *client)
}
vendor = le32_to_cpup((__le32 *)buf);
- if (vendor != TPM_CR50_I2C_DID_VID) {
+ if (vendor != TPM_CR50_I2C_DID_VID && vendor != TPM_TI50_I2C_DID_VID) {
dev_err(dev, "Vendor ID did not match! ID was %08x\n", vendor);
tpm_cr50_release_locality(chip, true);
return -ENODEV;
}
- dev_info(dev, "cr50 TPM 2.0 (i2c 0x%02x irq %d id 0x%x)\n",
+ dev_info(dev, "%s TPM 2.0 (i2c 0x%02x irq %d id 0x%x)\n",
+ vendor == TPM_TI50_I2C_DID_VID ? "ti50" : "cr50",
client->addr, client->irq, vendor >> 16);
-
return tpm_chip_register(chip);
}
@@ -768,8 +769,8 @@ static int tpm_cr50_i2c_remove(struct i2c_client *client)
struct device *dev = &client->dev;
if (!chip) {
- dev_err(dev, "Could not get client data at remove\n");
- return -ENODEV;
+ dev_crit(dev, "Could not get client data at remove, memory corruption ahead\n");
+ return 0;
}
tpm_chip_unregister(chip);
diff --git a/drivers/char/tpm/tpm_tis_spi.h b/drivers/char/tpm/tpm_tis_spi.h
index bba73979c368..d0f66f6f1931 100644
--- a/drivers/char/tpm/tpm_tis_spi.h
+++ b/drivers/char/tpm/tpm_tis_spi.h
@@ -31,10 +31,6 @@ extern int tpm_tis_spi_init(struct spi_device *spi, struct tpm_tis_spi_phy *phy,
extern int tpm_tis_spi_transfer(struct tpm_tis_data *data, u32 addr, u16 len,
u8 *in, const u8 *out);
-extern int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result);
-extern int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result);
-extern int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value);
-
#ifdef CONFIG_TCG_TIS_SPI_CR50
extern int cr50_spi_probe(struct spi_device *spi);
#else
diff --git a/drivers/char/tpm/tpm_tis_spi_cr50.c b/drivers/char/tpm/tpm_tis_spi_cr50.c
index 7bf123d3c537..f4937280e940 100644
--- a/drivers/char/tpm/tpm_tis_spi_cr50.c
+++ b/drivers/char/tpm/tpm_tis_spi_cr50.c
@@ -222,13 +222,13 @@ static int tpm_tis_spi_cr50_transfer(struct tpm_tis_data *data, u32 addr, u16 le
}
static int tpm_tis_spi_cr50_read_bytes(struct tpm_tis_data *data, u32 addr,
- u16 len, u8 *result)
+ u16 len, u8 *result, enum tpm_tis_io_mode io_mode)
{
return tpm_tis_spi_cr50_transfer(data, addr, len, result, NULL);
}
static int tpm_tis_spi_cr50_write_bytes(struct tpm_tis_data *data, u32 addr,
- u16 len, const u8 *value)
+ u16 len, const u8 *value, enum tpm_tis_io_mode io_mode)
{
return tpm_tis_spi_cr50_transfer(data, addr, len, NULL, value);
}
@@ -236,9 +236,6 @@ static int tpm_tis_spi_cr50_write_bytes(struct tpm_tis_data *data, u32 addr,
static const struct tpm_tis_phy_ops tpm_spi_cr50_phy_ops = {
.read_bytes = tpm_tis_spi_cr50_read_bytes,
.write_bytes = tpm_tis_spi_cr50_write_bytes,
- .read16 = tpm_tis_spi_read16,
- .read32 = tpm_tis_spi_read32,
- .write32 = tpm_tis_spi_write32,
};
static void cr50_print_fw_version(struct tpm_tis_data *data)
diff --git a/drivers/char/tpm/tpm_tis_spi_main.c b/drivers/char/tpm/tpm_tis_spi_main.c
index 184396b3af50..a0963a3e92bd 100644
--- a/drivers/char/tpm/tpm_tis_spi_main.c
+++ b/drivers/char/tpm/tpm_tis_spi_main.c
@@ -141,55 +141,17 @@ exit:
}
static int tpm_tis_spi_read_bytes(struct tpm_tis_data *data, u32 addr,
- u16 len, u8 *result)
+ u16 len, u8 *result, enum tpm_tis_io_mode io_mode)
{
return tpm_tis_spi_transfer(data, addr, len, result, NULL);
}
static int tpm_tis_spi_write_bytes(struct tpm_tis_data *data, u32 addr,
- u16 len, const u8 *value)
+ u16 len, const u8 *value, enum tpm_tis_io_mode io_mode)
{
return tpm_tis_spi_transfer(data, addr, len, NULL, value);
}
-int tpm_tis_spi_read16(struct tpm_tis_data *data, u32 addr, u16 *result)
-{
- __le16 result_le;
- int rc;
-
- rc = data->phy_ops->read_bytes(data, addr, sizeof(u16),
- (u8 *)&result_le);
- if (!rc)
- *result = le16_to_cpu(result_le);
-
- return rc;
-}
-
-int tpm_tis_spi_read32(struct tpm_tis_data *data, u32 addr, u32 *result)
-{
- __le32 result_le;
- int rc;
-
- rc = data->phy_ops->read_bytes(data, addr, sizeof(u32),
- (u8 *)&result_le);
- if (!rc)
- *result = le32_to_cpu(result_le);
-
- return rc;
-}
-
-int tpm_tis_spi_write32(struct tpm_tis_data *data, u32 addr, u32 value)
-{
- __le32 value_le;
- int rc;
-
- value_le = cpu_to_le32(value);
- rc = data->phy_ops->write_bytes(data, addr, sizeof(u32),
- (u8 *)&value_le);
-
- return rc;
-}
-
int tpm_tis_spi_init(struct spi_device *spi, struct tpm_tis_spi_phy *phy,
int irq, const struct tpm_tis_phy_ops *phy_ops)
{
@@ -205,9 +167,6 @@ int tpm_tis_spi_init(struct spi_device *spi, struct tpm_tis_spi_phy *phy,
static const struct tpm_tis_phy_ops tpm_spi_phy_ops = {
.read_bytes = tpm_tis_spi_read_bytes,
.write_bytes = tpm_tis_spi_write_bytes,
- .read16 = tpm_tis_spi_read16,
- .read32 = tpm_tis_spi_read32,
- .write32 = tpm_tis_spi_write32,
};
static int tpm_tis_spi_probe(struct spi_device *dev)
diff --git a/drivers/char/tpm/tpm_tis_synquacer.c b/drivers/char/tpm/tpm_tis_synquacer.c
index e47bdd272704..679196c61401 100644
--- a/drivers/char/tpm/tpm_tis_synquacer.c
+++ b/drivers/char/tpm/tpm_tis_synquacer.c
@@ -35,72 +35,53 @@ static inline struct tpm_tis_synquacer_phy *to_tpm_tis_tcg_phy(struct tpm_tis_da
}
static int tpm_tis_synquacer_read_bytes(struct tpm_tis_data *data, u32 addr,
- u16 len, u8 *result)
+ u16 len, u8 *result,
+ enum tpm_tis_io_mode io_mode)
{
struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data);
-
- while (len--)
- *result++ = ioread8(phy->iobase + addr);
+ switch (io_mode) {
+ case TPM_TIS_PHYS_8:
+ while (len--)
+ *result++ = ioread8(phy->iobase + addr);
+ break;
+ case TPM_TIS_PHYS_16:
+ result[1] = ioread8(phy->iobase + addr + 1);
+ result[0] = ioread8(phy->iobase + addr);
+ break;
+ case TPM_TIS_PHYS_32:
+ result[3] = ioread8(phy->iobase + addr + 3);
+ result[2] = ioread8(phy->iobase + addr + 2);
+ result[1] = ioread8(phy->iobase + addr + 1);
+ result[0] = ioread8(phy->iobase + addr);
+ break;
+ }
return 0;
}
static int tpm_tis_synquacer_write_bytes(struct tpm_tis_data *data, u32 addr,
- u16 len, const u8 *value)
+ u16 len, const u8 *value,
+ enum tpm_tis_io_mode io_mode)
{
struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data);
-
- while (len--)
- iowrite8(*value++, phy->iobase + addr);
-
- return 0;
-}
-
-static int tpm_tis_synquacer_read16_bw(struct tpm_tis_data *data,
- u32 addr, u16 *result)
-{
- struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data);
-
- /*
- * Due to the limitation of SPI controller on SynQuacer,
- * 16/32 bits access must be done in byte-wise and descending order.
- */
- *result = (ioread8(phy->iobase + addr + 1) << 8) |
- (ioread8(phy->iobase + addr));
-
- return 0;
-}
-
-static int tpm_tis_synquacer_read32_bw(struct tpm_tis_data *data,
- u32 addr, u32 *result)
-{
- struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data);
-
- /*
- * Due to the limitation of SPI controller on SynQuacer,
- * 16/32 bits access must be done in byte-wise and descending order.
- */
- *result = (ioread8(phy->iobase + addr + 3) << 24) |
- (ioread8(phy->iobase + addr + 2) << 16) |
- (ioread8(phy->iobase + addr + 1) << 8) |
- (ioread8(phy->iobase + addr));
-
- return 0;
-}
-
-static int tpm_tis_synquacer_write32_bw(struct tpm_tis_data *data,
- u32 addr, u32 value)
-{
- struct tpm_tis_synquacer_phy *phy = to_tpm_tis_tcg_phy(data);
-
- /*
- * Due to the limitation of SPI controller on SynQuacer,
- * 16/32 bits access must be done in byte-wise and descending order.
- */
- iowrite8(value >> 24, phy->iobase + addr + 3);
- iowrite8(value >> 16, phy->iobase + addr + 2);
- iowrite8(value >> 8, phy->iobase + addr + 1);
- iowrite8(value, phy->iobase + addr);
+ switch (io_mode) {
+ case TPM_TIS_PHYS_8:
+ while (len--)
+ iowrite8(*value++, phy->iobase + addr);
+ break;
+ case TPM_TIS_PHYS_16:
+ return -EINVAL;
+ case TPM_TIS_PHYS_32:
+ /*
+ * Due to the limitation of SPI controller on SynQuacer,
+ * 16/32 bits access must be done in byte-wise and descending order.
+ */
+ iowrite8(value[3], phy->iobase + addr + 3);
+ iowrite8(value[2], phy->iobase + addr + 2);
+ iowrite8(value[1], phy->iobase + addr + 1);
+ iowrite8(value[0], phy->iobase + addr);
+ break;
+ }
return 0;
}
@@ -108,9 +89,6 @@ static int tpm_tis_synquacer_write32_bw(struct tpm_tis_data *data,
static const struct tpm_tis_phy_ops tpm_tcg_bw = {
.read_bytes = tpm_tis_synquacer_read_bytes,
.write_bytes = tpm_tis_synquacer_write_bytes,
- .read16 = tpm_tis_synquacer_read16_bw,
- .read32 = tpm_tis_synquacer_read32_bw,
- .write32 = tpm_tis_synquacer_write32_bw,
};
static int tpm_tis_synquacer_init(struct device *dev,
diff --git a/drivers/crypto/caam/Kconfig b/drivers/crypto/caam/Kconfig
index 84ea7cba5ee5..ea9f8b1ae981 100644
--- a/drivers/crypto/caam/Kconfig
+++ b/drivers/crypto/caam/Kconfig
@@ -151,6 +151,9 @@ config CRYPTO_DEV_FSL_CAAM_RNG_API
Selecting this will register the SEC4 hardware rng to
the hw_random API for supplying the kernel entropy pool.
+config CRYPTO_DEV_FSL_CAAM_BLOB_GEN
+ bool
+
endif # CRYPTO_DEV_FSL_CAAM_JR
endif # CRYPTO_DEV_FSL_CAAM
diff --git a/drivers/crypto/caam/Makefile b/drivers/crypto/caam/Makefile
index 3570286eb9ce..25f7ae5a4642 100644
--- a/drivers/crypto/caam/Makefile
+++ b/drivers/crypto/caam/Makefile
@@ -21,6 +21,7 @@ caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI) += caamalg_qi.o
caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_AHASH_API) += caamhash.o
caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_RNG_API) += caamrng.o
caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_PKC_API) += caampkc.o pkc_desc.o
+caam_jr-$(CONFIG_CRYPTO_DEV_FSL_CAAM_BLOB_GEN) += blob_gen.o
caam-$(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI) += qi.o
ifneq ($(CONFIG_CRYPTO_DEV_FSL_CAAM_CRYPTO_API_QI),)
diff --git a/drivers/crypto/caam/blob_gen.c b/drivers/crypto/caam/blob_gen.c
new file mode 100644
index 000000000000..6345c7269eb0
--- /dev/null
+++ b/drivers/crypto/caam/blob_gen.c
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2015 Pengutronix, Steffen Trumtrar <kernel@pengutronix.de>
+ * Copyright (C) 2021 Pengutronix, Ahmad Fatoum <kernel@pengutronix.de>
+ */
+
+#define pr_fmt(fmt) "caam blob_gen: " fmt
+
+#include <linux/device.h>
+#include <soc/fsl/caam-blob.h>
+
+#include "compat.h"
+#include "desc_constr.h"
+#include "desc.h"
+#include "error.h"
+#include "intern.h"
+#include "jr.h"
+#include "regs.h"
+
+#define CAAM_BLOB_DESC_BYTES_MAX \
+ /* Command to initialize & stating length of descriptor */ \
+ (CAAM_CMD_SZ + \
+ /* Command to append the key-modifier + key-modifier data */ \
+ CAAM_CMD_SZ + CAAM_BLOB_KEYMOD_LENGTH + \
+ /* Command to include input key + pointer to the input key */ \
+ CAAM_CMD_SZ + CAAM_PTR_SZ_MAX + \
+ /* Command to include output key + pointer to the output key */ \
+ CAAM_CMD_SZ + CAAM_PTR_SZ_MAX + \
+ /* Command describing the operation to perform */ \
+ CAAM_CMD_SZ)
+
+struct caam_blob_priv {
+ struct device jrdev;
+};
+
+struct caam_blob_job_result {
+ int err;
+ struct completion completion;
+};
+
+static void caam_blob_job_done(struct device *dev, u32 *desc, u32 err, void *context)
+{
+ struct caam_blob_job_result *res = context;
+ int ecode = 0;
+
+ dev_dbg(dev, "%s %d: err 0x%x\n", __func__, __LINE__, err);
+
+ if (err)
+ ecode = caam_jr_strstatus(dev, err);
+
+ res->err = ecode;
+
+ /*
+ * Upon completion, desc points to a buffer containing a CAAM job
+ * descriptor which encapsulates data into an externally-storable
+ * blob.
+ */
+ complete(&res->completion);
+}
+
+int caam_process_blob(struct caam_blob_priv *priv,
+ struct caam_blob_info *info, bool encap)
+{
+ struct caam_blob_job_result testres;
+ struct device *jrdev = &priv->jrdev;
+ dma_addr_t dma_in, dma_out;
+ int op = OP_PCLID_BLOB;
+ size_t output_len;
+ u32 *desc;
+ int ret;
+
+ if (info->key_mod_len > CAAM_BLOB_KEYMOD_LENGTH)
+ return -EINVAL;
+
+ if (encap) {
+ op |= OP_TYPE_ENCAP_PROTOCOL;
+ output_len = info->input_len + CAAM_BLOB_OVERHEAD;
+ } else {
+ op |= OP_TYPE_DECAP_PROTOCOL;
+ output_len = info->input_len - CAAM_BLOB_OVERHEAD;
+ }
+
+ desc = kzalloc(CAAM_BLOB_DESC_BYTES_MAX, GFP_KERNEL | GFP_DMA);
+ if (!desc)
+ return -ENOMEM;
+
+ dma_in = dma_map_single(jrdev, info->input, info->input_len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(jrdev, dma_in)) {
+ dev_err(jrdev, "unable to map input DMA buffer\n");
+ ret = -ENOMEM;
+ goto out_free;
+ }
+
+ dma_out = dma_map_single(jrdev, info->output, output_len,
+ DMA_FROM_DEVICE);
+ if (dma_mapping_error(jrdev, dma_out)) {
+ dev_err(jrdev, "unable to map output DMA buffer\n");
+ ret = -ENOMEM;
+ goto out_unmap_in;
+ }
+
+ /*
+ * A data blob is encrypted using a blob key (BK); a random number.
+ * The BK is used as an AES-CCM key. The initial block (B0) and the
+ * initial counter (Ctr0) are generated automatically and stored in
+ * Class 1 Context DWords 0+1+2+3. The random BK is stored in the
+ * Class 1 Key Register. Operation Mode is set to AES-CCM.
+ */
+
+ init_job_desc(desc, 0);
+ append_key_as_imm(desc, info->key_mod, info->key_mod_len,
+ info->key_mod_len, CLASS_2 | KEY_DEST_CLASS_REG);
+ append_seq_in_ptr_intlen(desc, dma_in, info->input_len, 0);
+ append_seq_out_ptr_intlen(desc, dma_out, output_len, 0);
+ append_operation(desc, op);
+
+ print_hex_dump_debug("data@"__stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 1, info->input,
+ info->input_len, false);
+ print_hex_dump_debug("jobdesc@"__stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 1, desc,
+ desc_bytes(desc), false);
+
+ testres.err = 0;
+ init_completion(&testres.completion);
+
+ ret = caam_jr_enqueue(jrdev, desc, caam_blob_job_done, &testres);
+ if (ret == -EINPROGRESS) {
+ wait_for_completion(&testres.completion);
+ ret = testres.err;
+ print_hex_dump_debug("output@"__stringify(__LINE__)": ",
+ DUMP_PREFIX_ADDRESS, 16, 1, info->output,
+ output_len, false);
+ }
+
+ if (ret == 0)
+ info->output_len = output_len;
+
+ dma_unmap_single(jrdev, dma_out, output_len, DMA_FROM_DEVICE);
+out_unmap_in:
+ dma_unmap_single(jrdev, dma_in, info->input_len, DMA_TO_DEVICE);
+out_free:
+ kfree(desc);
+
+ return ret;
+}
+EXPORT_SYMBOL(caam_process_blob);
+
+struct caam_blob_priv *caam_blob_gen_init(void)
+{
+ struct caam_drv_private *ctrlpriv;
+ struct device *jrdev;
+
+ /*
+ * caam_blob_gen_init() may expectedly fail with -ENODEV, e.g. when
+ * CAAM driver didn't probe or when SoC lacks BLOB support. An
+ * error would be harsh in this case, so we stick to info level.
+ */
+
+ jrdev = caam_jr_alloc();
+ if (IS_ERR(jrdev)) {
+ pr_info("job ring requested, but none currently available\n");
+ return ERR_PTR(-ENODEV);
+ }
+
+ ctrlpriv = dev_get_drvdata(jrdev->parent);
+ if (!ctrlpriv->blob_present) {
+ dev_info(jrdev, "no hardware blob generation support\n");
+ caam_jr_free(jrdev);
+ return ERR_PTR(-ENODEV);
+ }
+
+ return container_of(jrdev, struct caam_blob_priv, jrdev);
+}
+EXPORT_SYMBOL(caam_blob_gen_init);
+
+void caam_blob_gen_exit(struct caam_blob_priv *priv)
+{
+ caam_jr_free(&priv->jrdev);
+}
+EXPORT_SYMBOL(caam_blob_gen_exit);
diff --git a/drivers/crypto/caam/ctrl.c b/drivers/crypto/caam/ctrl.c
index ca0361b2dbb0..38c4d88a9d03 100644
--- a/drivers/crypto/caam/ctrl.c
+++ b/drivers/crypto/caam/ctrl.c
@@ -820,12 +820,25 @@ static int caam_probe(struct platform_device *pdev)
return -ENOMEM;
}
- if (ctrlpriv->era < 10)
+ comp_params = rd_reg32(&ctrl->perfmon.comp_parms_ls);
+ ctrlpriv->blob_present = !!(comp_params & CTPR_LS_BLOB);
+
+ /*
+ * Some SoCs like the LS1028A (non-E) indicate CTPR_LS_BLOB support,
+ * but fail when actually using it due to missing AES support, so
+ * check both here.
+ */
+ if (ctrlpriv->era < 10) {
rng_vid = (rd_reg32(&ctrl->perfmon.cha_id_ls) &
CHA_ID_LS_RNG_MASK) >> CHA_ID_LS_RNG_SHIFT;
- else
+ ctrlpriv->blob_present = ctrlpriv->blob_present &&
+ (rd_reg32(&ctrl->perfmon.cha_num_ls) & CHA_ID_LS_AES_MASK);
+ } else {
rng_vid = (rd_reg32(&ctrl->vreg.rng) & CHA_VER_VID_MASK) >>
CHA_VER_VID_SHIFT;
+ ctrlpriv->blob_present = ctrlpriv->blob_present &&
+ (rd_reg32(&ctrl->vreg.aesa) & CHA_VER_MISC_AES_NUM_MASK);
+ }
/*
* If SEC has RNG version >= 4 and RNG state handle has not been
diff --git a/drivers/crypto/caam/intern.h b/drivers/crypto/caam/intern.h
index 7d45b21bd55a..e92210e2ab76 100644
--- a/drivers/crypto/caam/intern.h
+++ b/drivers/crypto/caam/intern.h
@@ -92,6 +92,7 @@ struct caam_drv_private {
*/
u8 total_jobrs; /* Total Job Rings in device */
u8 qi_present; /* Nonzero if QI present in device */
+ u8 blob_present; /* Nonzero if BLOB support present in device */
u8 mc_en; /* Nonzero if MC f/w is active */
int secvio_irq; /* Security violation interrupt number */
int virt_en; /* Virtualization enabled in CAAM */
diff --git a/drivers/crypto/caam/regs.h b/drivers/crypto/caam/regs.h
index 3738625c0250..66d6dad841bb 100644
--- a/drivers/crypto/caam/regs.h
+++ b/drivers/crypto/caam/regs.h
@@ -320,7 +320,8 @@ struct version_regs {
#define CHA_VER_VID_MASK (0xffull << CHA_VER_VID_SHIFT)
/* CHA Miscellaneous Information - AESA_MISC specific */
-#define CHA_VER_MISC_AES_GCM BIT(1 + CHA_VER_MISC_SHIFT)
+#define CHA_VER_MISC_AES_NUM_MASK GENMASK(7, 0)
+#define CHA_VER_MISC_AES_GCM BIT(1 + CHA_VER_MISC_SHIFT)
/* CHA Miscellaneous Information - PKHA_MISC specific */
#define CHA_VER_MISC_PKHA_NO_CRYPT BIT(7 + CHA_VER_MISC_SHIFT)
@@ -414,6 +415,7 @@ struct caam_perfmon {
#define CTPR_MS_PG_SZ_MASK 0x10
#define CTPR_MS_PG_SZ_SHIFT 4
u32 comp_parms_ms; /* CTPR - Compile Parameters Register */
+#define CTPR_LS_BLOB BIT(1)
u32 comp_parms_ls; /* CTPR - Compile Parameters Register */
u64 rsvd1[2];
diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h
index 2419a735420f..91e080efb918 100644
--- a/include/keys/system_keyring.h
+++ b/include/keys/system_keyring.h
@@ -10,6 +10,13 @@
#include <linux/key.h>
+enum blacklist_hash_type {
+ /* TBSCertificate hash */
+ BLACKLIST_HASH_X509_TBS = 1,
+ /* Raw data hash */
+ BLACKLIST_HASH_BINARY = 2,
+};
+
#ifdef CONFIG_SYSTEM_TRUSTED_KEYRING
extern int restrict_link_by_builtin_trusted(struct key *keyring,
@@ -54,13 +61,14 @@ static inline void __init set_machine_trusted_keys(struct key *keyring)
extern struct pkcs7_message *pkcs7;
#ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING
-extern int mark_hash_blacklisted(const char *hash);
+extern int mark_hash_blacklisted(const u8 *hash, size_t hash_len,
+ enum blacklist_hash_type hash_type);
extern int is_hash_blacklisted(const u8 *hash, size_t hash_len,
- const char *type);
+ enum blacklist_hash_type hash_type);
extern int is_binary_blacklisted(const u8 *hash, size_t hash_len);
#else
static inline int is_hash_blacklisted(const u8 *hash, size_t hash_len,
- const char *type)
+ enum blacklist_hash_type hash_type)
{
return 0;
}
diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h
index d89fa2579ac0..4eb64548a74f 100644
--- a/include/keys/trusted-type.h
+++ b/include/keys/trusted-type.h
@@ -64,7 +64,7 @@ struct trusted_key_ops {
/* Unseal a key. */
int (*unseal)(struct trusted_key_payload *p, char *datablob);
- /* Get a randomized key. */
+ /* Optional: Get a randomized key. */
int (*get_random)(unsigned char *key, size_t key_len);
/* Exit key interface. */
diff --git a/include/keys/trusted_caam.h b/include/keys/trusted_caam.h
new file mode 100644
index 000000000000..73fe2f32f65e
--- /dev/null
+++ b/include/keys/trusted_caam.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2021 Pengutronix, Ahmad Fatoum <kernel@pengutronix.de>
+ */
+
+#ifndef __CAAM_TRUSTED_KEY_H
+#define __CAAM_TRUSTED_KEY_H
+
+extern struct trusted_key_ops trusted_key_caam_ops;
+
+#endif
diff --git a/include/soc/fsl/caam-blob.h b/include/soc/fsl/caam-blob.h
new file mode 100644
index 000000000000..937cac52f36d
--- /dev/null
+++ b/include/soc/fsl/caam-blob.h
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020 Pengutronix, Ahmad Fatoum <kernel@pengutronix.de>
+ */
+
+#ifndef __CAAM_BLOB_GEN
+#define __CAAM_BLOB_GEN
+
+#include <linux/types.h>
+#include <linux/errno.h>
+
+#define CAAM_BLOB_KEYMOD_LENGTH 16
+#define CAAM_BLOB_OVERHEAD (32 + 16)
+#define CAAM_BLOB_MAX_LEN 4096
+
+struct caam_blob_priv;
+
+/**
+ * struct caam_blob_info - information for CAAM blobbing
+ * @input: pointer to input buffer (must be DMAable)
+ * @input_len: length of @input buffer in bytes.
+ * @output: pointer to output buffer (must be DMAable)
+ * @output_len: length of @output buffer in bytes.
+ * @key_mod: key modifier
+ * @key_mod_len: length of @key_mod in bytes.
+ * May not exceed %CAAM_BLOB_KEYMOD_LENGTH
+ */
+struct caam_blob_info {
+ void *input;
+ size_t input_len;
+
+ void *output;
+ size_t output_len;
+
+ const void *key_mod;
+ size_t key_mod_len;
+};
+
+/**
+ * caam_blob_gen_init - initialize blob generation
+ * Return: pointer to new &struct caam_blob_priv instance on success
+ * and ``ERR_PTR(-ENODEV)`` if CAAM has no hardware blobbing support
+ * or no job ring could be allocated.
+ */
+struct caam_blob_priv *caam_blob_gen_init(void);
+
+/**
+ * caam_blob_gen_exit - free blob generation resources
+ * @priv: instance returned by caam_blob_gen_init()
+ */
+void caam_blob_gen_exit(struct caam_blob_priv *priv);
+
+/**
+ * caam_process_blob - encapsulate or decapsulate blob
+ * @priv: instance returned by caam_blob_gen_init()
+ * @info: pointer to blobbing info describing key, blob and
+ * key modifier buffers.
+ * @encap: true for encapsulation, false for decapsulation
+ *
+ * Return: %0 and sets ``info->output_len`` on success and a negative
+ * error code otherwise.
+ */
+int caam_process_blob(struct caam_blob_priv *priv,
+ struct caam_blob_info *info, bool encap);
+
+/**
+ * caam_encap_blob - encapsulate blob
+ * @priv: instance returned by caam_blob_gen_init()
+ * @info: pointer to blobbing info describing input key,
+ * output blob and key modifier buffers.
+ *
+ * Return: %0 and sets ``info->output_len`` on success and
+ * a negative error code otherwise.
+ */
+static inline int caam_encap_blob(struct caam_blob_priv *priv,
+ struct caam_blob_info *info)
+{
+ if (info->output_len < info->input_len + CAAM_BLOB_OVERHEAD)
+ return -EINVAL;
+
+ return caam_process_blob(priv, info, true);
+}
+
+/**
+ * caam_decap_blob - decapsulate blob
+ * @priv: instance returned by caam_blob_gen_init()
+ * @info: pointer to blobbing info describing output key,
+ * input blob and key modifier buffers.
+ *
+ * Return: %0 and sets ``info->output_len`` on success and
+ * a negative error code otherwise.
+ */
+static inline int caam_decap_blob(struct caam_blob_priv *priv,
+ struct caam_blob_info *info)
+{
+ if (info->input_len < CAAM_BLOB_OVERHEAD ||
+ info->output_len < info->input_len - CAAM_BLOB_OVERHEAD)
+ return -EINVAL;
+
+ return caam_process_blob(priv, info, false);
+}
+
+#endif
diff --git a/scripts/check-blacklist-hashes.awk b/scripts/check-blacklist-hashes.awk
new file mode 100755
index 000000000000..107c1d3204d4
--- /dev/null
+++ b/scripts/check-blacklist-hashes.awk
@@ -0,0 +1,37 @@
+#!/usr/bin/awk -f
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright © 2020, Microsoft Corporation. All rights reserved.
+#
+# Author: Mickaël Salaün <mic@linux.microsoft.com>
+#
+# Check that a CONFIG_SYSTEM_BLACKLIST_HASH_LIST file contains a valid array of
+# hash strings. Such string must start with a prefix ("tbs" or "bin"), then a
+# colon (":"), and finally an even number of hexadecimal lowercase characters
+# (up to 128).
+
+BEGIN {
+ RS = ","
+}
+{
+ if (!match($0, "^[ \t\n\r]*\"([^\"]*)\"[ \t\n\r]*$", part1)) {
+ print "Not a string (item " NR "):", $0;
+ exit 1;
+ }
+ if (!match(part1[1], "^(tbs|bin):(.*)$", part2)) {
+ print "Unknown prefix (item " NR "):", part1[1];
+ exit 1;
+ }
+ if (!match(part2[2], "^([0-9a-f]+)$", part3)) {
+ print "Not a lowercase hexadecimal string (item " NR "):", part2[2];
+ exit 1;
+ }
+ if (length(part3[1]) > 128) {
+ print "Hash string too long (item " NR "):", part3[1];
+ exit 1;
+ }
+ if (length(part3[1]) % 2 == 1) {
+ print "Not an even number of hexadecimal characters (item " NR "):", part3[1];
+ exit 1;
+ }
+}
diff --git a/security/integrity/platform_certs/keyring_handler.c b/security/integrity/platform_certs/keyring_handler.c
index 1db4d3b4356d..a2464f3e66cc 100644
--- a/security/integrity/platform_certs/keyring_handler.c
+++ b/security/integrity/platform_certs/keyring_handler.c
@@ -17,34 +17,12 @@ static efi_guid_t efi_cert_x509_sha256_guid __initdata =
static efi_guid_t efi_cert_sha256_guid __initdata = EFI_CERT_SHA256_GUID;
/*
- * Blacklist a hash.
- */
-static __init void uefi_blacklist_hash(const char *source, const void *data,
- size_t len, const char *type,
- size_t type_len)
-{
- char *hash, *p;
-
- hash = kmalloc(type_len + len * 2 + 1, GFP_KERNEL);
- if (!hash)
- return;
- p = memcpy(hash, type, type_len);
- p += type_len;
- bin2hex(p, data, len);
- p += len * 2;
- *p = 0;
-
- mark_hash_blacklisted(hash);
- kfree(hash);
-}
-
-/*
* Blacklist an X509 TBS hash.
*/
static __init void uefi_blacklist_x509_tbs(const char *source,
const void *data, size_t len)
{
- uefi_blacklist_hash(source, data, len, "tbs:", 4);
+ mark_hash_blacklisted(data, len, BLACKLIST_HASH_X509_TBS);
}
/*
@@ -53,7 +31,7 @@ static __init void uefi_blacklist_x509_tbs(const char *source,
static __init void uefi_blacklist_binary(const char *source,
const void *data, size_t len)
{
- uefi_blacklist_hash(source, data, len, "bin:", 4);
+ mark_hash_blacklisted(data, len, BLACKLIST_HASH_BINARY);
}
/*
diff --git a/security/keys/Kconfig b/security/keys/Kconfig
index 0e30b361e1c1..abb03a1b2a5c 100644
--- a/security/keys/Kconfig
+++ b/security/keys/Kconfig
@@ -70,23 +70,19 @@ config BIG_KEYS
config TRUSTED_KEYS
tristate "TRUSTED KEYS"
- depends on KEYS && TCG_TPM
- select CRYPTO
- select CRYPTO_HMAC
- select CRYPTO_SHA1
- select CRYPTO_HASH_INFO
- select ASN1_ENCODER
- select OID_REGISTRY
- select ASN1
+ depends on KEYS
help
This option provides support for creating, sealing, and unsealing
keys in the kernel. Trusted keys are random number symmetric keys,
- generated and RSA-sealed by the TPM. The TPM only unseals the keys,
- if the boot PCRs and other criteria match. Userspace will only ever
- see encrypted blobs.
+ generated and sealed by a trust source selected at kernel boot-time.
+ Userspace will only ever see encrypted blobs.
If you are unsure as to whether this is required, answer N.
+if TRUSTED_KEYS
+source "security/keys/trusted-keys/Kconfig"
+endif
+
config ENCRYPTED_KEYS
tristate "ENCRYPTED KEYS"
depends on KEYS
diff --git a/security/keys/trusted-keys/Kconfig b/security/keys/trusted-keys/Kconfig
new file mode 100644
index 000000000000..dbfdd8536468
--- /dev/null
+++ b/security/keys/trusted-keys/Kconfig
@@ -0,0 +1,38 @@
+config TRUSTED_KEYS_TPM
+ bool "TPM-based trusted keys"
+ depends on TCG_TPM >= TRUSTED_KEYS
+ default y
+ select CRYPTO
+ select CRYPTO_HMAC
+ select CRYPTO_SHA1
+ select CRYPTO_HASH_INFO
+ select ASN1_ENCODER
+ select OID_REGISTRY
+ select ASN1
+ help
+ Enable use of the Trusted Platform Module (TPM) as trusted key
+ backend. Trusted keys are random number symmetric keys,
+ which will be generated and RSA-sealed by the TPM.
+ The TPM only unseals the keys, if the boot PCRs and other
+ criteria match.
+
+config TRUSTED_KEYS_TEE
+ bool "TEE-based trusted keys"
+ depends on TEE >= TRUSTED_KEYS
+ default y
+ help
+ Enable use of the Trusted Execution Environment (TEE) as trusted
+ key backend.
+
+config TRUSTED_KEYS_CAAM
+ bool "CAAM-based trusted keys"
+ depends on CRYPTO_DEV_FSL_CAAM_JR >= TRUSTED_KEYS
+ select CRYPTO_DEV_FSL_CAAM_BLOB_GEN
+ default y
+ help
+ Enable use of NXP's Cryptographic Accelerator and Assurance Module
+ (CAAM) as trusted key backend.
+
+if !TRUSTED_KEYS_TPM && !TRUSTED_KEYS_TEE && !TRUSTED_KEYS_CAAM
+comment "No trust source selected!"
+endif
diff --git a/security/keys/trusted-keys/Makefile b/security/keys/trusted-keys/Makefile
index feb8b6c3cc79..735aa0bc08ef 100644
--- a/security/keys/trusted-keys/Makefile
+++ b/security/keys/trusted-keys/Makefile
@@ -5,10 +5,12 @@
obj-$(CONFIG_TRUSTED_KEYS) += trusted.o
trusted-y += trusted_core.o
-trusted-y += trusted_tpm1.o
+trusted-$(CONFIG_TRUSTED_KEYS_TPM) += trusted_tpm1.o
$(obj)/trusted_tpm2.o: $(obj)/tpm2key.asn1.h
-trusted-y += trusted_tpm2.o
-trusted-y += tpm2key.asn1.o
+trusted-$(CONFIG_TRUSTED_KEYS_TPM) += trusted_tpm2.o
+trusted-$(CONFIG_TRUSTED_KEYS_TPM) += tpm2key.asn1.o
-trusted-$(CONFIG_TEE) += trusted_tee.o
+trusted-$(CONFIG_TRUSTED_KEYS_TEE) += trusted_tee.o
+
+trusted-$(CONFIG_TRUSTED_KEYS_CAAM) += trusted_caam.o
diff --git a/security/keys/trusted-keys/trusted_caam.c b/security/keys/trusted-keys/trusted_caam.c
new file mode 100644
index 000000000000..e3415c520c0a
--- /dev/null
+++ b/security/keys/trusted-keys/trusted_caam.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2021 Pengutronix, Ahmad Fatoum <kernel@pengutronix.de>
+ */
+
+#include <keys/trusted_caam.h>
+#include <keys/trusted-type.h>
+#include <linux/build_bug.h>
+#include <linux/key-type.h>
+#include <soc/fsl/caam-blob.h>
+
+static struct caam_blob_priv *blobifier;
+
+#define KEYMOD "SECURE_KEY"
+
+static_assert(MAX_KEY_SIZE + CAAM_BLOB_OVERHEAD <= CAAM_BLOB_MAX_LEN);
+static_assert(MAX_BLOB_SIZE <= CAAM_BLOB_MAX_LEN);
+
+static int trusted_caam_seal(struct trusted_key_payload *p, char *datablob)
+{
+ int ret;
+ struct caam_blob_info info = {
+ .input = p->key, .input_len = p->key_len,
+ .output = p->blob, .output_len = MAX_BLOB_SIZE,
+ .key_mod = KEYMOD, .key_mod_len = sizeof(KEYMOD) - 1,
+ };
+
+ ret = caam_encap_blob(blobifier, &info);
+ if (ret)
+ return ret;
+
+ p->blob_len = info.output_len;
+ return 0;
+}
+
+static int trusted_caam_unseal(struct trusted_key_payload *p, char *datablob)
+{
+ int ret;
+ struct caam_blob_info info = {
+ .input = p->blob, .input_len = p->blob_len,
+ .output = p->key, .output_len = MAX_KEY_SIZE,
+ .key_mod = KEYMOD, .key_mod_len = sizeof(KEYMOD) - 1,
+ };
+
+ ret = caam_decap_blob(blobifier, &info);
+ if (ret)
+ return ret;
+
+ p->key_len = info.output_len;
+ return 0;
+}
+
+static int trusted_caam_init(void)
+{
+ int ret;
+
+ blobifier = caam_blob_gen_init();
+ if (IS_ERR(blobifier))
+ return PTR_ERR(blobifier);
+
+ ret = register_key_type(&key_type_trusted);
+ if (ret)
+ caam_blob_gen_exit(blobifier);
+
+ return ret;
+}
+
+static void trusted_caam_exit(void)
+{
+ unregister_key_type(&key_type_trusted);
+ caam_blob_gen_exit(blobifier);
+}
+
+struct trusted_key_ops trusted_key_caam_ops = {
+ .migratable = 0, /* non-migratable */
+ .init = trusted_caam_init,
+ .seal = trusted_caam_seal,
+ .unseal = trusted_caam_unseal,
+ .exit = trusted_caam_exit,
+};
diff --git a/security/keys/trusted-keys/trusted_core.c b/security/keys/trusted-keys/trusted_core.c
index 9b9d3ef79cbe..c6fc50d67214 100644
--- a/security/keys/trusted-keys/trusted_core.c
+++ b/security/keys/trusted-keys/trusted_core.c
@@ -9,6 +9,7 @@
#include <keys/user-type.h>
#include <keys/trusted-type.h>
#include <keys/trusted_tee.h>
+#include <keys/trusted_caam.h>
#include <keys/trusted_tpm.h>
#include <linux/capability.h>
#include <linux/err.h>
@@ -16,23 +17,31 @@
#include <linux/key-type.h>
#include <linux/module.h>
#include <linux/parser.h>
+#include <linux/random.h>
#include <linux/rcupdate.h>
#include <linux/slab.h>
#include <linux/static_call.h>
#include <linux/string.h>
#include <linux/uaccess.h>
+static char *trusted_rng = "default";
+module_param_named(rng, trusted_rng, charp, 0);
+MODULE_PARM_DESC(rng, "Select trusted key RNG");
+
static char *trusted_key_source;
module_param_named(source, trusted_key_source, charp, 0);
-MODULE_PARM_DESC(source, "Select trusted keys source (tpm or tee)");
+MODULE_PARM_DESC(source, "Select trusted keys source (tpm, tee or caam)");
static const struct trusted_key_source trusted_key_sources[] = {
-#if IS_REACHABLE(CONFIG_TCG_TPM)
+#if defined(CONFIG_TRUSTED_KEYS_TPM)
{ "tpm", &trusted_key_tpm_ops },
#endif
-#if IS_REACHABLE(CONFIG_TEE)
+#if defined(CONFIG_TRUSTED_KEYS_TEE)
{ "tee", &trusted_key_tee_ops },
#endif
+#if defined(CONFIG_TRUSTED_KEYS_CAAM)
+ { "caam", &trusted_key_caam_ops },
+#endif
};
DEFINE_STATIC_CALL_NULL(trusted_key_init, *trusted_key_sources[0].ops->init);
@@ -312,8 +321,14 @@ struct key_type key_type_trusted = {
};
EXPORT_SYMBOL_GPL(key_type_trusted);
+static int kernel_get_random(unsigned char *key, size_t key_len)
+{
+ return get_random_bytes_wait(key, key_len) ?: key_len;
+}
+
static int __init init_trusted(void)
{
+ int (*get_random)(unsigned char *key, size_t key_len);
int i, ret = 0;
for (i = 0; i < ARRAY_SIZE(trusted_key_sources); i++) {
@@ -322,6 +337,28 @@ static int __init init_trusted(void)
strlen(trusted_key_sources[i].name)))
continue;
+ /*
+ * We always support trusted.rng="kernel" and "default" as
+ * well as trusted.rng=$trusted.source if the trust source
+ * defines its own get_random callback.
+ */
+ get_random = trusted_key_sources[i].ops->get_random;
+ if (trusted_rng && strcmp(trusted_rng, "default")) {
+ if (!strcmp(trusted_rng, "kernel")) {
+ get_random = kernel_get_random;
+ } else if (strcmp(trusted_rng, trusted_key_sources[i].name) ||
+ !get_random) {
+ pr_warn("Unsupported RNG. Supported: kernel");
+ if (get_random)
+ pr_cont(", %s", trusted_key_sources[i].name);
+ pr_cont(", default\n");
+ return -EINVAL;
+ }
+ }
+
+ if (!get_random)
+ get_random = kernel_get_random;
+
static_call_update(trusted_key_init,
trusted_key_sources[i].ops->init);
static_call_update(trusted_key_seal,
@@ -329,7 +366,7 @@ static int __init init_trusted(void)
static_call_update(trusted_key_unseal,
trusted_key_sources[i].ops->unseal);
static_call_update(trusted_key_get_random,
- trusted_key_sources[i].ops->get_random);
+ get_random);
static_call_update(trusted_key_exit,
trusted_key_sources[i].ops->exit);
migratable = trusted_key_sources[i].ops->migratable;
diff --git a/tools/certs/print-cert-tbs-hash.sh b/tools/certs/print-cert-tbs-hash.sh
new file mode 100755
index 000000000000..c93df5387ec9
--- /dev/null
+++ b/tools/certs/print-cert-tbs-hash.sh
@@ -0,0 +1,91 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright © 2020, Microsoft Corporation. All rights reserved.
+#
+# Author: Mickaël Salaün <mic@linux.microsoft.com>
+#
+# Compute and print the To Be Signed (TBS) hash of a certificate. This is used
+# as description of keys in the blacklist keyring to identify certificates.
+# This output should be redirected, without newline, in a file (hash0.txt) and
+# signed to create a PKCS#7 file (hash0.p7s). Both of these files can then be
+# loaded in the kernel with.
+#
+# Exemple on a workstation:
+# ./print-cert-tbs-hash.sh certificate-to-invalidate.pem > hash0.txt
+# openssl smime -sign -in hash0.txt -inkey builtin-private-key.pem \
+# -signer builtin-certificate.pem -certfile certificate-chain.pem \
+# -noattr -binary -outform DER -out hash0.p7s
+#
+# Exemple on a managed system:
+# keyctl padd blacklist "$(< hash0.txt)" %:.blacklist < hash0.p7s
+
+set -u -e -o pipefail
+
+CERT="${1:-}"
+BASENAME="$(basename -- "${BASH_SOURCE[0]}")"
+
+if [ $# -ne 1 ] || [ ! -f "${CERT}" ]; then
+ echo "usage: ${BASENAME} <certificate>" >&2
+ exit 1
+fi
+
+# Checks that it is indeed a certificate (PEM or DER encoded) and exclude the
+# optional PEM text header.
+if ! PEM="$(openssl x509 -inform DER -in "${CERT}" 2>/dev/null || openssl x509 -in "${CERT}")"; then
+ echo "ERROR: Failed to parse certificate" >&2
+ exit 1
+fi
+
+# TBSCertificate starts at the second entry.
+# Cf. https://tools.ietf.org/html/rfc3280#section-4.1
+#
+# Exemple of first lines printed by openssl asn1parse:
+# 0:d=0 hl=4 l= 763 cons: SEQUENCE
+# 4:d=1 hl=4 l= 483 cons: SEQUENCE
+# 8:d=2 hl=2 l= 3 cons: cont [ 0 ]
+# 10:d=3 hl=2 l= 1 prim: INTEGER :02
+# 13:d=2 hl=2 l= 20 prim: INTEGER :3CEB2CB8818D968AC00EEFE195F0DF9665328B7B
+# 35:d=2 hl=2 l= 13 cons: SEQUENCE
+# 37:d=3 hl=2 l= 9 prim: OBJECT :sha256WithRSAEncryption
+RANGE_AND_DIGEST_RE='
+2s/^\s*\([0-9]\+\):d=\s*[0-9]\+\s\+hl=\s*[0-9]\+\s\+l=\s*\([0-9]\+\)\s\+cons:\s*SEQUENCE\s*$/\1 \2/p;
+7s/^\s*[0-9]\+:d=\s*[0-9]\+\s\+hl=\s*[0-9]\+\s\+l=\s*[0-9]\+\s\+prim:\s*OBJECT\s*:\(.*\)$/\1/p;
+'
+
+RANGE_AND_DIGEST=($(echo "${PEM}" | \
+ openssl asn1parse -in - | \
+ sed -n -e "${RANGE_AND_DIGEST_RE}"))
+
+if [ "${#RANGE_AND_DIGEST[@]}" != 3 ]; then
+ echo "ERROR: Failed to parse TBSCertificate." >&2
+ exit 1
+fi
+
+OFFSET="${RANGE_AND_DIGEST[0]}"
+END="$(( OFFSET + RANGE_AND_DIGEST[1] ))"
+DIGEST="${RANGE_AND_DIGEST[2]}"
+
+# The signature hash algorithm is used by Linux to blacklist certificates.
+# Cf. crypto/asymmetric_keys/x509_cert_parser.c:x509_note_pkey_algo()
+DIGEST_MATCH=""
+while read -r DIGEST_ITEM; do
+ if [ -z "${DIGEST_ITEM}" ]; then
+ break
+ fi
+ if echo "${DIGEST}" | grep -qiF "${DIGEST_ITEM}"; then
+ DIGEST_MATCH="${DIGEST_ITEM}"
+ break
+ fi
+done < <(openssl list -digest-commands | tr ' ' '\n' | sort -ur)
+
+if [ -z "${DIGEST_MATCH}" ]; then
+ echo "ERROR: Unknown digest algorithm: ${DIGEST}" >&2
+ exit 1
+fi
+
+echo "${PEM}" | \
+ openssl x509 -in - -outform DER | \
+ dd "bs=1" "skip=${OFFSET}" "count=${END}" "status=none" | \
+ openssl dgst "-${DIGEST_MATCH}" - | \
+ awk '{printf "tbs:" $2}'