diff options
-rw-r--r-- | crypto/asymmetric_keys/asymmetric_type.c | 7 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs7_parser.c | 38 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs7_parser.h | 10 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs7_trust.c | 12 | ||||
-rw-r--r-- | crypto/asymmetric_keys/pkcs7_verify.c | 107 | ||||
-rw-r--r-- | crypto/asymmetric_keys/public_key.c | 20 | ||||
-rw-r--r-- | crypto/asymmetric_keys/signature.c | 18 | ||||
-rw-r--r-- | crypto/asymmetric_keys/x509_cert_parser.c | 52 | ||||
-rw-r--r-- | crypto/asymmetric_keys/x509_parser.h | 11 | ||||
-rw-r--r-- | crypto/asymmetric_keys/x509_public_key.c | 182 | ||||
-rw-r--r-- | include/crypto/public_key.h | 6 | ||||
-rw-r--r-- | include/keys/asymmetric-subtype.h | 2 | ||||
-rw-r--r-- | include/keys/asymmetric-type.h | 7 |
13 files changed, 281 insertions, 191 deletions
diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 9f2165b27d52..a79d30128821 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -331,7 +331,8 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) pr_devel("==>%s()\n", __func__); if (subtype) { - subtype->destroy(prep->payload.data[asym_crypto]); + subtype->destroy(prep->payload.data[asym_crypto], + prep->payload.data[asym_auth]); module_put(subtype->owner); } asymmetric_key_free_kids(kids); @@ -346,13 +347,15 @@ static void asymmetric_key_destroy(struct key *key) struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); struct asymmetric_key_ids *kids = key->payload.data[asym_key_ids]; void *data = key->payload.data[asym_crypto]; + void *auth = key->payload.data[asym_auth]; key->payload.data[asym_crypto] = NULL; key->payload.data[asym_subtype] = NULL; key->payload.data[asym_key_ids] = NULL; + key->payload.data[asym_auth] = NULL; if (subtype) { - subtype->destroy(data); + subtype->destroy(data, auth); module_put(subtype->owner); } diff --git a/crypto/asymmetric_keys/pkcs7_parser.c b/crypto/asymmetric_keys/pkcs7_parser.c index 40de03f49ff8..835701613125 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.c +++ b/crypto/asymmetric_keys/pkcs7_parser.c @@ -44,9 +44,7 @@ struct pkcs7_parse_context { static void pkcs7_free_signed_info(struct pkcs7_signed_info *sinfo) { if (sinfo) { - kfree(sinfo->sig.s); - kfree(sinfo->sig.digest); - kfree(sinfo->signing_cert_id); + public_key_signature_free(sinfo->sig); kfree(sinfo); } } @@ -125,6 +123,10 @@ struct pkcs7_message *pkcs7_parse_message(const void *data, size_t datalen) ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); if (!ctx->sinfo) goto out_no_sinfo; + ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature), + GFP_KERNEL); + if (!ctx->sinfo->sig) + goto out_no_sig; ctx->data = (unsigned long)data; ctx->ppcerts = &ctx->certs; @@ -150,6 +152,7 @@ out: ctx->certs = cert->next; x509_free_certificate(cert); } +out_no_sig: pkcs7_free_signed_info(ctx->sinfo); out_no_sinfo: pkcs7_free_message(ctx->msg); @@ -218,25 +221,26 @@ int pkcs7_sig_note_digest_algo(void *context, size_t hdrlen, switch (ctx->last_oid) { case OID_md4: - ctx->sinfo->sig.hash_algo = "md4"; + ctx->sinfo->sig->hash_algo = "md4"; break; case OID_md5: - ctx->sinfo->sig.hash_algo = "md5"; + ctx->sinfo->sig->hash_algo = "md5"; break; case OID_sha1: - ctx->sinfo->sig.hash_algo = "sha1"; + ctx->sinfo->sig->hash_algo = "sha1"; break; case OID_sha256: - ctx->sinfo->sig.hash_algo = "sha256"; + ctx->sinfo->sig->hash_algo = "sha256"; break; case OID_sha384: - ctx->sinfo->sig.hash_algo = "sha384"; + ctx->sinfo->sig->hash_algo = "sha384"; break; case OID_sha512: - ctx->sinfo->sig.hash_algo = "sha512"; + ctx->sinfo->sig->hash_algo = "sha512"; break; case OID_sha224: - ctx->sinfo->sig.hash_algo = "sha224"; + ctx->sinfo->sig->hash_algo = "sha224"; + break; default: printk("Unsupported digest algo: %u\n", ctx->last_oid); return -ENOPKG; @@ -255,7 +259,7 @@ int pkcs7_sig_note_pkey_algo(void *context, size_t hdrlen, switch (ctx->last_oid) { case OID_rsaEncryption: - ctx->sinfo->sig.pkey_algo = "rsa"; + ctx->sinfo->sig->pkey_algo = "rsa"; break; default: printk("Unsupported pkey algo: %u\n", ctx->last_oid); @@ -615,11 +619,11 @@ int pkcs7_sig_note_signature(void *context, size_t hdrlen, { struct pkcs7_parse_context *ctx = context; - ctx->sinfo->sig.s = kmemdup(value, vlen, GFP_KERNEL); - if (!ctx->sinfo->sig.s) + ctx->sinfo->sig->s = kmemdup(value, vlen, GFP_KERNEL); + if (!ctx->sinfo->sig->s) return -ENOMEM; - ctx->sinfo->sig.s_size = vlen; + ctx->sinfo->sig->s_size = vlen; return 0; } @@ -655,12 +659,16 @@ int pkcs7_note_signed_info(void *context, size_t hdrlen, pr_devel("SINFO KID: %u [%*phN]\n", kid->len, kid->len, kid->data); - sinfo->signing_cert_id = kid; + sinfo->sig->auth_ids[0] = kid; sinfo->index = ++ctx->sinfo_index; *ctx->ppsinfo = sinfo; ctx->ppsinfo = &sinfo->next; ctx->sinfo = kzalloc(sizeof(struct pkcs7_signed_info), GFP_KERNEL); if (!ctx->sinfo) return -ENOMEM; + ctx->sinfo->sig = kzalloc(sizeof(struct public_key_signature), + GFP_KERNEL); + if (!ctx->sinfo->sig) + return -ENOMEM; return 0; } diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h index a66b19ebcf47..d5eec31e95b6 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.h +++ b/crypto/asymmetric_keys/pkcs7_parser.h @@ -41,19 +41,17 @@ struct pkcs7_signed_info { #define sinfo_has_ms_statement_type 5 time64_t signing_time; - /* 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. * * This contains the generated digest of _either_ the Content Data or * the Authenticated Attributes [RFC2315 9.3]. If the latter, one of * the attributes contains the digest of the the Content Data within * it. + * + * THis also contains the issuing cert serial number and issuer's name + * [PKCS#7 or CMS ver 1] or issuing cert's SKID [CMS ver 3]. */ - struct public_key_signature sig; + struct public_key_signature *sig; }; struct pkcs7_message { diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index 7d7a39b47c62..b9a5487cd82d 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -27,7 +27,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo, struct key *trust_keyring) { - struct public_key_signature *sig = &sinfo->sig; + struct public_key_signature *sig = sinfo->sig; struct x509_certificate *x509, *last = NULL, *p; struct key *key; bool trusted; @@ -80,16 +80,16 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, might_sleep(); last = x509; - sig = &last->sig; + sig = last->sig; } /* No match - see if the root certificate has a signer amongst the * trusted keys. */ - if (last && (last->akid_id || last->akid_skid)) { + if (last && (last->sig->auth_ids[0] || last->sig->auth_ids[1])) { key = x509_request_asymmetric_key(trust_keyring, - last->akid_id, - last->akid_skid, + last->sig->auth_ids[0], + last->sig->auth_ids[1], false); if (!IS_ERR(key)) { x509 = last; @@ -105,7 +105,7 @@ static int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, * the signed info directly. */ key = x509_request_asymmetric_key(trust_keyring, - sinfo->signing_cert_id, + sinfo->sig->auth_ids[0], NULL, false); if (!IS_ERR(key)) { diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index 50be2a15e531..44b746e9df1b 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -25,34 +25,36 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo) { + struct public_key_signature *sig = sinfo->sig; struct crypto_shash *tfm; struct shash_desc *desc; - size_t digest_size, desc_size; - void *digest; + size_t desc_size; int ret; - kenter(",%u,%s", sinfo->index, sinfo->sig.hash_algo); + kenter(",%u,%s", sinfo->index, sinfo->sig->hash_algo); - if (!sinfo->sig.hash_algo) + if (!sinfo->sig->hash_algo) return -ENOPKG; /* Allocate the hashing algorithm we're going to need and find out how * big the hash operational data will be. */ - tfm = crypto_alloc_shash(sinfo->sig.hash_algo, 0, 0); + tfm = crypto_alloc_shash(sinfo->sig->hash_algo, 0, 0); if (IS_ERR(tfm)) return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); - sinfo->sig.digest_size = digest_size = crypto_shash_digestsize(tfm); + sig->digest_size = crypto_shash_digestsize(tfm); ret = -ENOMEM; - digest = kzalloc(ALIGN(digest_size, __alignof__(*desc)) + desc_size, - GFP_KERNEL); - if (!digest) + sig->digest = kmalloc(sig->digest_size, GFP_KERNEL); + if (!sig->digest) + goto error_no_desc; + + desc = kzalloc(desc_size, GFP_KERNEL); + if (!desc) goto error_no_desc; - desc = PTR_ALIGN(digest + digest_size, __alignof__(*desc)); desc->tfm = tfm; desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; @@ -60,10 +62,11 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, ret = crypto_shash_init(desc); if (ret < 0) goto error; - ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len, digest); + ret = crypto_shash_finup(desc, pkcs7->data, pkcs7->data_len, + sig->digest); if (ret < 0) goto error; - pr_devel("MsgDigest = [%*ph]\n", 8, digest); + pr_devel("MsgDigest = [%*ph]\n", 8, sig->digest); /* However, if there are authenticated attributes, there must be a * message digest attribute amongst them which corresponds to the @@ -78,14 +81,15 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, goto error; } - if (sinfo->msgdigest_len != sinfo->sig.digest_size) { + if (sinfo->msgdigest_len != sig->digest_size) { pr_debug("Sig %u: Invalid digest size (%u)\n", sinfo->index, sinfo->msgdigest_len); ret = -EBADMSG; goto error; } - if (memcmp(digest, sinfo->msgdigest, sinfo->msgdigest_len) != 0) { + if (memcmp(sig->digest, sinfo->msgdigest, + sinfo->msgdigest_len) != 0) { pr_debug("Sig %u: Message digest doesn't match\n", sinfo->index); ret = -EKEYREJECTED; @@ -97,7 +101,7 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, * convert the attributes from a CONT.0 into a SET before we * hash it. */ - memset(digest, 0, sinfo->sig.digest_size); + memset(sig->digest, 0, sig->digest_size); ret = crypto_shash_init(desc); if (ret < 0) @@ -107,17 +111,14 @@ static int pkcs7_digest(struct pkcs7_message *pkcs7, if (ret < 0) goto error; ret = crypto_shash_finup(desc, sinfo->authattrs, - sinfo->authattrs_len, digest); + sinfo->authattrs_len, sig->digest); if (ret < 0) goto error; - pr_devel("AADigest = [%*ph]\n", 8, digest); + pr_devel("AADigest = [%*ph]\n", 8, sig->digest); } - sinfo->sig.digest = digest; - digest = NULL; - error: - kfree(digest); + kfree(desc); error_no_desc: crypto_free_shash(tfm); kleave(" = %d", ret); @@ -144,12 +145,12 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, * PKCS#7 message - but I can't be 100% sure of that. It's * possible this will need element-by-element comparison. */ - if (!asymmetric_key_id_same(x509->id, sinfo->signing_cert_id)) + if (!asymmetric_key_id_same(x509->id, sinfo->sig->auth_ids[0])) continue; pr_devel("Sig %u: Found cert serial match X.509[%u]\n", sinfo->index, certix); - if (x509->pub->pkey_algo != sinfo->sig.pkey_algo) { + if (x509->pub->pkey_algo != sinfo->sig->pkey_algo) { pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n", sinfo->index); continue; @@ -164,7 +165,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, */ pr_debug("Sig %u: Issuing X.509 cert not found (#%*phN)\n", sinfo->index, - sinfo->signing_cert_id->len, sinfo->signing_cert_id->data); + sinfo->sig->auth_ids[0]->len, sinfo->sig->auth_ids[0]->data); return 0; } @@ -174,6 +175,7 @@ static int pkcs7_find_key(struct pkcs7_message *pkcs7, static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, struct pkcs7_signed_info *sinfo) { + struct public_key_signature *sig; struct x509_certificate *x509 = sinfo->signer, *p; struct asymmetric_key_id *auth; int ret; @@ -188,34 +190,26 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, x509->subject, x509->raw_serial_size, x509->raw_serial); x509->seen = true; - ret = x509_get_sig_params(x509); - if (ret < 0) - goto maybe_missing_crypto_in_x509; + if (x509->unsupported_key) + goto unsupported_crypto_in_x509; pr_debug("- issuer %s\n", x509->issuer); - if (x509->akid_id) + sig = x509->sig; + if (sig->auth_ids[0]) pr_debug("- authkeyid.id %*phN\n", - x509->akid_id->len, x509->akid_id->data); - if (x509->akid_skid) + sig->auth_ids[0]->len, sig->auth_ids[0]->data); + if (sig->auth_ids[1]) pr_debug("- authkeyid.skid %*phN\n", - x509->akid_skid->len, x509->akid_skid->data); + sig->auth_ids[1]->len, sig->auth_ids[1]->data); - if ((!x509->akid_id && !x509->akid_skid) || - strcmp(x509->subject, x509->issuer) == 0) { + if (x509->self_signed) { /* If there's no authority certificate specified, then * the certificate must be self-signed and is the root * of the chain. Likewise if the cert is its own * authority. */ - pr_debug("- no auth?\n"); - if (x509->raw_subject_size != x509->raw_issuer_size || - memcmp(x509->raw_subject, x509->raw_issuer, - x509->raw_issuer_size) != 0) - return 0; - - ret = x509_check_signature(x509->pub, x509); - if (ret < 0) - goto maybe_missing_crypto_in_x509; + if (x509->unsupported_sig) + goto unsupported_crypto_in_x509; x509->signer = x509; pr_debug("- self-signed\n"); return 0; @@ -224,7 +218,7 @@ 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. */ - auth = x509->akid_id; + auth = sig->auth_ids[0]; if (auth) { pr_debug("- want %*phN\n", auth->len, auth->data); for (p = pkcs7->certs; p; p = p->next) { @@ -234,7 +228,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, goto found_issuer_check_skid; } } else { - auth = x509->akid_skid; + auth = sig->auth_ids[1]; pr_debug("- want %*phN\n", auth->len, auth->data); for (p = pkcs7->certs; p; p = p->next) { if (!p->skid) @@ -254,8 +248,8 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, /* 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)) { + if (sig->auth_ids[1] && + !asymmetric_key_id_same(p->skid, sig->auth_ids[1])) { pr_warn("Sig %u: X.509 chain contains auth-skid nonmatch (%u->%u)\n", sinfo->index, x509->index, p->index); return -EKEYREJECTED; @@ -267,7 +261,7 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, sinfo->index); return 0; } - ret = x509_check_signature(p->pub, x509); + ret = public_key_verify_signature(p->pub, p->sig); if (ret < 0) return ret; x509->signer = p; @@ -279,16 +273,14 @@ static int pkcs7_verify_sig_chain(struct pkcs7_message *pkcs7, might_sleep(); } -maybe_missing_crypto_in_x509: +unsupported_crypto_in_x509: /* Just prune the certificate chain at this point if we lack some * crypto module to go further. Note, however, we don't want to set - * sinfo->missing_crypto as the signed info block may still be + * sinfo->unsupported_crypto as the signed info block may still be * validatable against an X.509 cert lower in the chain that we have a * trusted copy of. */ - if (ret == -ENOPKG) - return 0; - return ret; + return 0; } /* @@ -332,7 +324,7 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7, } /* Verify the PKCS#7 binary against the key */ - ret = public_key_verify_signature(sinfo->signer->pub, &sinfo->sig); + ret = public_key_verify_signature(sinfo->signer->pub, sinfo->sig); if (ret < 0) return ret; @@ -375,9 +367,8 @@ int pkcs7_verify(struct pkcs7_message *pkcs7, enum key_being_used_for usage) { struct pkcs7_signed_info *sinfo; - struct x509_certificate *x509; int enopkg = -ENOPKG; - int ret, n; + int ret; kenter(""); @@ -419,12 +410,6 @@ int pkcs7_verify(struct pkcs7_message *pkcs7, return -EINVAL; } - for (n = 0, x509 = pkcs7->certs; x509; x509 = x509->next, n++) { - ret = x509_get_sig_params(x509); - if (ret < 0) - return ret; - } - for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { ret = pkcs7_verify_one(pkcs7, sinfo); if (ret < 0) { diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index 0f8b264b3961..fd76b5fc3b3a 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -39,15 +39,23 @@ static void public_key_describe(const struct key *asymmetric_key, /* * Destroy a public key algorithm key. */ -void public_key_destroy(void *payload) +void public_key_free(struct public_key *key) { - struct public_key *key = payload; - - if (key) + if (key) { kfree(key->key); - kfree(key); + kfree(key); + } +} +EXPORT_SYMBOL_GPL(public_key_free); + +/* + * Destroy a public key algorithm key. + */ +static void public_key_destroy(void *payload0, void *payload3) +{ + public_key_free(payload0); + public_key_signature_free(payload3); } -EXPORT_SYMBOL_GPL(public_key_destroy); struct public_key_completion { struct completion completion; diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/signature.c index 004d5fc8e56b..11b7ba170904 100644 --- a/crypto/asymmetric_keys/signature.c +++ b/crypto/asymmetric_keys/signature.c @@ -15,9 +15,27 @@ #include <keys/asymmetric-subtype.h> #include <linux/export.h> #include <linux/err.h> +#include <linux/slab.h> #include <crypto/public_key.h> #include "asymmetric_keys.h" +/* + * Destroy a public key signature. + */ +void public_key_signature_free(struct public_key_signature *sig) +{ + int i; + + if (sig) { + for (i = 0; i < ARRAY_SIZE(sig->auth_ids); i++) + kfree(sig->auth_ids[i]); + kfree(sig->s); + kfree(sig->digest); + kfree(sig); + } +} +EXPORT_SYMBOL_GPL(public_key_signature_free); + /** * verify_signature - Initiate the use of an asymmetric key to verify a signature * @key: The asymmetric key to verify against diff --git a/crypto/asymmetric_keys/x509_cert_parser.c b/crypto/asymmetric_keys/x509_cert_parser.c index 4a29bac70060..865f46ea724f 100644 --- a/crypto/asymmetric_keys/x509_cert_parser.c +++ b/crypto/asymmetric_keys/x509_cert_parser.c @@ -47,15 +47,12 @@ struct x509_parse_context { void x509_free_certificate(struct x509_certificate *cert) { if (cert) { - public_key_destroy(cert->pub); + public_key_free(cert->pub); + public_key_signature_free(cert->sig); kfree(cert->issuer); kfree(cert->subject); kfree(cert->id); kfree(cert->skid); - kfree(cert->akid_id); - kfree(cert->akid_skid); - kfree(cert->sig.digest); - kfree(cert->sig.s); kfree(cert); } } @@ -78,6 +75,9 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) cert->pub = kzalloc(sizeof(struct public_key), GFP_KERNEL); if (!cert->pub) goto error_no_ctx; + cert->sig = kzalloc(sizeof(struct public_key_signature), GFP_KERNEL); + if (!cert->sig) + goto error_no_ctx; ctx = kzalloc(sizeof(struct x509_parse_context), GFP_KERNEL); if (!ctx) goto error_no_ctx; @@ -108,6 +108,11 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) cert->pub->keylen = ctx->key_size; + /* Grab the signature bits */ + ret = x509_get_sig_params(cert); + if (ret < 0) + goto error_decode; + /* Generate cert issuer + serial number key ID */ kid = asymmetric_key_generate_id(cert->raw_serial, cert->raw_serial_size, @@ -119,6 +124,11 @@ struct x509_certificate *x509_cert_parse(const void *data, size_t datalen) } cert->id = kid; + /* Detect self-signed certificates */ + ret = x509_check_for_self_signed(cert); + if (ret < 0) + goto error_decode; + kfree(ctx); return cert; @@ -188,33 +198,33 @@ int x509_note_pkey_algo(void *context, size_t hdrlen, return -ENOPKG; /* Unsupported combination */ case OID_md4WithRSAEncryption: - ctx->cert->sig.hash_algo = "md4"; - ctx->cert->sig.pkey_algo = "rsa"; + ctx->cert->sig->hash_algo = "md4"; + ctx->cert->sig->pkey_algo = "rsa"; break; case OID_sha1WithRSAEncryption: - ctx->cert->sig.hash_algo = "sha1"; - ctx->cert->sig.pkey_algo = "rsa"; + ctx->cert->sig->hash_algo = "sha1"; + ctx->cert->sig->pkey_algo = "rsa"; break; case OID_sha256WithRSAEncryption: - ctx->cert->sig.hash_algo = "sha256"; - ctx->cert->sig.pkey_algo = "rsa"; + ctx->cert->sig->hash_algo = "sha256"; + ctx->cert->sig->pkey_algo = "rsa"; break; case OID_sha384WithRSAEncryption: - ctx->cert->sig.hash_algo = "sha384"; - ctx->cert->sig.pkey_algo = "rsa"; + ctx->cert->sig->hash_algo = "sha384"; + ctx->cert->sig->pkey_algo = "rsa"; break; case OID_sha512WithRSAEncryption: - ctx->cert->sig.hash_algo = "sha512"; - ctx->cert->sig.pkey_algo = "rsa"; + ctx->cert->sig->hash_algo = "sha512"; + ctx->cert->sig->pkey_algo = "rsa"; break; case OID_sha224WithRSAEncryption: - ctx->cert->sig.hash_algo = "sha224"; - ctx->cert->sig.pkey_algo = "rsa"; + ctx->cert->sig->hash_algo = "sha224"; + ctx->cert->sig->pkey_algo = "rsa"; break; } @@ -572,14 +582,14 @@ int x509_akid_note_kid(void *context, size_t hdrlen, pr_debug("AKID: keyid: %*phN\n", (int)vlen, value); - if (ctx->cert->akid_skid) + if (ctx->cert->sig->auth_ids[1]) 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; + ctx->cert->sig->auth_ids[1] = kid; return 0; } @@ -611,7 +621,7 @@ int x509_akid_note_serial(void *context, size_t hdrlen, pr_debug("AKID: serial: %*phN\n", (int)vlen, value); - if (!ctx->akid_raw_issuer || ctx->cert->akid_id) + if (!ctx->akid_raw_issuer || ctx->cert->sig->auth_ids[0]) return 0; kid = asymmetric_key_generate_id(value, @@ -622,6 +632,6 @@ int x509_akid_note_serial(void *context, size_t hdrlen, return PTR_ERR(kid); pr_debug("authkeyid %*phN\n", kid->len, kid->data); - ctx->cert->akid_id = kid; + ctx->cert->sig->auth_ids[0] = kid; return 0; } diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index dbeed6018e63..f24f4d808e7f 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -17,13 +17,11 @@ struct x509_certificate { struct x509_certificate *next; struct x509_certificate *signer; /* Certificate that signed this one */ struct public_key *pub; /* Public key details */ - struct public_key_signature sig; /* Signature parameters */ + struct public_key_signature *sig; /* Signature parameters */ char *issuer; /* Name of certificate issuer */ char *subject; /* Name of certificate subject */ struct asymmetric_key_id *id; /* Issuer + Serial number */ struct asymmetric_key_id *skid; /* Subject + subjectKeyId (optional) */ - 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 */ @@ -42,7 +40,9 @@ struct x509_certificate { bool seen; /* Infinite recursion prevention */ bool verified; bool trusted; - bool unsupported_crypto; /* T if can't be verified due to missing crypto */ + bool self_signed; /* T if self-signed (check unsupported_sig too) */ + bool unsupported_key; /* T if key uses unsupported crypto */ + bool unsupported_sig; /* T if signature uses unsupported crypto */ }; /* @@ -58,5 +58,4 @@ extern int x509_decode_time(time64_t *_t, size_t hdrlen, * x509_public_key.c */ extern int x509_get_sig_params(struct x509_certificate *cert); -extern int x509_check_signature(const struct public_key *pub, - struct x509_certificate *cert); +extern int x509_check_for_self_signed(struct x509_certificate *cert); diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 733c046aacc6..fc77a2bd70ba 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -88,7 +88,7 @@ struct key *x509_request_asymmetric_key(struct key *keyring, lookup = skid->data; len = skid->len; } - + /* Construct an identifier "id:<keyid>". */ p = req = kmalloc(2 + 1 + len * 2 + 1, GFP_KERNEL); if (!req) @@ -137,7 +137,7 @@ struct key *x509_request_asymmetric_key(struct key *keyring, goto reject; } } - + pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key)); return key; @@ -153,90 +153,128 @@ EXPORT_SYMBOL_GPL(x509_request_asymmetric_key); */ int x509_get_sig_params(struct x509_certificate *cert) { + struct public_key_signature *sig = cert->sig; struct crypto_shash *tfm; struct shash_desc *desc; - size_t digest_size, desc_size; - void *digest; + size_t desc_size; int ret; pr_devel("==>%s()\n", __func__); - if (cert->unsupported_crypto) - return -ENOPKG; - if (cert->sig.s) + if (!cert->pub->pkey_algo) + cert->unsupported_key = true; + + if (!sig->pkey_algo) + cert->unsupported_sig = true; + + /* We check the hash if we can - even if we can't then verify it */ + if (!sig->hash_algo) { + cert->unsupported_sig = true; return 0; + } - cert->sig.s = kmemdup(cert->raw_sig, cert->raw_sig_size, - GFP_KERNEL); - if (!cert->sig.s) + sig->s = kmemdup(cert->raw_sig, cert->raw_sig_size, GFP_KERNEL); + if (!sig->s) return -ENOMEM; - cert->sig.s_size = cert->raw_sig_size; + sig->s_size = cert->raw_sig_size; /* Allocate the hashing algorithm we're going to need and find out how * big the hash operational data will be. */ - tfm = crypto_alloc_shash(cert->sig.hash_algo, 0, 0); + tfm = crypto_alloc_shash(sig->hash_algo, 0, 0); if (IS_ERR(tfm)) { if (PTR_ERR(tfm) == -ENOENT) { - cert->unsupported_crypto = true; - return -ENOPKG; + cert->unsupported_sig = true; + return 0; } return PTR_ERR(tfm); } desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); - digest_size = crypto_shash_digestsize(tfm); + sig->digest_size = crypto_shash_digestsize(tfm); - /* We allocate the hash operational data storage on the end of the - * digest storage space. - */ ret = -ENOMEM; - digest = kzalloc(ALIGN(digest_size, __alignof__(*desc)) + desc_size, - GFP_KERNEL); - if (!digest) + sig->digest = kmalloc(sig->digest_size, GFP_KERNEL); + if (!sig->digest) goto error; - cert->sig.digest = digest; - cert->sig.digest_size = digest_size; + desc = kzalloc(desc_size, GFP_KERNEL); + if (!desc) + goto error; - desc = PTR_ALIGN(digest + digest_size, __alignof__(*desc)); desc->tfm = tfm; desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; ret = crypto_shash_init(desc); if (ret < 0) - goto error; + goto error_2; might_sleep(); - ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, digest); + ret = crypto_shash_finup(desc, cert->tbs, cert->tbs_size, sig->digest); + +error_2: + kfree(desc); error: crypto_free_shash(tfm); pr_devel("<==%s() = %d\n", __func__, ret); return ret; } -EXPORT_SYMBOL_GPL(x509_get_sig_params); /* - * Check the signature on a certificate using the provided public key + * Check for self-signedness in an X.509 cert and if found, check the signature + * immediately if we can. */ -int x509_check_signature(const struct public_key *pub, - struct x509_certificate *cert) +int x509_check_for_self_signed(struct x509_certificate *cert) { - int ret; + int ret = 0; pr_devel("==>%s()\n", __func__); - ret = x509_get_sig_params(cert); - if (ret < 0) - return ret; + if (cert->raw_subject_size != cert->raw_issuer_size || + memcmp(cert->raw_subject, cert->raw_issuer, + cert->raw_issuer_size) != 0) + goto not_self_signed; + + if (cert->sig->auth_ids[0] || cert->sig->auth_ids[1]) { + /* If the AKID is present it may have one or two parts. If + * both are supplied, both must match. + */ + bool a = asymmetric_key_id_same(cert->skid, cert->sig->auth_ids[1]); + bool b = asymmetric_key_id_same(cert->id, cert->sig->auth_ids[0]); + + if (!a && !b) + goto not_self_signed; + + ret = -EKEYREJECTED; + if (((a && !b) || (b && !a)) && + cert->sig->auth_ids[0] && cert->sig->auth_ids[1]) + goto out; + } - ret = public_key_verify_signature(pub, &cert->sig); - if (ret == -ENOPKG) - cert->unsupported_crypto = true; - pr_debug("Cert Verification: %d\n", ret); + ret = -EKEYREJECTED; + if (cert->pub->pkey_algo != cert->sig->pkey_algo) + goto out; + + ret = public_key_verify_signature(cert->pub, cert->sig); + if (ret < 0) { + if (ret == -ENOPKG) { + cert->unsupported_sig = true; + ret = 0; + } + goto out; + } + + pr_devel("Cert Self-signature verified"); + cert->self_signed = true; + +out: + pr_devel("<==%s() = %d\n", __func__, ret); return ret; + +not_self_signed: + pr_devel("<==%s() = 0 [not]\n", __func__); + return 0; } -EXPORT_SYMBOL_GPL(x509_check_signature); /* * Check the new certificate against the ones in the trust keyring. If one of @@ -250,25 +288,34 @@ EXPORT_SYMBOL_GPL(x509_check_signature); static int x509_validate_trust(struct x509_certificate *cert, struct key *trust_keyring) { + struct public_key_signature *sig = cert->sig; struct key *key; int ret = 1; + if (!sig->auth_ids[0] && !sig->auth_ids[1]) + return 1; + if (!trust_keyring) return -EOPNOTSUPP; - - if (ca_keyid && !asymmetric_key_id_partial(cert->akid_skid, ca_keyid)) + if (ca_keyid && !asymmetric_key_id_partial(sig->auth_ids[1], ca_keyid)) return -EPERM; + if (cert->unsupported_sig) + return -ENOPKG; key = x509_request_asymmetric_key(trust_keyring, - cert->akid_id, cert->akid_skid, + sig->auth_ids[0], sig->auth_ids[1], false); - if (!IS_ERR(key)) { - if (!use_builtin_keys - || test_bit(KEY_FLAG_BUILTIN, &key->flags)) - ret = x509_check_signature(key->payload.data[asym_crypto], - cert); - key_put(key); + if (IS_ERR(key)) + return PTR_ERR(key); + + if (!use_builtin_keys || + test_bit(KEY_FLAG_BUILTIN, &key->flags)) { + ret = public_key_verify_signature( + key->payload.data[asym_crypto], cert->sig); + if (ret == -ENOPKG) + cert->unsupported_sig = true; } + key_put(key); return ret; } @@ -291,34 +338,41 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) pr_devel("Cert Issuer: %s\n", cert->issuer); pr_devel("Cert Subject: %s\n", cert->subject); - if (!cert->pub->pkey_algo || - !cert->sig.pkey_algo || - !cert->sig.hash_algo) { + if (cert->unsupported_key) { ret = -ENOPKG; goto error_free_cert; } pr_devel("Cert Key Algo: %s\n", cert->pub->pkey_algo); pr_devel("Cert Valid period: %lld-%lld\n", cert->valid_from, cert->valid_to); - pr_devel("Cert Signature: %s + %s\n", - cert->sig.pkey_algo, - cert->sig.hash_algo); cert->pub->id_type = "X509"; - /* Check the signature on the key if it appears to be self-signed */ - 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; - } else if (!prep->trusted) { + /* See if we can derive the trustability of this certificate. + * + * When it comes to self-signed certificates, we cannot evaluate + * trustedness except by the fact that we obtained it from a trusted + * location. So we just rely on x509_validate_trust() failing in this + * case. + * + * Note that there's a possibility of a self-signed cert matching a + * cert that we have (most likely a duplicate that we already trust) - + * in which case it will be marked trusted. + */ + if (cert->unsupported_sig || cert->self_signed) { + public_key_signature_free(cert->sig); + cert->sig = NULL; + } else { + pr_devel("Cert Signature: %s + %s\n", + cert->sig->pkey_algo, cert->sig->hash_algo); + ret = x509_validate_trust(cert, get_system_trusted_keyring()); if (ret) ret = x509_validate_trust(cert, get_ima_mok_keyring()); + if (ret == -EKEYREJECTED) + goto error_free_cert; if (!ret) - prep->trusted = 1; + prep->trusted = true; } /* Propose a description */ @@ -353,6 +407,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) prep->payload.data[asym_subtype] = &public_key_subtype; prep->payload.data[asym_key_ids] = kids; prep->payload.data[asym_crypto] = cert->pub; + prep->payload.data[asym_auth] = cert->sig; prep->description = desc; prep->quotalen = 100; @@ -360,6 +415,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) cert->pub = NULL; cert->id = NULL; cert->skid = NULL; + cert->sig = NULL; desc = NULL; ret = 0; diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index aa730ea7faf8..2f5de5c1a3a0 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -41,12 +41,13 @@ struct public_key { const char *pkey_algo; }; -extern void public_key_destroy(void *payload); +extern void public_key_free(struct public_key *key); /* * Public key cryptography signature data */ struct public_key_signature { + struct asymmetric_key_id *auth_ids[2]; u8 *s; /* Signature */ u32 s_size; /* Number of bytes in signature */ u8 *digest; @@ -55,7 +56,10 @@ struct public_key_signature { const char *hash_algo; }; +extern void public_key_signature_free(struct public_key_signature *sig); + extern struct asymmetric_key_subtype public_key_subtype; + struct key; extern int verify_signature(const struct key *key, const struct public_key_signature *sig); diff --git a/include/keys/asymmetric-subtype.h b/include/keys/asymmetric-subtype.h index 4915d40d3c3c..2480469ce8fb 100644 --- a/include/keys/asymmetric-subtype.h +++ b/include/keys/asymmetric-subtype.h @@ -32,7 +32,7 @@ struct asymmetric_key_subtype { void (*describe)(const struct key *key, struct seq_file *m); /* Destroy a key of this subtype */ - void (*destroy)(void *payload); + void (*destroy)(void *payload_crypto, void *payload_auth); /* Verify the signature on a key of this subtype (optional) */ int (*verify_signature)(const struct key *key, diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h index 59c1df9cf922..70a8775bb444 100644 --- a/include/keys/asymmetric-type.h +++ b/include/keys/asymmetric-type.h @@ -23,9 +23,10 @@ extern struct key_type key_type_asymmetric; * follows: */ enum asymmetric_payload_bits { - asym_crypto, - asym_subtype, - asym_key_ids, + asym_crypto, /* The data representing the key */ + asym_subtype, /* Pointer to an asymmetric_key_subtype struct */ + asym_key_ids, /* Pointer to an asymmetric_key_ids struct */ + asym_auth /* The key's authorisation (signature, parent key ID) */ }; /* |