[PATCH 27/27] PKCS#7: Restrict content type and authenticated attributes by purpose [ver #7]

From: David Howells
Date: Wed Aug 05 2015 - 09:48:14 EST


Restrict the content type in the and the authenticated attributes permitted
in a PKCS#7 SignedData object according the purpose to which the message is
being put:

(*) VERIFYING_MODULE_SIGNATURE
(*) VERIFYING_FIRMWARE_SIGNATURE
(*) VERIFYING_UNSPECIFIED_SIGNATURE

These three all require the SignedData content type to be pkcs7-data.
The first also forbids authattrs, the second requires them and the
third is okay either way.

(*) VERIFYING_KEXEC_PE_SIGNATURE

This only supports the Authenticode SPC_INDIRECT_DATA content type and
requires at least an SpcSpOpusInfo authattr but also permits an
SPC_STATEMENT_TYPE authattr (and an S/MIME capabilities authattr
because the pesign program doesn't remove these).

(*) VERIFYING_KEY_SIGNATURE
(*) VERIFYING_KEY_SELF_SIGNATURE

These are invalid in this context but are included for later use when
limiting the use of X.509 certs.



The PKCS#7 test key type is given the usage to specify in a module
parameter. For example:

echo 1 >/sys/module/pkcs7_test_key/parameters/usage
keyctl padd pkcs7_test foo @s </tmp/stuff.pkcs7

will attempt to check the signature on stuff.pkcs7 as if it contains a
firmware blob (1 being VERIFYING_FIRMWARE_SIGNATURE).

Signed-off-by: David Howells <dhowells@xxxxxxxxxx>
---

arch/x86/kernel/kexec-bzimage64.c | 4 +++-
crypto/asymmetric_keys/asymmetric_type.c | 11 ++++++++++
crypto/asymmetric_keys/pkcs7_key_type.c | 19 +++++++++++------
crypto/asymmetric_keys/pkcs7_verify.c | 34 ++++++++++++++++++++----------
crypto/asymmetric_keys/verify_pefile.c | 7 ++++--
include/crypto/pkcs7.h | 9 +++-----
include/crypto/public_key.h | 14 ++++++++++++
include/keys/system_keyring.h | 4 +++-
include/linux/verify_pefile.h | 6 ++++-
kernel/module_signing.c | 3 ++-
kernel/system_keyring.c | 6 ++++-
11 files changed, 85 insertions(+), 32 deletions(-)

diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c
index ca83f7ac388b..fab22e72808c 100644
--- a/arch/x86/kernel/kexec-bzimage64.c
+++ b/arch/x86/kernel/kexec-bzimage64.c
@@ -536,7 +536,9 @@ static int bzImage64_verify_sig(const char *kernel, unsigned long kernel_len)
int ret;

ret = verify_pefile_signature(kernel, kernel_len,
- system_trusted_keyring, &trusted);
+ system_trusted_keyring,
+ VERIFYING_KEXEC_PE_SIGNATURE,
+ &trusted);
if (ret < 0)
return ret;
if (!trusted)
diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c
index b0e4ed23d668..1916680ad81b 100644
--- a/crypto/asymmetric_keys/asymmetric_type.c
+++ b/crypto/asymmetric_keys/asymmetric_type.c
@@ -12,6 +12,7 @@
*/
#include <keys/asymmetric-subtype.h>
#include <keys/asymmetric-parser.h>
+#include <crypto/public_key.h>
#include <linux/seq_file.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -20,6 +21,16 @@

MODULE_LICENSE("GPL");

+const char *const key_being_used_for[NR__KEY_BEING_USED_FOR] = {
+ [VERIFYING_MODULE_SIGNATURE] = "mod sig",
+ [VERIFYING_FIRMWARE_SIGNATURE] = "firmware sig",
+ [VERIFYING_KEXEC_PE_SIGNATURE] = "kexec PE sig",
+ [VERIFYING_KEY_SIGNATURE] = "key sig",
+ [VERIFYING_KEY_SELF_SIGNATURE] = "key self sig",
+ [VERIFYING_UNSPECIFIED_SIGNATURE] = "unspec sig",
+};
+EXPORT_SYMBOL_GPL(key_being_used_for);
+
static LIST_HEAD(asymmetric_key_parsers);
static DECLARE_RWSEM(asymmetric_key_parsers_sem);

diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c
index 6eae1a30b143..10d34dbd00b9 100644
--- a/crypto/asymmetric_keys/pkcs7_key_type.c
+++ b/crypto/asymmetric_keys/pkcs7_key_type.c
@@ -14,21 +14,23 @@
#include <linux/err.h>
#include <linux/module.h>
#include <linux/key-type.h>
+#include <keys/asymmetric-type.h>
#include <crypto/pkcs7.h>
#include <keys/user-type.h>
#include <keys/system_keyring.h>
#include "pkcs7_parser.h"

-static unsigned pkcs7_want_authattrs;
-module_param_named(authattrs, pkcs7_want_authattrs, uint, S_IWUSR | S_IRUGO);
-MODULE_PARM_DESC(pkcs7_want_authattrs,
- "Whether or not a PKCS#7 message should contain authattrs (0-2)");
+static unsigned pkcs7_usage;
+module_param_named(usage, pkcs7_usage, uint, S_IWUSR | S_IRUGO);
+MODULE_PARM_DESC(pkcs7_usage,
+ "Usage to specify when verifying the PKCS#7 message");

/*
* Preparse a PKCS#7 wrapped and validated data blob.
*/
static int pkcs7_preparse(struct key_preparsed_payload *prep)
{
+ enum key_being_used_for usage = pkcs7_usage;
struct pkcs7_message *pkcs7;
const void *data, *saved_prep_data;
size_t datalen, saved_prep_datalen;
@@ -37,6 +39,11 @@ static int pkcs7_preparse(struct key_preparsed_payload *prep)

kenter("");

+ if (usage >= NR__KEY_BEING_USED_FOR) {
+ pr_err("Invalid usage type %d\n", usage);
+ return -EINVAL;
+ }
+
saved_prep_data = prep->data;
saved_prep_datalen = prep->datalen;
pkcs7 = pkcs7_parse_message(saved_prep_data, saved_prep_datalen);
@@ -45,9 +52,7 @@ static int pkcs7_preparse(struct key_preparsed_payload *prep)
goto error;
}

- if (pkcs7_want_authattrs > 2)
- pkcs7_want_authattrs = 0;
- ret = pkcs7_verify(pkcs7, pkcs7_want_authattrs);
+ ret = pkcs7_verify(pkcs7, usage);
if (ret < 0)
goto error_free;

diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c
index 320f9a0b0ae9..d20c0b4b880e 100644
--- a/crypto/asymmetric_keys/pkcs7_verify.c
+++ b/crypto/asymmetric_keys/pkcs7_verify.c
@@ -346,7 +346,7 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
/**
* pkcs7_verify - Verify a PKCS#7 message
* @pkcs7: The PKCS#7 message to be verified
- * @attr_style: Whether we want authenticatedAttributes or not.
+ * @usage: The use to which the key is being put
*
* Verify a PKCS#7 message is internally consistent - that is, the data digest
* matches the digest in the AuthAttrs and any signature in the message or one
@@ -358,6 +358,9 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
*
* Returns, in order of descending priority:
*
+ * (*) -EKEYREJECTED if a key was selected that had a usage restriction at
+ * odds with the specified usage, or:
+ *
* (*) -EKEYREJECTED if a signature failed to match for which we found an
* appropriate X.509 certificate, or:
*
@@ -369,7 +372,8 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7,
* (*) 0 if all the signature chains that don't incur -ENOPKG can be verified
* (note that a signature chain may be of zero length), or:
*/
-int pkcs7_verify(struct pkcs7_message *pkcs7, enum pkcs7_attr_style attr_style)
+int pkcs7_verify(struct pkcs7_message *pkcs7,
+ enum key_being_used_for usage)
{
struct pkcs7_signed_info *sinfo;
struct x509_certificate *x509;
@@ -378,34 +382,42 @@ int pkcs7_verify(struct pkcs7_message *pkcs7, enum pkcs7_attr_style attr_style)

kenter("");

- switch (attr_style) {
- case PKCS7_REJECT_AUTHATTRS:
+ switch (usage) {
+ case VERIFYING_MODULE_SIGNATURE:
if (pkcs7->data_type != OID_data) {
- pr_warn("Signed message is not ordinary data\n");
+ pr_warn("Invalid module sig (not pkcs7-data)\n");
return -EKEYREJECTED;
}
if (pkcs7->have_authattrs) {
- pr_warn("Message contains unwanted authAttrs\n");
+ pr_warn("Invalid module sig (has authattrs)\n");
return -EKEYREJECTED;
}
break;
- case PKCS7_REQUIRE_AUTHATTRS:
+ case VERIFYING_FIRMWARE_SIGNATURE:
if (pkcs7->data_type != OID_data) {
- pr_warn("Signed message is not ordinary data\n");
+ pr_warn("Invalid firmware sig (not pkcs7-data)\n");
return -EKEYREJECTED;
}
if (!pkcs7->have_authattrs) {
- pr_warn("Message does not contain authAttrs\n");
+ pr_warn("Invalid firmware sig (missing authattrs)\n");
return -EKEYREJECTED;
}
break;
- case PKCS7_AUTHENTICODE_AUTHATTRS:
+ case VERIFYING_KEXEC_PE_SIGNATURE:
if (pkcs7->data_type != OID_msIndirectData) {
- pr_warn("Signed message is not Authenticode\n");
+ pr_warn("Invalid kexec sig (not Authenticode)\n");
return -EKEYREJECTED;
}
/* Authattr presence checked in parser */
break;
+ case VERIFYING_UNSPECIFIED_SIGNATURE:
+ if (pkcs7->data_type != OID_data) {
+ pr_warn("Invalid unspecified sig (not pkcs7-data)\n");
+ return -EKEYREJECTED;
+ }
+ break;
+ default:
+ return -EINVAL;
}

for (n = 0, x509 = pkcs7->certs; x509; x509 = x509->next, n++) {
diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c
index d2044eeb72f4..897b734dabf9 100644
--- a/crypto/asymmetric_keys/verify_pefile.c
+++ b/crypto/asymmetric_keys/verify_pefile.c
@@ -393,6 +393,7 @@ error_no_desc:
* @pebuf: Buffer containing the PE binary image
* @pelen: Length of the binary image
* @trust_keyring: Signing certificates to use as starting points
+ * @usage: The use to which the key is being put.
* @_trusted: Set to true if trustworth, false otherwise
*
* Validate that the certificate chain inside the PKCS#7 message inside the PE
@@ -417,7 +418,9 @@ error_no_desc:
* May also return -ENOMEM.
*/
int verify_pefile_signature(const void *pebuf, unsigned pelen,
- struct key *trusted_keyring, bool *_trusted)
+ struct key *trusted_keyring,
+ enum key_being_used_for usage,
+ bool *_trusted)
{
struct pkcs7_message *pkcs7;
struct pefile_context ctx;
@@ -462,7 +465,7 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen,
if (ret < 0)
goto error;

- ret = pkcs7_verify(pkcs7, PKCS7_AUTHENTICODE_AUTHATTRS);
+ ret = pkcs7_verify(pkcs7, usage);
if (ret < 0)
goto error;

diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h
index 15214059f408..441aff9b5aa7 100644
--- a/include/crypto/pkcs7.h
+++ b/include/crypto/pkcs7.h
@@ -12,6 +12,8 @@
#ifndef _CRYPTO_PKCS7_H
#define _CRYPTO_PKCS7_H

+#include <crypto/public_key.h>
+
struct key;
struct pkcs7_message;

@@ -36,13 +38,8 @@ extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7,
/*
* pkcs7_verify.c
*/
-enum pkcs7_attr_style {
- PKCS7_REJECT_AUTHATTRS,
- PKCS7_REQUIRE_AUTHATTRS,
- PKCS7_AUTHENTICODE_AUTHATTRS,
-};
extern int pkcs7_verify(struct pkcs7_message *pkcs7,
- enum pkcs7_attr_style attr_style);
+ enum key_being_used_for usage);

extern int pkcs7_supply_detached_data(struct pkcs7_message *pkcs7,
const void *data, size_t datalen);
diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h
index fda097e079a4..067c242b1e15 100644
--- a/include/crypto/public_key.h
+++ b/include/crypto/public_key.h
@@ -40,6 +40,20 @@ enum pkey_id_type {
extern const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST];

/*
+ * The use to which an asymmetric key is being put.
+ */
+enum key_being_used_for {
+ VERIFYING_MODULE_SIGNATURE,
+ VERIFYING_FIRMWARE_SIGNATURE,
+ VERIFYING_KEXEC_PE_SIGNATURE,
+ VERIFYING_KEY_SIGNATURE,
+ VERIFYING_KEY_SELF_SIGNATURE,
+ VERIFYING_UNSPECIFIED_SIGNATURE,
+ NR__KEY_BEING_USED_FOR
+};
+extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR];
+
+/*
* Cryptographic data for the public-key subtype of the asymmetric key type.
*
* Note that this may include private part of the key as well as the public
diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h
index 9791c907cdb7..b20cd885c1fd 100644
--- a/include/keys/system_keyring.h
+++ b/include/keys/system_keyring.h
@@ -15,6 +15,7 @@
#ifdef CONFIG_SYSTEM_TRUSTED_KEYRING

#include <linux/key.h>
+#include <crypto/public_key.h>

extern struct key *system_trusted_keyring;
static inline struct key *get_system_trusted_keyring(void)
@@ -30,7 +31,8 @@ static inline struct key *get_system_trusted_keyring(void)

#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
extern int system_verify_data(const void *data, unsigned long len,
- const void *raw_pkcs7, size_t pkcs7_len);
+ const void *raw_pkcs7, size_t pkcs7_len,
+ enum key_being_used_for usage);
#endif

#endif /* _KEYS_SYSTEM_KEYRING_H */
diff --git a/include/linux/verify_pefile.h b/include/linux/verify_pefile.h
index ac34819214f9..da2049b5161c 100644
--- a/include/linux/verify_pefile.h
+++ b/include/linux/verify_pefile.h
@@ -12,7 +12,11 @@
#ifndef _LINUX_VERIFY_PEFILE_H
#define _LINUX_VERIFY_PEFILE_H

+#include <crypto/public_key.h>
+
extern int verify_pefile_signature(const void *pebuf, unsigned pelen,
- struct key *trusted_keyring, bool *_trusted);
+ struct key *trusted_keyring,
+ enum key_being_used_for usage,
+ bool *_trusted);

