// SPDX-License-Identifier: GPL-2.0-or-later /* Parse a signed PE binary * * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com)
*/
/* * Check and strip the PE wrapper from around the signature and check that the * remnant looks something like PKCS#7.
*/ staticint pefile_strip_sig_wrapper(constvoid *pebuf, struct pefile_context *ctx)
{ struct win_certificate wrapper; const u8 *pkcs7; unsigned len;
if (ctx->sig_len < sizeof(wrapper)) {
pr_warn("Signature wrapper too short\n"); return -ELIBBAD;
}
/* sbsign rounds up the length of certificate table (in optional * header data directories) to 8 byte alignment. However, the PE * specification states that while entries are 8-byte aligned, this is * not included in their length, and as a result, pesign has not * rounded up since 0.110.
*/ if (wrapper.length > ctx->sig_len) {
pr_warn("Signature wrapper bigger than sig len (%x > %x)\n",
ctx->sig_len, wrapper.length); return -ELIBBAD;
} if (wrapper.revision != WIN_CERT_REVISION_2_0) {
pr_warn("Signature is not revision 2.0\n"); return -ENOTSUPP;
} if (wrapper.cert_type != WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
pr_warn("Signature certificate type is not PKCS\n"); return -ENOTSUPP;
}
/* It looks like the pkcs signature length in wrapper->length and the * size obtained from the data dir entries, which lists the total size * of certificate table, are both aligned to an octaword boundary, so * we may have to deal with some padding.
*/
ctx->sig_len = wrapper.length;
ctx->sig_offset += sizeof(wrapper);
ctx->sig_len -= sizeof(wrapper); if (ctx->sig_len < 4) {
pr_warn("Signature data missing\n"); return -EKEYREJECTED;
}
/* What's left should be a PKCS#7 cert */
pkcs7 = pebuf + ctx->sig_offset; if (pkcs7[0] != (ASN1_CONS_BIT | ASN1_SEQ)) goto not_pkcs7;
switch (pkcs7[1]) { case 0 ... 0x7f:
len = pkcs7[1] + 2; goto check_len; case ASN1_INDEFINITE_LENGTH: return 0; case 0x81:
len = pkcs7[2] + 3; goto check_len; case 0x82:
len = ((pkcs7[2] << 8) | pkcs7[3]) + 4; goto check_len; case 0x83 ... 0xff: return -EMSGSIZE; default: goto not_pkcs7;
}
check_len: if (len <= ctx->sig_len) { /* There may be padding */
ctx->sig_len = len; return 0;
}
not_pkcs7:
pr_warn("Signature data not PKCS#7\n"); return -ELIBBAD;
}
/* * Compare two sections for canonicalisation.
*/ staticint pefile_compare_shdrs(constvoid *a, constvoid *b)
{ conststruct section_header *shdra = a; conststruct section_header *shdrb = b; int rc;
if (shdra->data_addr > shdrb->data_addr) return 1; if (shdrb->data_addr > shdra->data_addr) return -1;
if (shdra->virtual_address > shdrb->virtual_address) return 1; if (shdrb->virtual_address > shdra->virtual_address) return -1;
rc = strcmp(shdra->name, shdrb->name); if (rc != 0) return rc;
if (shdra->virtual_size > shdrb->virtual_size) return 1; if (shdrb->virtual_size > shdra->virtual_size) return -1;
if (shdra->raw_data_size > shdrb->raw_data_size) return 1; if (shdrb->raw_data_size > shdra->raw_data_size) return -1;
return 0;
}
/* * Load the contents of the PE binary into the digest, leaving out the image * checksum and the certificate data block.
*/ staticint pefile_digest_pe_contents(constvoid *pebuf, unsignedint pelen, struct pefile_context *ctx, struct shash_desc *desc)
{ unsigned *canon, tmp, loop, i, hashed_bytes; int ret;
/* Digest the header and data directory, but leave out the image * checksum and the data dirent for the signature.
*/
ret = crypto_shash_update(desc, pebuf, ctx->image_checksum_offset); if (ret < 0) return ret;
tmp = ctx->image_checksum_offset + sizeof(uint32_t);
ret = crypto_shash_update(desc, pebuf + tmp,
ctx->cert_dirent_offset - tmp); if (ret < 0) return ret;
canon = kcalloc(ctx->n_sections, sizeof(unsigned), GFP_KERNEL); if (!canon) return -ENOMEM;
/* We have to canonicalise the section table, so we perform an * insertion sort.
*/
canon[0] = 0; for (loop = 1; loop < ctx->n_sections; loop++) { for (i = 0; i < loop; i++) { if (pefile_compare_shdrs(&ctx->secs[canon[i]],
&ctx->secs[loop]) > 0) {
memmove(&canon[i + 1], &canon[i],
(loop - i) * sizeof(canon[0])); break;
}
}
canon[i] = loop;
}
hashed_bytes = ctx->header_size; for (loop = 0; loop < ctx->n_sections; loop++) {
i = canon[loop]; if (ctx->secs[i].raw_data_size == 0) continue;
ret = crypto_shash_update(desc,
pebuf + ctx->secs[i].data_addr,
ctx->secs[i].raw_data_size); if (ret < 0) {
kfree(canon); return ret;
}
hashed_bytes += ctx->secs[i].raw_data_size;
}
kfree(canon);
if (pelen > hashed_bytes) {
tmp = hashed_bytes + ctx->certs_size;
ret = crypto_shash_update(desc,
pebuf + hashed_bytes,
pelen - tmp); if (ret < 0) return ret;
}
return 0;
}
/* * Digest the contents of the PE binary, leaving out the image checksum and the * certificate data block.
*/ staticint pefile_digest_pe(constvoid *pebuf, unsignedint pelen, struct pefile_context *ctx)
{ struct crypto_shash *tfm; struct shash_desc *desc;
size_t digest_size, desc_size; void *digest; int ret;
kenter(",%s", ctx->digest_algo);
/* Allocate the hashing algorithm we're going to need and find out how * big the hash operational data will be.
*/
tfm = crypto_alloc_shash(ctx->digest_algo, 0, 0); if (IS_ERR(tfm)) return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
/* Check that the PE file digest matches that in the MSCODE part of the * PKCS#7 certificate.
*/ if (memcmp(digest, ctx->digest, ctx->digest_len) != 0) {
pr_warn("Digest mismatch\n");
ret = -EKEYREJECTED;
} else {
pr_debug("The digests match!\n");
}
/** * verify_pefile_signature - Verify the signature on a PE binary image * @pebuf: Buffer containing the PE binary image * @pelen: Length of the binary image * @trusted_keys: Signing certificate(s) to use as starting points * @usage: The use to which the key is being put. * * Validate that the certificate chain inside the PKCS#7 message inside the PE * binary image intersects keys we already know and trust. * * Returns, in order of descending priority: * * (*) -ELIBBAD if the image cannot be parsed, or: * * (*) -EKEYREJECTED if a signature failed to match for which we have a valid * key, or: * * (*) 0 if at least one signature chain intersects with the keys in the trust * keyring, or: * * (*) -ENODATA if there is no signature present. * * (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a * chain. * * (*) -ENOKEY if we couldn't find a match for any of the signature chains in * the message. * * May also return -ENOMEM.
*/ int verify_pefile_signature(constvoid *pebuf, unsigned pelen, struct key *trusted_keys, enum key_being_used_for usage)
{ struct pefile_context ctx; int ret;
kenter("");
memset(&ctx, 0, sizeof(ctx));
ret = pefile_parse_binary(pebuf, pelen, &ctx); if (ret < 0) return ret;
ret = pefile_strip_sig_wrapper(pebuf, &ctx); if (ret < 0) return ret;
ret = verify_pkcs7_signature(NULL, 0,
pebuf + ctx.sig_offset, ctx.sig_len,
trusted_keys, usage,
mscode_parse, &ctx); if (ret < 0) goto error;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.