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 <dhowe...@redhat.com> --- 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 majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/