#endif /* _LINUX_VERIFY_PEFILE_H */
diff --git a/kernel/module_signing.c b/kernel/module_signing.c
index 70ad463f6df0..bd62f5cda746 100644
--- a/kernel/module_signing.c
+++ b/kernel/module_signing.c
@@ -72,5 +72,6 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen)
return -EBADMSG;
}

- return system_verify_data(mod, modlen, mod + modlen, sig_len);
+ return system_verify_data(mod, modlen, mod + modlen, sig_len,
+ VERIFYING_MODULE_SIGNATURE);
}
diff --git a/kernel/system_keyring.c b/kernel/system_keyring.c
index f9e1a75db59b..2570598b784d 100644
--- a/kernel/system_keyring.c
+++ b/kernel/system_keyring.c
@@ -113,9 +113,11 @@ late_initcall(load_system_certificate_list);
* @len: Size of @data.
* @raw_pkcs7: The PKCS#7 message that is the signature.
* @pkcs7_len: The size of @raw_pkcs7.
+ * @usage: The use to which the key is being put.
*/
int system_verify_data(const void *data, unsigned long len,
- const void *raw_pkcs7, size_t pkcs7_len)
+ const void *raw_pkcs7, size_t pkcs7_len,
+ enum key_being_used_for usage)
{
struct pkcs7_message *pkcs7;
bool trusted;
@@ -132,7 +134,7 @@ int system_verify_data(const void *data, unsigned long len,
goto error;
}

- ret = pkcs7_verify(pkcs7, PKCS7_REJECT_AUTHATTRS);
+ ret = pkcs7_verify(pkcs7, usage);
if (ret < 0)
goto error;


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/