On Thu, Aug 25, 2022 at 10:23:12PM +0800, Lee, Chun-Yi wrote: > This patch adds the logic for checking the CodeSigning extended > key usage when verifying signature of kernel module or > kexec PE binary in PKCS#7. Pretty much the same feedback as for 1/4. > > Signed-off-by: "Lee, Chun-Yi" <jlee@xxxxxxxx> > --- > certs/blacklist.c | 5 ++-- > certs/system_keyring.c | 4 +-- > crypto/asymmetric_keys/Kconfig | 9 ++++++ > crypto/asymmetric_keys/pkcs7_trust.c | 43 ++++++++++++++++++++++++++-- > crypto/asymmetric_keys/selftest.c | 2 +- > include/crypto/pkcs7.h | 4 ++- > include/keys/system_keyring.h | 7 +++-- > 7 files changed, 63 insertions(+), 11 deletions(-) > > diff --git a/certs/blacklist.c b/certs/blacklist.c > index 41f10601cc72..fa41454055be 100644 > --- a/certs/blacklist.c > +++ b/certs/blacklist.c > @@ -282,11 +282,12 @@ int add_key_to_revocation_list(const char *data, size_t size) > * is_key_on_revocation_list - Determine if the key for a PKCS#7 message is revoked > * @pkcs7: The PKCS#7 message to check > */ > -int is_key_on_revocation_list(struct pkcs7_message *pkcs7) > +int is_key_on_revocation_list(struct pkcs7_message *pkcs7, > + enum key_being_used_for usage) > { > int ret; > > - ret = pkcs7_validate_trust(pkcs7, blacklist_keyring); > + ret = pkcs7_validate_trust(pkcs7, blacklist_keyring, usage, false); > > if (ret == 0) > return -EKEYREJECTED; > diff --git a/certs/system_keyring.c b/certs/system_keyring.c > index 5042cc54fa5e..66737bfb26de 100644 > --- a/certs/system_keyring.c > +++ b/certs/system_keyring.c > @@ -263,13 +263,13 @@ int verify_pkcs7_message_sig(const void *data, size_t len, > goto error; > } > > - ret = is_key_on_revocation_list(pkcs7); > + ret = is_key_on_revocation_list(pkcs7, usage); > if (ret != -ENOKEY) { > pr_devel("PKCS#7 platform key is on revocation list\n"); > goto error; > } > } > - ret = pkcs7_validate_trust(pkcs7, trusted_keys); > + ret = pkcs7_validate_trust(pkcs7, trusted_keys, usage, true); > if (ret < 0) { > if (ret == -ENOKEY) > pr_devel("PKCS#7 signature not signed with a trusted key\n"); > diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig > index 3df3fe4ed95f..189536bd0f9a 100644 > --- a/crypto/asymmetric_keys/Kconfig > +++ b/crypto/asymmetric_keys/Kconfig > @@ -85,4 +85,13 @@ config FIPS_SIGNATURE_SELFTEST > depends on ASYMMETRIC_KEY_TYPE > depends on PKCS7_MESSAGE_PARSER > > +config CHECK_CODESIGN_EKU > + bool "Check codeSigning extended key usage" > + depends on PKCS7_MESSAGE_PARSER=y > + depends on SYSTEM_DATA_VERIFICATION > + help > + This option provides support for checking the codeSigning extended > + key usage when verifying the signature in PKCS#7. It affects kernel > + module verification and kexec PE binary verification. > + > endif # ASYMMETRIC_KEY_TYPE > diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c > index 9a87c34ed173..087d3761d9a8 100644 > --- a/crypto/asymmetric_keys/pkcs7_trust.c > +++ b/crypto/asymmetric_keys/pkcs7_trust.c > @@ -16,12 +16,40 @@ > #include <crypto/public_key.h> > #include "pkcs7_parser.h" > > +#ifdef CONFIG_CHECK_CODESIGN_EKU > +static bool check_eku_by_usage(struct key *key, enum key_being_used_for usage) > +{ > + struct public_key *public_key = key->payload.data[asym_crypto]; > + bool ret = true; > + > + switch (usage) { > + case VERIFYING_MODULE_SIGNATURE: > + case VERIFYING_KEXEC_PE_SIGNATURE: > + ret = !!(public_key->ext_key_usage & EKU_codeSigning); > + if (!ret) > + pr_warn("The signer '%s' key is not CodeSigning\n", > + key->description); > + break; > + default: > + break; > + } > + return ret; > +} > +#else > +static bool check_eku_by_usage(struct key *key, enum key_being_used_for usage) > +{ > + return true; > +} > +#endif > + > /* > * Check the trust on one PKCS#7 SignedInfo block. > */ > static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, > struct pkcs7_signed_info *sinfo, > - struct key *trust_keyring) > + struct key *trust_keyring, > + enum key_being_used_for usage, > + bool check_eku) > { > struct public_key_signature *sig = sinfo->sig; > struct x509_certificate *x509, *last = NULL, *p; > @@ -112,6 +140,10 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, > return -ENOKEY; > > matched: > + if (check_eku && !check_eku_by_usage(key, usage)) { > + key_put(key); > + return -ENOKEY; > + } Why you don't just open code this to those rare call sites where it is needed? I counted that there is exactly one call site. > ret = verify_signature(key, sig); > key_put(key); > if (ret < 0) { > @@ -135,6 +167,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, > * pkcs7_validate_trust - Validate PKCS#7 trust chain > * @pkcs7: The PKCS#7 certificate to validate > * @trust_keyring: Signing certificates to use as starting points > + * @usage: The use to which the key is being put. > + * @check_eku: Check EKU (Extended Key Usage) > * > * Validate that the certificate chain inside the PKCS#7 message intersects > * keys we already know and trust. > @@ -156,7 +190,9 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, > * May also return -ENOMEM. > */ > int pkcs7_validate_trust(struct pkcs7_message *pkcs7, > - struct key *trust_keyring) > + struct key *trust_keyring, > + enum key_being_used_for usage, > + bool check_eku) > { > struct pkcs7_signed_info *sinfo; > struct x509_certificate *p; > @@ -167,7 +203,8 @@ int pkcs7_validate_trust(struct pkcs7_message *pkcs7, > p->seen = false; > > for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { > - ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); > + ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring, > + usage, check_eku); > switch (ret) { > case -ENOKEY: > continue; > diff --git a/crypto/asymmetric_keys/selftest.c b/crypto/asymmetric_keys/selftest.c > index fa0bf7f24284..756e9f224d8a 100644 > --- a/crypto/asymmetric_keys/selftest.c > +++ b/crypto/asymmetric_keys/selftest.c > @@ -212,7 +212,7 @@ int __init fips_signature_selftest(void) > if (ret < 0) > panic("Certs selftest %d: pkcs7_verify() = %d\n", i, ret); > > - ret = pkcs7_validate_trust(pkcs7, keyring); > + ret = pkcs7_validate_trust(pkcs7, keyring, VERIFYING_MODULE_SIGNATURE, false); > if (ret < 0) > panic("Certs selftest %d: pkcs7_validate_trust() = %d\n", i, ret); > > diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h > index 38ec7f5f9041..5d87b8a02f79 100644 > --- a/include/crypto/pkcs7.h > +++ b/include/crypto/pkcs7.h > @@ -30,7 +30,9 @@ extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, > * pkcs7_trust.c > */ > extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, > - struct key *trust_keyring); > + struct key *trust_keyring, > + enum key_being_used_for usage, > + bool check_eku); > > /* > * pkcs7_verify.c > diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h > index 91e080efb918..bb33b527240e 100644 > --- a/include/keys/system_keyring.h > +++ b/include/keys/system_keyring.h > @@ -9,6 +9,7 @@ > #define _KEYS_SYSTEM_KEYRING_H > > #include <linux/key.h> > +#include <keys/asymmetric-type.h> > > enum blacklist_hash_type { > /* TBSCertificate hash */ > @@ -81,13 +82,15 @@ static inline int is_binary_blacklisted(const u8 *hash, size_t hash_len) > > #ifdef CONFIG_SYSTEM_REVOCATION_LIST > extern int add_key_to_revocation_list(const char *data, size_t size); > -extern int is_key_on_revocation_list(struct pkcs7_message *pkcs7); > +extern int is_key_on_revocation_list(struct pkcs7_message *pkcs7, > + enum key_being_used_for usage); > #else > static inline int add_key_to_revocation_list(const char *data, size_t size) > { > return 0; > } > -static inline int is_key_on_revocation_list(struct pkcs7_message *pkcs7) > +static inline int is_key_on_revocation_list(struct pkcs7_message *pkcs7, > + enum key_being_used_for usage) > { > return -ENOKEY; > } > -- > 2.26.2 > BR, Jarkko