|
@@ -185,6 +185,192 @@ static int pefile_strip_sig_wrapper(const void *pebuf,
|
|
|
return -ELIBBAD;
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * Compare two sections for canonicalisation.
|
|
|
+ */
|
|
|
+static int pefile_compare_shdrs(const void *a, const void *b)
|
|
|
+{
|
|
|
+ const struct section_header *shdra = a;
|
|
|
+ const struct 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.
|
|
|
+ */
|
|
|
+static int pefile_digest_pe_contents(const void *pebuf, unsigned int 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;
|
|
|
+
|
|
|
+ tmp = ctx->cert_dirent_offset + sizeof(struct data_dirent);
|
|
|
+ ret = crypto_shash_update(desc, pebuf + tmp, ctx->header_size - 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.
|
|
|
+ */
|
|
|
+static int pefile_digest_pe(const void *pebuf, unsigned int pelen,
|
|
|
+ struct pefile_context *ctx)
|
|
|
+{
|
|
|
+ struct crypto_shash *tfm;
|
|
|
+ struct shash_desc *desc;
|
|
|
+ size_t digest_size, desc_size;
|
|
|
+ void *digest;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ kenter(",%u", 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(hash_algo_name[ctx->digest_algo], 0, 0);
|
|
|
+ if (IS_ERR(tfm))
|
|
|
+ return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
|
|
|
+
|
|
|
+ desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
|
|
|
+ digest_size = crypto_shash_digestsize(tfm);
|
|
|
+
|
|
|
+ if (digest_size != ctx->digest_len) {
|
|
|
+ pr_debug("Digest size mismatch (%zx != %x)\n",
|
|
|
+ digest_size, ctx->digest_len);
|
|
|
+ ret = -EBADMSG;
|
|
|
+ goto error_no_desc;
|
|
|
+ }
|
|
|
+ pr_debug("Digest: desc=%zu size=%zu\n", desc_size, digest_size);
|
|
|
+
|
|
|
+ ret = -ENOMEM;
|
|
|
+ desc = kzalloc(desc_size + digest_size, GFP_KERNEL);
|
|
|
+ if (!desc)
|
|
|
+ goto error_no_desc;
|
|
|
+
|
|
|
+ desc->tfm = tfm;
|
|
|
+ desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP;
|
|
|
+ ret = crypto_shash_init(desc);
|
|
|
+ if (ret < 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ ret = pefile_digest_pe_contents(pebuf, pelen, ctx, desc);
|
|
|
+ if (ret < 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ digest = (void *)desc + desc_size;
|
|
|
+ ret = crypto_shash_final(desc, digest);
|
|
|
+ if (ret < 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ pr_debug("Digest calc = [%*ph]\n", ctx->digest_len, digest);
|
|
|
+
|
|
|
+ /* 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_debug("Digest mismatch\n");
|
|
|
+ ret = -EKEYREJECTED;
|
|
|
+ } else {
|
|
|
+ pr_debug("The digests match!\n");
|
|
|
+ }
|
|
|
+
|
|
|
+error:
|
|
|
+ kfree(desc);
|
|
|
+error_no_desc:
|
|
|
+ crypto_free_shash(tfm);
|
|
|
+ kleave(" = %d", ret);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* verify_pefile_signature - Verify the signature on a PE binary image
|
|
|
* @pebuf: Buffer containing the PE binary image
|
|
@@ -252,6 +438,17 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen,
|
|
|
pr_debug("Digest: %u [%*ph]\n",
|
|
|
ctx.digest_len, ctx.digest_len, ctx.digest);
|
|
|
|
|
|
+ /* Generate the digest and check against the PKCS7 certificate
|
|
|
+ * contents.
|
|
|
+ */
|
|
|
+ ret = pefile_digest_pe(pebuf, pelen, &ctx);
|
|
|
+ if (ret < 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
+ ret = pkcs7_verify(pkcs7);
|
|
|
+ if (ret < 0)
|
|
|
+ goto error;
|
|
|
+
|
|
|
ret = -ENOANO; // Not yet complete
|
|
|
|
|
|
error:
|