diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-08 12:41:25 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-09-08 12:41:25 -0700 |
commit | b793c005ceabf6db0b17494b0ec67ade6796bb34 (patch) | |
tree | 080c884f04254403ec9564742f591a9fd9b7e95a /crypto | |
parent | 6f0a2fc1feb19bd142961a39dc118e7e55418b3f (diff) | |
parent | 07f081fb5057b2ea98baeca3a47bf0eb33e94aa1 (diff) | |
download | lwn-b793c005ceabf6db0b17494b0ec67ade6796bb34.tar.gz lwn-b793c005ceabf6db0b17494b0ec67ade6796bb34.zip |
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris:
"Highlights:
- PKCS#7 support added to support signed kexec, also utilized for
module signing. See comments in 3f1e1bea.
** NOTE: this requires linking against the OpenSSL library, which
must be installed, e.g. the openssl-devel on Fedora **
- Smack
- add IPv6 host labeling; ignore labels on kernel threads
- support smack labeling mounts which use binary mount data
- SELinux:
- add ioctl whitelisting (see
http://kernsec.org/files/lss2015/vanderstoep.pdf)
- fix mprotect PROT_EXEC regression caused by mm change
- Seccomp:
- add ptrace options for suspend/resume"
* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (57 commits)
PKCS#7: Add OIDs for sha224, sha284 and sha512 hash algos and use them
Documentation/Changes: Now need OpenSSL devel packages for module signing
scripts: add extract-cert and sign-file to .gitignore
modsign: Handle signing key in source tree
modsign: Use if_changed rule for extracting cert from module signing key
Move certificate handling to its own directory
sign-file: Fix warning about BIO_reset() return value
PKCS#7: Add MODULE_LICENSE() to test module
Smack - Fix build error with bringup unconfigured
sign-file: Document dependency on OpenSSL devel libraries
PKCS#7: Appropriately restrict authenticated attributes and content type
KEYS: Add a name for PKEY_ID_PKCS7
PKCS#7: Improve and export the X.509 ASN.1 time object decoder
modsign: Use extract-cert to process CONFIG_SYSTEM_TRUSTED_KEYS
extract-cert: Cope with multiple X.509 certificates in a single file
sign-file: Generate CMS message as signature instead of PKCS#7
PKCS#7: Support CMS messages also [RFC5652]
X.509: Change recorded SKID & AKID to not include Subject or Issuer
PKCS#7: Check content type and versions
MAINTAINERS: The keyrings mailing list has moved
...
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/Kconfig | 1 | ||||
-rw-r--r-- | crypto/asymmetric_keys/Makefile | 8 | ||||
-rw-r--r-- | crypto/asymmetric_keys/asymmetric_type.c | 11 | ||||
-rw-r--r-- | crypto/asymmetric_keys/mscode_parser.c | 9 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs7.asn1 | 22 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs7_key_type.c | 17 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs7_parser.c | 277 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs7_parser.h | 20 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs7_trust.c | 10 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs7_verify.c | 145 | ||||
-rw-r--r-- | crypto/asymmetric_keys/public_key.c | 1 | ||||
-rw-r--r-- | crypto/asymmetric_keys/verify_pefile.c | 7 | ||||
-rw-r--r-- | crypto/asymmetric_keys/x509_akid.asn1 | 35 | ||||
-rw-r--r-- | crypto/asymmetric_keys/x509_cert_parser.c | 231 | ||||
-rw-r--r-- | crypto/asymmetric_keys/x509_parser.h | 12 | ||||
-rw-r--r-- | crypto/asymmetric_keys/x509_public_key.c | 95 |
16 files changed, 738 insertions, 163 deletions
diff --git a/crypto/Kconfig b/crypto/Kconfig index b582ea7f78d3..48ee3e175dac 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -1635,5 +1635,6 @@ config CRYPTO_HASH_INFO source "drivers/crypto/Kconfig" source crypto/asymmetric_keys/Kconfig +source certs/Kconfig endif # if CRYPTO diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index e47fcd9ac5e8..cd1406f9b14a 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -15,15 +15,21 @@ obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o obj-$(CONFIG_X509_CERTIFICATE_PARSER) += x509_key_parser.o x509_key_parser-y := \ x509-asn1.o \ + x509_akid-asn1.o \ x509_rsakey-asn1.o \ x509_cert_parser.o \ x509_public_key.o -$(obj)/x509_cert_parser.o: $(obj)/x509-asn1.h $(obj)/x509_rsakey-asn1.h +$(obj)/x509_cert_parser.o: \ + $(obj)/x509-asn1.h \ + $(obj)/x509_akid-asn1.h \ + $(obj)/x509_rsakey-asn1.h $(obj)/x509-asn1.o: $(obj)/x509-asn1.c $(obj)/x509-asn1.h +$(obj)/x509_akid-asn1.o: $(obj)/x509_akid-asn1.c $(obj)/x509_akid-asn1.h $(obj)/x509_rsakey-asn1.o: $(obj)/x509_rsakey-asn1.c $(obj)/x509_rsakey-asn1.h clean-files += x509-asn1.c x509-asn1.h +clean-files += x509_akid-asn1.c x509_akid-asn1.h clean-files += x509_rsakey-asn1.c x509_rsakey-asn1.h # 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/mscode_parser.c b/crypto/asymmetric_keys/mscode_parser.c index 214a992123cd..adcef59eec0b 100644 --- a/crypto/asymmetric_keys/mscode_parser.c +++ b/crypto/asymmetric_keys/mscode_parser.c @@ -97,6 +97,15 @@ int mscode_note_digest_algo(void *context, size_t hdrlen, case OID_sha256: ctx->digest_algo = HASH_ALGO_SHA256; break; + case OID_sha384: + ctx->digest_algo = HASH_ALGO_SHA384; + break; + case OID_sha512: + ctx->digest_algo = HASH_ALGO_SHA512; + break; + case OID_sha224: + ctx->digest_algo = HASH_ALGO_SHA224; + break; case OID__NR: sprint_oid(value, vlen, buffer, sizeof(buffer)); diff --git a/crypto/asymmetric_keys/pkcs7.asn1 b/crypto/asymmetric_keys/pkcs7.asn1 index a5a14ef28c86..1eca740b816a 100644 --- a/crypto/asymmetric_keys/pkcs7.asn1 +++ b/crypto/asymmetric_keys/pkcs7.asn1 @@ -1,14 +1,14 @@ PKCS7ContentInfo ::= SEQUENCE { - contentType ContentType, + contentType ContentType ({ pkcs7_check_content_type }), content [0] EXPLICIT SignedData OPTIONAL } ContentType ::= OBJECT IDENTIFIER ({ pkcs7_note_OID }) SignedData ::= SEQUENCE { - version INTEGER, + version INTEGER ({ pkcs7_note_signeddata_version }), digestAlgorithms DigestAlgorithmIdentifiers, - contentInfo ContentInfo, + contentInfo ContentInfo ({ pkcs7_note_content }), certificates CHOICE { certSet [0] IMPLICIT ExtendedCertificatesAndCertificates, certSequence [2] IMPLICIT Certificates @@ -21,7 +21,7 @@ SignedData ::= SEQUENCE { } ContentInfo ::= SEQUENCE { - contentType ContentType, + contentType ContentType ({ pkcs7_note_OID }), content [0] EXPLICIT Data OPTIONAL } @@ -68,8 +68,8 @@ SignerInfos ::= CHOICE { } SignerInfo ::= SEQUENCE { - version INTEGER, - issuerAndSerialNumber IssuerAndSerialNumber, + version INTEGER ({ pkcs7_note_signerinfo_version }), + sid SignerIdentifier, -- CMS variant, not PKCS#7 digestAlgorithm DigestAlgorithmIdentifier ({ pkcs7_sig_note_digest_algo }), authenticatedAttributes CHOICE { aaSet [0] IMPLICIT SetOfAuthenticatedAttribute @@ -88,6 +88,12 @@ SignerInfo ::= SEQUENCE { } OPTIONAL } ({ pkcs7_note_signed_info }) +SignerIdentifier ::= CHOICE { + -- RFC5652 sec 5.3 + issuerAndSerialNumber IssuerAndSerialNumber, + subjectKeyIdentifier [0] IMPLICIT SubjectKeyIdentifier +} + IssuerAndSerialNumber ::= SEQUENCE { issuer Name ({ pkcs7_sig_note_issuer }), serialNumber CertificateSerialNumber ({ pkcs7_sig_note_serial }) @@ -95,6 +101,8 @@ IssuerAndSerialNumber ::= SEQUENCE { CertificateSerialNumber ::= INTEGER +SubjectKeyIdentifier ::= OCTET STRING ({ pkcs7_sig_note_skid }) + SetOfAuthenticatedAttribute ::= SET OF AuthenticatedAttribute AuthenticatedAttribute ::= SEQUENCE { @@ -103,7 +111,7 @@ AuthenticatedAttribute ::= SEQUENCE { } UnauthenticatedAttribute ::= SEQUENCE { - type OBJECT IDENTIFIER ({ pkcs7_note_OID }), + type OBJECT IDENTIFIER, values SET OF ANY } diff --git a/crypto/asymmetric_keys/pkcs7_key_type.c b/crypto/asymmetric_keys/pkcs7_key_type.c index 3d13b042da73..e2d0edbbc71a 100644 --- a/crypto/asymmetric_keys/pkcs7_key_type.c +++ b/crypto/asymmetric_keys/pkcs7_key_type.c @@ -14,16 +14,26 @@ #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" +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PKCS#7 testing key type"); + +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; @@ -32,6 +42,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); @@ -40,7 +55,7 @@ static int pkcs7_preparse(struct key_preparsed_payload *prep) goto error; } - ret = pkcs7_verify(pkcs7); + ret = pkcs7_verify(pkcs7, usage); if (ret < 0) goto error_free; diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c index 3bd5a1e4c493..758acabf2d81 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -33,6 +33,9 @@ struct pkcs7_parse_context { unsigned raw_serial_size; unsigned raw_issuer_size; const void *raw_issuer; + const void *raw_skid; + unsigned raw_skid_size; + bool expect_skid; }; /* @@ -78,6 +81,30 @@ void pkcs7_free_message(struct pkcs7_message *pkcs7) } EXPORT_SYMBOL_GPL(pkcs7_free_message); +/* + * Check authenticatedAttributes are provided or not provided consistently. + */ +static int pkcs7_check_authattrs(struct pkcs7_message *msg) +{ + struct pkcs7_signed_info *sinfo; + bool want; + + sinfo = msg->signed_infos; + if (sinfo->authattrs) { + want = true; + msg->have_authattrs = true; + } + + for (sinfo = sinfo->next; sinfo; sinfo = sinfo->next) + if (!!sinfo->authattrs != want) + goto inconsistent; + return 0; + +inconsistent: + pr_warn("Inconsistently supplied authAttrs\n"); + return -EINVAL; +} + /** * pkcs7_parse_message - Parse a PKCS#7 message * @data: The raw binary ASN.1 encoded message to be parsed @@ -110,6 +137,10 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen) goto out; } + ret = pkcs7_check_authattrs(ctx->msg); + if (ret < 0) + goto out; + msg = ctx->msg; ctx->msg = NULL; @@ -198,6 +229,14 @@ int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen, case OID_sha256: ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA256; break; + case OID_sha384: + ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA384; + break; + case OID_sha512: + ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA512; + break; + case OID_sha224: + ctx->sinfo->sig.pkey_hash_algo = HASH_ALGO_SHA224; default: printk("Unsupported digest algo: %u\n", ctx->last_oid); return -ENOPKG; @@ -226,6 +265,100 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen, } /* + * We only support signed data [RFC2315 sec 9]. + */ +int pkcs7_check_content_type(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + if (ctx->last_oid != OID_signed_data) { + pr_warn("Only support pkcs7_signedData type\n"); + return -EINVAL; + } + + return 0; +} + +/* + * Note the SignedData version + */ +int pkcs7_note_signeddata_version(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + unsigned version; + + if (vlen != 1) + goto unsupported; + + ctx->msg->version = version = *(const u8 *)value; + switch (version) { + case 1: + /* PKCS#7 SignedData [RFC2315 sec 9.1] + * CMS ver 1 SignedData [RFC5652 sec 5.1] + */ + break; + case 3: + /* CMS ver 3 SignedData [RFC2315 sec 5.1] */ + break; + default: + goto unsupported; + } + + return 0; + +unsupported: + pr_warn("Unsupported SignedData version\n"); + return -EINVAL; +} + +/* + * Note the SignerInfo version + */ +int pkcs7_note_signerinfo_version(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + unsigned version; + + if (vlen != 1) + goto unsupported; + + version = *(const u8 *)value; + switch (version) { + case 1: + /* PKCS#7 SignerInfo [RFC2315 sec 9.2] + * CMS ver 1 SignerInfo [RFC5652 sec 5.3] + */ + if (ctx->msg->version != 1) + goto version_mismatch; + ctx->expect_skid = false; + break; + case 3: + /* CMS ver 3 SignerInfo [RFC2315 sec 5.3] */ + if (ctx->msg->version == 1) + goto version_mismatch; + ctx->expect_skid = true; + break; + default: + goto unsupported; + } + + return 0; + +unsupported: + pr_warn("Unsupported SignerInfo version\n"); + return -EINVAL; +version_mismatch: + pr_warn("SignedData-SignerInfo version mismatch\n"); + return -EBADMSG; +} + +/* * Extract a certificate and store it in the context. */ int pkcs7_extract_cert(void *context, size_t hdrlen, @@ -284,6 +417,25 @@ int pkcs7_note_certificate_list(void *context, size_t hdrlen, } /* + * Note the content type. + */ +int pkcs7_note_content(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + if (ctx->last_oid != OID_data && + ctx->last_oid != OID_msIndirectData) { + pr_warn("Unsupported data type %d\n", ctx->last_oid); + return -EINVAL; + } + + ctx->msg->data_type = ctx->last_oid; + return 0; +} + +/* * Extract the data from the message and store that and its content type OID in * the context. */ @@ -298,45 +450,119 @@ int pkcs7_note_data(void *context, size_t hdrlen, ctx->msg->data = value; ctx->msg->data_len = vlen; ctx->msg->data_hdrlen = hdrlen; - ctx->msg->data_type = ctx->last_oid; return 0; } /* - * Parse authenticated attributes + * Parse authenticated attributes. */ int pkcs7_sig_note_authenticated_attr(void *context, size_t hdrlen, unsigned char tag, const void *value, size_t vlen) { struct pkcs7_parse_context *ctx = context; + struct pkcs7_signed_info *sinfo = ctx->sinfo; + enum OID content_type; pr_devel("AuthAttr: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value); switch (ctx->last_oid) { + case OID_contentType: + if (__test_and_set_bit(sinfo_has_content_type, &sinfo->aa_set)) + goto repeated; + content_type = look_up_OID(value, vlen); + if (content_type != ctx->msg->data_type) { + pr_warn("Mismatch between global data type (%d) and sinfo %u (%d)\n", + ctx->msg->data_type, sinfo->index, + content_type); + return -EBADMSG; + } + return 0; + + case OID_signingTime: + if (__test_and_set_bit(sinfo_has_signing_time, &sinfo->aa_set)) + goto repeated; + /* Should we check that the signing time is consistent + * with the signer's X.509 cert? + */ + return x509_decode_time(&sinfo->signing_time, + hdrlen, tag, value, vlen); + case OID_messageDigest: + if (__test_and_set_bit(sinfo_has_message_digest, &sinfo->aa_set)) + goto repeated; if (tag != ASN1_OTS) return -EBADMSG; - ctx->sinfo->msgdigest = value; - ctx->sinfo->msgdigest_len = vlen; + sinfo->msgdigest = value; + sinfo->msgdigest_len = vlen; + return 0; + + case OID_smimeCapabilites: + if (__test_and_set_bit(sinfo_has_smime_caps, &sinfo->aa_set)) + goto repeated; + if (ctx->msg->data_type != OID_msIndirectData) { + pr_warn("S/MIME Caps only allowed with Authenticode\n"); + return -EKEYREJECTED; + } + return 0; + + /* Microsoft SpOpusInfo seems to be contain cont[0] 16-bit BE + * char URLs and cont[1] 8-bit char URLs. + * + * Microsoft StatementType seems to contain a list of OIDs that + * are also used as extendedKeyUsage types in X.509 certs. + */ + case OID_msSpOpusInfo: + if (__test_and_set_bit(sinfo_has_ms_opus_info, &sinfo->aa_set)) + goto repeated; + goto authenticode_check; + case OID_msStatementType: + if (__test_and_set_bit(sinfo_has_ms_statement_type, &sinfo->aa_set)) + goto repeated; + authenticode_check: + if (ctx->msg->data_type != OID_msIndirectData) { + pr_warn("Authenticode AuthAttrs only allowed with Authenticode\n"); + return -EKEYREJECTED; + } + /* I'm not sure how to validate these */ return 0; default: return 0; } + +repeated: + /* We permit max one item per AuthenticatedAttribute and no repeats */ + pr_warn("Repeated/multivalue AuthAttrs not permitted\n"); + return -EKEYREJECTED; } /* - * Note the set of auth attributes for digestion purposes [RFC2315 9.3] + * Note the set of auth attributes for digestion purposes [RFC2315 sec 9.3] */ int pkcs7_sig_note_set_of_authattrs(void *context, size_t hdrlen, unsigned char tag, const void *value, size_t vlen) { struct pkcs7_parse_context *ctx = context; + struct pkcs7_signed_info *sinfo = ctx->sinfo; + + if (!test_bit(sinfo_has_content_type, &sinfo->aa_set) || + !test_bit(sinfo_has_message_digest, &sinfo->aa_set) || + (ctx->msg->data_type == OID_msIndirectData && + !test_bit(sinfo_has_ms_opus_info, &sinfo->aa_set))) { + pr_warn("Missing required AuthAttr\n"); + return -EBADMSG; + } + + if (ctx->msg->data_type != OID_msIndirectData && + test_bit(sinfo_has_ms_opus_info, &sinfo->aa_set)) { + pr_warn("Unexpected Authenticode AuthAttr\n"); + return -EBADMSG; + } /* We need to switch the 'CONT 0' to a 'SET OF' when we digest */ - ctx->sinfo->authattrs = value - (hdrlen - 1); - ctx->sinfo->authattrs_len = vlen + (hdrlen - 1); + sinfo->authattrs = value - (hdrlen - 1); + sinfo->authattrs_len = vlen + (hdrlen - 1); return 0; } @@ -367,6 +593,22 @@ int pkcs7_sig_note_issuer(void *context, size_t hdrlen, } /* + * Note the issuing cert's subjectKeyIdentifier + */ +int pkcs7_sig_note_skid(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pkcs7_parse_context *ctx = context; + + pr_devel("SKID: %02x %zu [%*ph]\n", tag, vlen, (unsigned)vlen, value); + + ctx->raw_skid = value; + ctx->raw_skid_size = vlen; + return 0; +} + +/* * Note the signature data */ int pkcs7_sig_note_signature(void *context, size_t hdrlen, @@ -398,14 +640,27 @@ int pkcs7_note_signed_info(void *context, size_t hdrlen, struct pkcs7_signed_info *sinfo = ctx->sinfo; struct asymmetric_key_id *kid; + if (ctx->msg->data_type == OID_msIndirectData && !sinfo->authattrs) { + pr_warn("Authenticode requires AuthAttrs\n"); + return -EBADMSG; + } + /* Generate cert issuer + serial number key ID */ - kid = asymmetric_key_generate_id(ctx->raw_serial, - ctx->raw_serial_size, - ctx->raw_issuer, - ctx->raw_issuer_size); + if (!ctx->expect_skid) { + kid = asymmetric_key_generate_id(ctx->raw_serial, + ctx->raw_serial_size, + ctx->raw_issuer, + ctx->raw_issuer_size); + } else { + kid = asymmetric_key_generate_id(ctx->raw_skid, + ctx->raw_skid_size, + "", 0); + } if (IS_ERR(kid)) return PTR_ERR(kid); + pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data); + sinfo->signing_cert_id = kid; sinfo->index = ++ctx->sinfo_index; *ctx->ppsinfo = sinfo; diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h index efc7dc9b8f9c..a66b19ebcf47 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.h +++ b/crypto/asymmetric_keys/pkcs7_parser.h @@ -21,9 +21,9 @@ struct pkcs7_signed_info { struct pkcs7_signed_info *next; struct x509_certificate *signer; /* Signing certificate (in msg->certs) */ - unsigned index; - bool trusted; - bool unsupported_crypto; /* T if not usable due to missing crypto */ + unsigned index; + bool trusted; + bool unsupported_crypto; /* T if not usable due to missing crypto */ /* Message digest - the digest of the Content Data (or NULL) */ const void *msgdigest; @@ -32,8 +32,18 @@ struct pkcs7_signed_info { /* Authenticated Attribute data (or NULL) */ unsigned authattrs_len; const void *authattrs; + unsigned long aa_set; +#define sinfo_has_content_type 0 +#define sinfo_has_signing_time 1 +#define sinfo_has_message_digest 2 +#define sinfo_has_smime_caps 3 +#define sinfo_has_ms_opus_info 4 +#define sinfo_has_ms_statement_type 5 + time64_t signing_time; - /* Issuing cert serial number and issuer's name */ + /* Issuing cert serial number and issuer's name [PKCS#7 or CMS ver 1] + * or issuing cert's SKID [CMS ver 3]. + */ struct asymmetric_key_id *signing_cert_id; /* Message signature. @@ -50,6 +60,8 @@ struct pkcs7_message { struct x509_certificate *certs; /* Certificate list */ struct x509_certificate *crl; /* Revocation list */ struct pkcs7_signed_info *signed_infos; + u8 version; /* Version of cert (1 -> PKCS#7 or CMS; 3 -> CMS) */ + bool have_authattrs; /* T if have authattrs */ /* Content Data (or NULL) */ enum OID data_type; /* Type of Data */ diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index 1d29376072da..90d6d47965b0 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -54,7 +54,8 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, /* Look to see if this certificate is present in the trusted * keys. */ - key = x509_request_asymmetric_key(trust_keyring, x509->id, + key = x509_request_asymmetric_key(trust_keyring, + x509->id, x509->skid, false); if (!IS_ERR(key)) { /* One of the X.509 certificates in the PKCS#7 message @@ -85,8 +86,10 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, /* No match - see if the root certificate has a signer amongst the * trusted keys. */ - if (last && last->authority) { - key = x509_request_asymmetric_key(trust_keyring, last->authority, + if (last && (last->akid_id || last->akid_skid)) { + key = x509_request_asymmetric_key(trust_keyring, + last->akid_id, + last->akid_skid, false); if (!IS_ERR(key)) { x509 = last; @@ -103,6 +106,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, */ key = x509_request_asymmetric_key(trust_keyring, sinfo->signing_cert_id, + NULL, false); if (!IS_ERR(key)) { pr_devel("sinfo %u: Direct signer is key %x\n", diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index cd455450b069..d20c0b4b880e 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -70,9 +70,15 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, * message digest attribute amongst them which corresponds to the * digest we just calculated. */ - if (sinfo->msgdigest) { + if (sinfo->authattrs) { u8 tag; + if (!sinfo->msgdigest) { + pr_warn("Sig %u: No messageDigest\n", sinfo->index); + ret = -EKEYREJECTED; + goto error; + } + if (sinfo->msgdigest_len != sinfo->sig.digest_size) { pr_debug("Sig %u: Invalid digest size (%u)\n", sinfo->index, sinfo->msgdigest_len); @@ -170,6 +176,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo) { struct x509_certificate *x509 = sinfo->signer, *p; + struct asymmetric_key_id *auth; int ret; kenter(""); @@ -187,11 +194,14 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, goto maybe_missing_crypto_in_x509; pr_debug("- issuer %s\n", x509->issuer); - if (x509->authority) - pr_debug("- authkeyid %*phN\n", - x509->authority->len, x509->authority->data); - - if (!x509->authority || + if (x509->akid_id) + pr_debug("- authkeyid.id %*phN\n", + x509->akid_id->len, x509->akid_id->data); + if (x509->akid_skid) + pr_debug("- authkeyid.skid %*phN\n", + x509->akid_skid->len, x509->akid_skid->data); + + if ((!x509->akid_id && !x509->akid_skid) || strcmp(x509->subject, x509->issuer) == 0) { /* If there's no authority certificate specified, then * the certificate must be self-signed and is the root @@ -215,21 +225,42 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, /* Look through the X.509 certificates in the PKCS#7 message's * list to see if the next one is there. */ - pr_debug("- want %*phN\n", - x509->authority->len, x509->authority->data); - for (p = pkcs7->certs; p; p = p->next) { - if (!p->skid) - continue; - pr_debug("- cmp [%u] %*phN\n", - p->index, p->skid->len, p->skid->data); - if (asymmetric_key_id_same(p->skid, x509->authority)) - goto found_issuer; + auth = x509->akid_id; + if (auth) { + pr_debug("- want %*phN\n", auth->len, auth->data); + for (p = pkcs7->certs; p; p = p->next) { + pr_debug("- cmp [%u] %*phN\n", + p->index, p->id->len, p->id->data); + if (asymmetric_key_id_same(p->id, auth)) + goto found_issuer_check_skid; + } + } else { + auth = x509->akid_skid; + pr_debug("- want %*phN\n", auth->len, auth->data); + for (p = pkcs7->certs; p; p = p->next) { + if (!p->skid) + continue; + pr_debug("- cmp [%u] %*phN\n", + p->index, p->skid->len, p->skid->data); + if (asymmetric_key_id_same(p->skid, auth)) + goto found_issuer; + } } /* We didn't find the root of this chain */ pr_debug("- top\n"); return 0; + found_issuer_check_skid: + /* We matched issuer + serialNumber, but if there's an + * authKeyId.keyId, that must match the CA subjKeyId also. + */ + if (x509->akid_skid && + !asymmetric_key_id_same(p->skid, x509->akid_skid)) { + pr_warn("Sig %u: X.509 chain contains auth-skid nonmatch (%u->%u)\n", + sinfo->index, x509->index, p->index); + return -EKEYREJECTED; + } found_issuer: pr_debug("- subject %s\n", p->subject); if (p->seen) { @@ -289,6 +320,18 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7, pr_devel("Using X.509[%u] for sig %u\n", sinfo->signer->index, sinfo->index); + /* Check that the PKCS#7 signing time is valid according to the X.509 + * certificate. We can't, however, check against the system clock + * since that may not have been set yet and may be wrong. + */ + if (test_bit(sinfo_has_signing_time, &sinfo->aa_set)) { + if (sinfo->signing_time < sinfo->signer->valid_from || + sinfo->signing_time > sinfo->signer->valid_to) { + pr_warn("Message signed outside of X.509 validity window\n"); + return -EKEYREJECTED; + } + } + /* Verify the PKCS#7 binary against the key */ ret = public_key_verify_signature(sinfo->signer->pub, &sinfo->sig); if (ret < 0) @@ -303,6 +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 + * @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 @@ -314,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: * @@ -325,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) +int pkcs7_verify(struct pkcs7_message *pkcs7, + enum key_being_used_for usage) { struct pkcs7_signed_info *sinfo; struct x509_certificate *x509; @@ -334,12 +382,48 @@ int pkcs7_verify(struct pkcs7_message *pkcs7) kenter(""); + switch (usage) { + case VERIFYING_MODULE_SIGNATURE: + if (pkcs7->data_type != OID_data) { + pr_warn("Invalid module sig (not pkcs7-data)\n"); + return -EKEYREJECTED; + } + if (pkcs7->have_authattrs) { + pr_warn("Invalid module sig (has authattrs)\n"); + return -EKEYREJECTED; + } + break; + case VERIFYING_FIRMWARE_SIGNATURE: + if (pkcs7->data_type != OID_data) { + pr_warn("Invalid firmware sig (not pkcs7-data)\n"); + return -EKEYREJECTED; + } + if (!pkcs7->have_authattrs) { + pr_warn("Invalid firmware sig (missing authattrs)\n"); + return -EKEYREJECTED; + } + break; + case VERIFYING_KEXEC_PE_SIGNATURE: + if (pkcs7->data_type != OID_msIndirectData) { + 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++) { ret = x509_get_sig_params(x509); if (ret < 0) return ret; - pr_debug("X.509[%u] %*phN\n", - n, x509->authority->len, x509->authority->data); } for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { @@ -359,3 +443,28 @@ int pkcs7_verify(struct pkcs7_message *pkcs7) return enopkg; } EXPORT_SYMBOL_GPL(pkcs7_verify); + +/** + * pkcs7_supply_detached_data - Supply the data needed to verify a PKCS#7 message + * @pkcs7: The PKCS#7 message + * @data: The data to be verified + * @datalen: The amount of data + * + * Supply the detached data needed to verify a PKCS#7 message. Note that no + * attempt to retain/pin the data is made. That is left to the caller. The + * data will not be modified by pkcs7_verify() and will not be freed when the + * PKCS#7 message is freed. + * + * Returns -EINVAL if data is already supplied in the message, 0 otherwise. + */ +int pkcs7_supply_detached_data(struct pkcs7_message *pkcs7, + const void *data, size_t datalen) +{ + if (pkcs7->data) { + pr_debug("Data already supplied\n"); + return -EINVAL; + } + pkcs7->data = data; + pkcs7->data_len = datalen; + return 0; +} diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index 2f6e4fb1a1ea..81efccbe22d5 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -39,6 +39,7 @@ EXPORT_SYMBOL_GPL(pkey_algo); const char *const pkey_id_type_name[PKEY_ID_TYPE__LAST] = { [PKEY_ID_PGP] = "PGP", [PKEY_ID_X509] = "X509", + [PKEY_ID_PKCS7] = "PKCS#7", }; EXPORT_SYMBOL_GPL(pkey_id_type_name); diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c index 2421f46184ce..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); + ret = pkcs7_verify(pkcs7, usage); if (ret < 0) goto error; diff --git a/crypto/asymmetric_keys/x509_akid.asn1 b/crypto/asymmetric_keys/x509_akid.asn1 new file mode 100644 index 000000000000..1a33231a75a8 --- /dev/null +++ b/crypto/asymmetric_keys/x509_akid.asn1 @@ -0,0 +1,35 @@ +-- X.509 AuthorityKeyIdentifier +-- rfc5280 section 4.2.1.1 + +AuthorityKeyIdentifier ::= SEQUENCE { + keyIdentifier [0] IMPLICIT KeyIdentifier OPTIONAL, + authorityCertIssuer [1] IMPLICIT GeneralNames OPTIONAL, + authorityCertSerialNumber [2] IMPLICIT CertificateSerialNumber OPTIONAL + } + +KeyIdentifier ::= OCTET STRING ({ x509_akid_note_kid }) + +CertificateSerialNumber ::= INTEGER ({ x509_akid_note_serial }) + +GeneralNames ::= SEQUENCE OF GeneralName + +GeneralName ::= CHOICE { + otherName [0] ANY, + rfc822Name [1] IA5String, + dNSName [2] IA5String, + x400Address [3] ANY, + directoryName [4] Name ({ x509_akid_note_name }), + ediPartyName [5] ANY, + uniformResourceIdentifier [6] IA5String, + iPAddress [7] OCTET STRING, + registeredID [8] OBJECT IDENTIFIER + } + +Name ::= SEQUENCE OF RelativeDistinguishedName + +RelativeDistinguishedName ::= SET OF AttributeValueAssertion + +AttributeValueAssertion ::= SEQUENCE { + attributeType OBJECT IDENTIFIER ({ x509_note_OID }), + attributeValue ANY ({ x509_extract_name_segment }) + } diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index a668d90302d3..af71878dc15b 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -18,6 +18,7 @@ #include "public_key.h" #include "x509_parser.h" #include "x509-asn1.h" +#include "x509_akid-asn1.h" #include "x509_rsakey-asn1.h" struct x509_parse_context { @@ -35,6 +36,10 @@ struct x509_parse_context { u16 o_offset; /* Offset of organizationName (O) */ u16 cn_offset; /* Offset of commonName (CN) */ u16 email_offset; /* Offset of emailAddress */ + unsigned raw_akid_size; + const void *raw_akid; /* Raw authorityKeyId in ASN.1 */ + const void *akid_raw_issuer; /* Raw directoryName in authorityKeyId */ + unsigned akid_raw_issuer_size; }; /* @@ -48,7 +53,8 @@ void x509_free_certificate(struct x509_certificate *cert) kfree(cert->subject); kfree(cert->id); kfree(cert->skid); - kfree(cert->authority); + kfree(cert->akid_id); + kfree(cert->akid_skid); kfree(cert->sig.digest); mpi_free(cert->sig.rsa.s); kfree(cert); @@ -85,6 +91,18 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) if (ret < 0) goto error_decode; + /* Decode the AuthorityKeyIdentifier */ + if (ctx->raw_akid) { + pr_devel("AKID: %u %*phN\n", + ctx->raw_akid_size, ctx->raw_akid_size, ctx->raw_akid); + ret = asn1_ber_decoder(&x509_akid_decoder, ctx, + ctx->raw_akid, ctx->raw_akid_size); + if (ret < 0) { + pr_warn("Couldn't decode AuthKeyIdentifier\n"); + goto error_decode; + } + } + /* Decode the public key */ ret = asn1_ber_decoder(&x509_rsakey_decoder, ctx, ctx->key, ctx->key_size); @@ -422,7 +440,6 @@ int x509_process_extension(void *context, size_t hdrlen, struct x509_parse_context *ctx = context; struct asymmetric_key_id *kid; const unsigned char *v = value; - int i; pr_debug("Extension: %u\n", ctx->last_oid); @@ -437,9 +454,7 @@ int x509_process_extension(void *context, size_t hdrlen, ctx->cert->raw_skid_size = vlen; ctx->cert->raw_skid = v; - kid = asymmetric_key_generate_id(ctx->cert->raw_subject, - ctx->cert->raw_subject_size, - v, vlen); + kid = asymmetric_key_generate_id(v, vlen, "", 0); if (IS_ERR(kid)) return PTR_ERR(kid); ctx->cert->skid = kid; @@ -449,117 +464,113 @@ int x509_process_extension(void *context, size_t hdrlen, if (ctx->last_oid == OID_authorityKeyIdentifier) { /* Get hold of the CA key fingerprint */ - if (ctx->cert->authority || vlen < 5) - return -EBADMSG; - - /* Authority Key Identifier must be a Constructed SEQUENCE */ - if (v[0] != (ASN1_SEQ | (ASN1_CONS << 5))) - return -EBADMSG; - - /* Authority Key Identifier is not indefinite length */ - if (unlikely(vlen == ASN1_INDEFINITE_LENGTH)) - return -EBADMSG; - - if (vlen < ASN1_INDEFINITE_LENGTH) { - /* Short Form length */ - if (v[1] != vlen - 2 || - v[2] != SEQ_TAG_KEYID || - v[3] > vlen - 4) - return -EBADMSG; - - vlen = v[3]; - v += 4; - } else { - /* Long Form length */ - size_t seq_len = 0; - size_t sub = v[1] - ASN1_INDEFINITE_LENGTH; - - if (sub > 2) - return -EBADMSG; - - /* calculate the length from subsequent octets */ - v += 2; - for (i = 0; i < sub; i++) { - seq_len <<= 8; - seq_len |= v[i]; - } - - if (seq_len != vlen - 2 - sub || - v[sub] != SEQ_TAG_KEYID || - v[sub + 1] > vlen - 4 - sub) - return -EBADMSG; - - vlen = v[sub + 1]; - v += (sub + 2); - } - - kid = asymmetric_key_generate_id(ctx->cert->raw_issuer, - ctx->cert->raw_issuer_size, - v, vlen); - if (IS_ERR(kid)) - return PTR_ERR(kid); - pr_debug("authkeyid %*phN\n", kid->len, kid->data); - ctx->cert->authority = kid; + ctx->raw_akid = v; + ctx->raw_akid_size = vlen; return 0; } return 0; } -/* - * Record a certificate time. +/** + * x509_decode_time - Decode an X.509 time ASN.1 object + * @_t: The time to fill in + * @hdrlen: The length of the object header + * @tag: The object tag + * @value: The object value + * @vlen: The size of the object value + * + * Decode an ASN.1 universal time or generalised time field into a struct the + * kernel can handle and check it for validity. The time is decoded thus: + * + * [RFC5280 §4.1.2.5] + * CAs conforming to this profile MUST always encode certificate validity + * dates through the year 2049 as UTCTime; certificate validity dates in + * 2050 or later MUST be encoded as GeneralizedTime. Conforming + * applications MUST be able to process validity dates that are encoded in + * either UTCTime or GeneralizedTime. */ -static int x509_note_time(struct tm *tm, size_t hdrlen, - unsigned char tag, - const unsigned char *value, size_t vlen) +int x509_decode_time(time64_t *_t, size_t hdrlen, + unsigned char tag, + const unsigned char *value, size_t vlen) { + static const unsigned char month_lengths[] = { 31, 29, 31, 30, 31, 30, + 31, 31, 30, 31, 30, 31 }; const unsigned char *p = value; + unsigned year, mon, day, hour, min, sec, mon_len; -#define dec2bin(X) ((X) - '0') +#define dec2bin(X) ({ unsigned char x = (X) - '0'; if (x > 9) goto invalid_time; x; }) #define DD2bin(P) ({ unsigned x = dec2bin(P[0]) * 10 + dec2bin(P[1]); P += 2; x; }) if (tag == ASN1_UNITIM) { /* UTCTime: YYMMDDHHMMSSZ */ if (vlen != 13) goto unsupported_time; - tm->tm_year = DD2bin(p); - if (tm->tm_year >= 50) - tm->tm_year += 1900; + year = DD2bin(p); + if (year >= 50) + year += 1900; else - tm->tm_year += 2000; + year += 2000; } else if (tag == ASN1_GENTIM) { /* GenTime: YYYYMMDDHHMMSSZ */ if (vlen != 15) goto unsupported_time; - tm->tm_year = DD2bin(p) * 100 + DD2bin(p); + year = DD2bin(p) * 100 + DD2bin(p); + if (year >= 1950 && year <= 2049) + goto invalid_time; } else { goto unsupported_time; } - tm->tm_year -= 1900; - tm->tm_mon = DD2bin(p) - 1; - tm->tm_mday = DD2bin(p); - tm->tm_hour = DD2bin(p); - tm->tm_min = DD2bin(p); - tm->tm_sec = DD2bin(p); + mon = DD2bin(p); + day = DD2bin(p); + hour = DD2bin(p); + min = DD2bin(p); + sec = DD2bin(p); if (*p != 'Z') goto unsupported_time; + mon_len = month_lengths[mon]; + if (mon == 2) { + if (year % 4 == 0) { + mon_len = 29; + if (year % 100 == 0) { + year /= 100; + if (year % 4 != 0) + mon_len = 28; + } + } + } + + if (year < 1970 || + mon < 1 || mon > 12 || + day < 1 || day > mon_len || + hour < 0 || hour > 23 || + min < 0 || min > 59 || + sec < 0 || sec > 59) + goto invalid_time; + + *_t = mktime64(year, mon, day, hour, min, sec); return 0; unsupported_time: - pr_debug("Got unsupported time [tag %02x]: '%*.*s'\n", - tag, (int)vlen, (int)vlen, value); + pr_debug("Got unsupported time [tag %02x]: '%*phN'\n", + tag, (int)vlen, value); + return -EBADMSG; +invalid_time: + pr_debug("Got invalid time [tag %02x]: '%*phN'\n", + tag, (int)vlen, value); return -EBADMSG; } +EXPORT_SYMBOL_GPL(x509_decode_time); int x509_note_not_before(void *context, size_t hdrlen, unsigned char tag, const void *value, size_t vlen) { struct x509_parse_context *ctx = context; - return x509_note_time(&ctx->cert->valid_from, hdrlen, tag, value, vlen); + return x509_decode_time(&ctx->cert->valid_from, hdrlen, tag, value, vlen); } int x509_note_not_after(void *context, size_t hdrlen, @@ -567,5 +578,71 @@ int x509_note_not_after(void *context, size_t hdrlen, const void *value, size_t vlen) { struct x509_parse_context *ctx = context; - return x509_note_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen); + return x509_decode_time(&ctx->cert->valid_to, hdrlen, tag, value, vlen); +} + +/* + * Note a key identifier-based AuthorityKeyIdentifier + */ +int x509_akid_note_kid(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct x509_parse_context *ctx = context; + struct asymmetric_key_id *kid; + + pr_debug("AKID: keyid: %*phN\n", (int)vlen, value); + + if (ctx->cert->akid_skid) + return 0; + + kid = asymmetric_key_generate_id(value, vlen, "", 0); + if (IS_ERR(kid)) + return PTR_ERR(kid); + pr_debug("authkeyid %*phN\n", kid->len, kid->data); + ctx->cert->akid_skid = kid; + return 0; +} + +/* + * Note a directoryName in an AuthorityKeyIdentifier + */ +int x509_akid_note_name(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct x509_parse_context *ctx = context; + + pr_debug("AKID: name: %*phN\n", (int)vlen, value); + + ctx->akid_raw_issuer = value; + ctx->akid_raw_issuer_size = vlen; + return 0; +} + +/* + * Note a serial number in an AuthorityKeyIdentifier + */ +int x509_akid_note_serial(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct x509_parse_context *ctx = context; + struct asymmetric_key_id *kid; + + pr_debug("AKID: serial: %*phN\n", (int)vlen, value); + + if (!ctx->akid_raw_issuer || ctx->cert->akid_id) + return 0; + + kid = asymmetric_key_generate_id(value, + vlen, + ctx->akid_raw_issuer, + ctx->akid_raw_issuer_size); + if (IS_ERR(kid)) + return PTR_ERR(kid); + + pr_debug("authkeyid %*phN\n", kid->len, kid->data); + ctx->cert->akid_id = kid; + return 0; } diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index 3dfe6b5d6f0b..1de01eaec884 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -19,11 +19,12 @@ struct x509_certificate { struct public_key_signature sig; /* Signature parameters */ char *issuer; /* Name of certificate issuer */ char *subject; /* Name of certificate subject */ - struct asymmetric_key_id *id; /* Serial number + issuer */ + struct asymmetric_key_id *id; /* Issuer + Serial number */ struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */ - struct asymmetric_key_id *authority; /* Authority key identifier (optional) */ - struct tm valid_from; - struct tm valid_to; + struct asymmetric_key_id *akid_id; /* CA AuthKeyId matching ->id (optional) */ + struct asymmetric_key_id *akid_skid; /* CA AuthKeyId matching ->skid (optional) */ + time64_t valid_from; + time64_t valid_to; const void *tbs; /* Signed data */ unsigned tbs_size; /* Size of signed data */ unsigned raw_sig_size; /* Size of sigature */ @@ -48,6 +49,9 @@ struct x509_certificate { */ extern void x509_free_certificate(struct x509_certificate *cert); extern struct x509_certificate *x509_cert_parse(const void *data, size_t datalen); +extern int x509_decode_time(time64_t *_t, size_t hdrlen, + unsigned char tag, + const unsigned char *value, size_t vlen); /* * x509_public_key.c diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 24f17e6c5904..6d88dd15c98d 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -65,23 +65,37 @@ __setup("ca_keys=", ca_keys_setup); /** * x509_request_asymmetric_key - Request a key by X.509 certificate params. * @keyring: The keys to search. - * @kid: The key ID. + * @id: The issuer & serialNumber to look for or NULL. + * @skid: The subjectKeyIdentifier to look for or NULL. * @partial: Use partial match if true, exact if false. * - * Find a key in the given keyring by subject name and key ID. These might, - * for instance, be the issuer name and the authority key ID of an X.509 - * certificate that needs to be verified. + * Find a key in the given keyring by identifier. The preferred identifier is + * the issuer + serialNumber and the fallback identifier is the + * subjectKeyIdentifier. If both are given, the lookup is by the former, but + * the latter must also match. */ struct key *x509_request_asymmetric_key(struct key *keyring, - const struct asymmetric_key_id *kid, + const struct asymmetric_key_id *id, + const struct asymmetric_key_id *skid, bool partial) { - key_ref_t key; - char *id, *p; - + struct key *key; + key_ref_t ref; + const char *lookup; + char *req, *p; + int len; + + if (id) { + lookup = id->data; + len = id->len; + } else { + lookup = skid->data; + len = skid->len; + } + /* Construct an identifier "id:<keyid>". */ - p = id = kmalloc(2 + 1 + kid->len * 2 + 1, GFP_KERNEL); - if (!id) + p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL); + if (!req) return ERR_PTR(-ENOMEM); if (partial) { @@ -92,32 +106,48 @@ struct key *x509_request_asymmetric_key(struct key *keyring, *p++ = 'x'; } *p++ = ':'; - p = bin2hex(p, kid->data, kid->len); + p = bin2hex(p, lookup, len); *p = 0; - pr_debug("Look up: \"%s\"\n", id); + pr_debug("Look up: \"%s\"\n", req); - key = keyring_search(make_key_ref(keyring, 1), - &key_type_asymmetric, id); - if (IS_ERR(key)) - pr_debug("Request for key '%s' err %ld\n", id, PTR_ERR(key)); - kfree(id); + ref = keyring_search(make_key_ref(keyring, 1), + &key_type_asymmetric, req); + if (IS_ERR(ref)) + pr_debug("Request for key '%s' err %ld\n", req, PTR_ERR(ref)); + kfree(req); - if (IS_ERR(key)) { - switch (PTR_ERR(key)) { + if (IS_ERR(ref)) { + switch (PTR_ERR(ref)) { /* Hide some search errors */ case -EACCES: case -ENOTDIR: case -EAGAIN: return ERR_PTR(-ENOKEY); default: - return ERR_CAST(key); + return ERR_CAST(ref); + } + } + + key = key_ref_to_ptr(ref); + if (id && skid) { + const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); + if (!kids->id[1]) { + pr_debug("issuer+serial match, but expected SKID missing\n"); + goto reject; + } + if (!asymmetric_key_id_same(skid, kids->id[1])) { + pr_debug("issuer+serial match, but SKID does not\n"); + goto reject; } } + + pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key)); + return key; - pr_devel("<==%s() = 0 [%x]\n", __func__, - key_serial(key_ref_to_ptr(key))); - return key_ref_to_ptr(key); +reject: + key_put(key); + return ERR_PTR(-EKEYREJECTED); } EXPORT_SYMBOL_GPL(x509_request_asymmetric_key); @@ -227,10 +257,11 @@ static int x509_validate_trust(struct x509_certificate *cert, if (!trust_keyring) return -EOPNOTSUPP; - if (ca_keyid && !asymmetric_key_id_partial(cert->authority, ca_keyid)) + if (ca_keyid && !asymmetric_key_id_partial(cert->akid_skid, ca_keyid)) return -EPERM; - key = x509_request_asymmetric_key(trust_keyring, cert->authority, + key = x509_request_asymmetric_key(trust_keyring, + cert->akid_id, cert->akid_skid, false); if (!IS_ERR(key)) { if (!use_builtin_keys @@ -271,14 +302,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) } pr_devel("Cert Key Algo: %s\n", pkey_algo_name[cert->pub->pkey_algo]); - pr_devel("Cert Valid From: %04ld-%02d-%02d %02d:%02d:%02d\n", - cert->valid_from.tm_year + 1900, cert->valid_from.tm_mon + 1, - cert->valid_from.tm_mday, cert->valid_from.tm_hour, - cert->valid_from.tm_min, cert->valid_from.tm_sec); - pr_devel("Cert Valid To: %04ld-%02d-%02d %02d:%02d:%02d\n", - cert->valid_to.tm_year + 1900, cert->valid_to.tm_mon + 1, - cert->valid_to.tm_mday, cert->valid_to.tm_hour, - cert->valid_to.tm_min, cert->valid_to.tm_sec); + pr_devel("Cert Valid period: %lld-%lld\n", cert->valid_from, cert->valid_to); pr_devel("Cert Signature: %s + %s\n", pkey_algo_name[cert->sig.pkey_algo], hash_algo_name[cert->sig.pkey_hash_algo]); @@ -287,8 +311,9 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) cert->pub->id_type = PKEY_ID_X509; /* Check the signature on the key if it appears to be self-signed */ - if (!cert->authority || - asymmetric_key_id_same(cert->skid, cert->authority)) { + if ((!cert->akid_skid && !cert->akid_id) || + asymmetric_key_id_same(cert->skid, cert->akid_skid) || + asymmetric_key_id_same(cert->id, cert->akid_id)) { ret = x509_check_signature(cert->pub, cert); /* self-signed */ if (ret < 0) goto error_free_cert; |