126d1164bSDavid Howells /* Parse a signed PE binary 226d1164bSDavid Howells * 326d1164bSDavid Howells * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. 426d1164bSDavid Howells * Written by David Howells (dhowells@redhat.com) 526d1164bSDavid Howells * 626d1164bSDavid Howells * This program is free software; you can redistribute it and/or 726d1164bSDavid Howells * modify it under the terms of the GNU General Public Licence 826d1164bSDavid Howells * as published by the Free Software Foundation; either version 926d1164bSDavid Howells * 2 of the Licence, or (at your option) any later version. 1026d1164bSDavid Howells */ 1126d1164bSDavid Howells 1226d1164bSDavid Howells #define pr_fmt(fmt) "PEFILE: "fmt 1326d1164bSDavid Howells #include <linux/module.h> 1426d1164bSDavid Howells #include <linux/kernel.h> 1526d1164bSDavid Howells #include <linux/slab.h> 1626d1164bSDavid Howells #include <linux/err.h> 1726d1164bSDavid Howells #include <linux/pe.h> 1809dacbbdSDavid Howells #include <linux/asn1.h> 1926d1164bSDavid Howells #include <crypto/pkcs7.h> 2026d1164bSDavid Howells #include <crypto/hash.h> 2126d1164bSDavid Howells #include "verify_pefile.h" 2226d1164bSDavid Howells 2326d1164bSDavid Howells /* 2426d1164bSDavid Howells * Parse a PE binary. 2526d1164bSDavid Howells */ 2626d1164bSDavid Howells static int pefile_parse_binary(const void *pebuf, unsigned int pelen, 2726d1164bSDavid Howells struct pefile_context *ctx) 2826d1164bSDavid Howells { 2926d1164bSDavid Howells const struct mz_hdr *mz = pebuf; 3026d1164bSDavid Howells const struct pe_hdr *pe; 3126d1164bSDavid Howells const struct pe32_opt_hdr *pe32; 3226d1164bSDavid Howells const struct pe32plus_opt_hdr *pe64; 3326d1164bSDavid Howells const struct data_directory *ddir; 3426d1164bSDavid Howells const struct data_dirent *dde; 3526d1164bSDavid Howells const struct section_header *secs, *sec; 3626d1164bSDavid Howells size_t cursor, datalen = pelen; 3726d1164bSDavid Howells 3826d1164bSDavid Howells kenter(""); 3926d1164bSDavid Howells 4026d1164bSDavid Howells #define chkaddr(base, x, s) \ 4126d1164bSDavid Howells do { \ 4226d1164bSDavid Howells if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \ 4326d1164bSDavid Howells return -ELIBBAD; \ 4426d1164bSDavid Howells } while (0) 4526d1164bSDavid Howells 4626d1164bSDavid Howells chkaddr(0, 0, sizeof(*mz)); 4726d1164bSDavid Howells if (mz->magic != MZ_MAGIC) 4826d1164bSDavid Howells return -ELIBBAD; 4926d1164bSDavid Howells cursor = sizeof(*mz); 5026d1164bSDavid Howells 5126d1164bSDavid Howells chkaddr(cursor, mz->peaddr, sizeof(*pe)); 5226d1164bSDavid Howells pe = pebuf + mz->peaddr; 5326d1164bSDavid Howells if (pe->magic != PE_MAGIC) 5426d1164bSDavid Howells return -ELIBBAD; 5526d1164bSDavid Howells cursor = mz->peaddr + sizeof(*pe); 5626d1164bSDavid Howells 5726d1164bSDavid Howells chkaddr(0, cursor, sizeof(pe32->magic)); 5826d1164bSDavid Howells pe32 = pebuf + cursor; 5926d1164bSDavid Howells pe64 = pebuf + cursor; 6026d1164bSDavid Howells 6126d1164bSDavid Howells switch (pe32->magic) { 6226d1164bSDavid Howells case PE_OPT_MAGIC_PE32: 6326d1164bSDavid Howells chkaddr(0, cursor, sizeof(*pe32)); 6426d1164bSDavid Howells ctx->image_checksum_offset = 6526d1164bSDavid Howells (unsigned long)&pe32->csum - (unsigned long)pebuf; 6626d1164bSDavid Howells ctx->header_size = pe32->header_size; 6726d1164bSDavid Howells cursor += sizeof(*pe32); 6826d1164bSDavid Howells ctx->n_data_dirents = pe32->data_dirs; 6926d1164bSDavid Howells break; 7026d1164bSDavid Howells 7126d1164bSDavid Howells case PE_OPT_MAGIC_PE32PLUS: 7226d1164bSDavid Howells chkaddr(0, cursor, sizeof(*pe64)); 7326d1164bSDavid Howells ctx->image_checksum_offset = 7426d1164bSDavid Howells (unsigned long)&pe64->csum - (unsigned long)pebuf; 7526d1164bSDavid Howells ctx->header_size = pe64->header_size; 7626d1164bSDavid Howells cursor += sizeof(*pe64); 7726d1164bSDavid Howells ctx->n_data_dirents = pe64->data_dirs; 7826d1164bSDavid Howells break; 7926d1164bSDavid Howells 8026d1164bSDavid Howells default: 8126d1164bSDavid Howells pr_debug("Unknown PEOPT magic = %04hx\n", pe32->magic); 8226d1164bSDavid Howells return -ELIBBAD; 8326d1164bSDavid Howells } 8426d1164bSDavid Howells 8526d1164bSDavid Howells pr_debug("checksum @ %x\n", ctx->image_checksum_offset); 8626d1164bSDavid Howells pr_debug("header size = %x\n", ctx->header_size); 8726d1164bSDavid Howells 8826d1164bSDavid Howells if (cursor >= ctx->header_size || ctx->header_size >= datalen) 8926d1164bSDavid Howells return -ELIBBAD; 9026d1164bSDavid Howells 9126d1164bSDavid Howells if (ctx->n_data_dirents > (ctx->header_size - cursor) / sizeof(*dde)) 9226d1164bSDavid Howells return -ELIBBAD; 9326d1164bSDavid Howells 9426d1164bSDavid Howells ddir = pebuf + cursor; 9526d1164bSDavid Howells cursor += sizeof(*dde) * ctx->n_data_dirents; 9626d1164bSDavid Howells 9726d1164bSDavid Howells ctx->cert_dirent_offset = 9826d1164bSDavid Howells (unsigned long)&ddir->certs - (unsigned long)pebuf; 9926d1164bSDavid Howells ctx->certs_size = ddir->certs.size; 10026d1164bSDavid Howells 10126d1164bSDavid Howells if (!ddir->certs.virtual_address || !ddir->certs.size) { 10226d1164bSDavid Howells pr_debug("Unsigned PE binary\n"); 10326d1164bSDavid Howells return -EKEYREJECTED; 10426d1164bSDavid Howells } 10526d1164bSDavid Howells 10626d1164bSDavid Howells chkaddr(ctx->header_size, ddir->certs.virtual_address, 10726d1164bSDavid Howells ddir->certs.size); 10826d1164bSDavid Howells ctx->sig_offset = ddir->certs.virtual_address; 10926d1164bSDavid Howells ctx->sig_len = ddir->certs.size; 11026d1164bSDavid Howells pr_debug("cert = %x @%x [%*ph]\n", 11126d1164bSDavid Howells ctx->sig_len, ctx->sig_offset, 11226d1164bSDavid Howells ctx->sig_len, pebuf + ctx->sig_offset); 11326d1164bSDavid Howells 11426d1164bSDavid Howells ctx->n_sections = pe->sections; 11526d1164bSDavid Howells if (ctx->n_sections > (ctx->header_size - cursor) / sizeof(*sec)) 11626d1164bSDavid Howells return -ELIBBAD; 11726d1164bSDavid Howells ctx->secs = secs = pebuf + cursor; 11826d1164bSDavid Howells 11926d1164bSDavid Howells return 0; 12026d1164bSDavid Howells } 12126d1164bSDavid Howells 12209dacbbdSDavid Howells /* 12309dacbbdSDavid Howells * Check and strip the PE wrapper from around the signature and check that the 12409dacbbdSDavid Howells * remnant looks something like PKCS#7. 12509dacbbdSDavid Howells */ 12609dacbbdSDavid Howells static int pefile_strip_sig_wrapper(const void *pebuf, 12709dacbbdSDavid Howells struct pefile_context *ctx) 12809dacbbdSDavid Howells { 12909dacbbdSDavid Howells struct win_certificate wrapper; 13009dacbbdSDavid Howells const u8 *pkcs7; 13109dacbbdSDavid Howells 13209dacbbdSDavid Howells if (ctx->sig_len < sizeof(wrapper)) { 13309dacbbdSDavid Howells pr_debug("Signature wrapper too short\n"); 13409dacbbdSDavid Howells return -ELIBBAD; 13509dacbbdSDavid Howells } 13609dacbbdSDavid Howells 13709dacbbdSDavid Howells memcpy(&wrapper, pebuf + ctx->sig_offset, sizeof(wrapper)); 13809dacbbdSDavid Howells pr_debug("sig wrapper = { %x, %x, %x }\n", 13909dacbbdSDavid Howells wrapper.length, wrapper.revision, wrapper.cert_type); 14009dacbbdSDavid Howells 14109dacbbdSDavid Howells /* Both pesign and sbsign round up the length of certificate table 14209dacbbdSDavid Howells * (in optional header data directories) to 8 byte alignment. 14309dacbbdSDavid Howells */ 14409dacbbdSDavid Howells if (round_up(wrapper.length, 8) != ctx->sig_len) { 14509dacbbdSDavid Howells pr_debug("Signature wrapper len wrong\n"); 14609dacbbdSDavid Howells return -ELIBBAD; 14709dacbbdSDavid Howells } 14809dacbbdSDavid Howells if (wrapper.revision != WIN_CERT_REVISION_2_0) { 14909dacbbdSDavid Howells pr_debug("Signature is not revision 2.0\n"); 15009dacbbdSDavid Howells return -ENOTSUPP; 15109dacbbdSDavid Howells } 15209dacbbdSDavid Howells if (wrapper.cert_type != WIN_CERT_TYPE_PKCS_SIGNED_DATA) { 15309dacbbdSDavid Howells pr_debug("Signature certificate type is not PKCS\n"); 15409dacbbdSDavid Howells return -ENOTSUPP; 15509dacbbdSDavid Howells } 15609dacbbdSDavid Howells 15709dacbbdSDavid Howells /* Looks like actual pkcs signature length is in wrapper->length. 15809dacbbdSDavid Howells * size obtained from data dir entries lists the total size of 15909dacbbdSDavid Howells * certificate table which is also aligned to octawrod boundary. 16009dacbbdSDavid Howells * 16109dacbbdSDavid Howells * So set signature length field appropriately. 16209dacbbdSDavid Howells */ 16309dacbbdSDavid Howells ctx->sig_len = wrapper.length; 16409dacbbdSDavid Howells ctx->sig_offset += sizeof(wrapper); 16509dacbbdSDavid Howells ctx->sig_len -= sizeof(wrapper); 16609dacbbdSDavid Howells if (ctx->sig_len == 0) { 16709dacbbdSDavid Howells pr_debug("Signature data missing\n"); 16809dacbbdSDavid Howells return -EKEYREJECTED; 16909dacbbdSDavid Howells } 17009dacbbdSDavid Howells 17109dacbbdSDavid Howells /* What's left should a PKCS#7 cert */ 17209dacbbdSDavid Howells pkcs7 = pebuf + ctx->sig_offset; 17309dacbbdSDavid Howells if (pkcs7[0] == (ASN1_CONS_BIT | ASN1_SEQ)) { 17409dacbbdSDavid Howells if (pkcs7[1] == 0x82 && 17509dacbbdSDavid Howells pkcs7[2] == (((ctx->sig_len - 4) >> 8) & 0xff) && 17609dacbbdSDavid Howells pkcs7[3] == ((ctx->sig_len - 4) & 0xff)) 17709dacbbdSDavid Howells return 0; 17809dacbbdSDavid Howells if (pkcs7[1] == 0x80) 17909dacbbdSDavid Howells return 0; 18009dacbbdSDavid Howells if (pkcs7[1] > 0x82) 18109dacbbdSDavid Howells return -EMSGSIZE; 18209dacbbdSDavid Howells } 18309dacbbdSDavid Howells 18409dacbbdSDavid Howells pr_debug("Signature data not PKCS#7\n"); 18509dacbbdSDavid Howells return -ELIBBAD; 18609dacbbdSDavid Howells } 18709dacbbdSDavid Howells 188*af316fc4SDavid Howells /* 189*af316fc4SDavid Howells * Compare two sections for canonicalisation. 190*af316fc4SDavid Howells */ 191*af316fc4SDavid Howells static int pefile_compare_shdrs(const void *a, const void *b) 192*af316fc4SDavid Howells { 193*af316fc4SDavid Howells const struct section_header *shdra = a; 194*af316fc4SDavid Howells const struct section_header *shdrb = b; 195*af316fc4SDavid Howells int rc; 196*af316fc4SDavid Howells 197*af316fc4SDavid Howells if (shdra->data_addr > shdrb->data_addr) 198*af316fc4SDavid Howells return 1; 199*af316fc4SDavid Howells if (shdrb->data_addr > shdra->data_addr) 200*af316fc4SDavid Howells return -1; 201*af316fc4SDavid Howells 202*af316fc4SDavid Howells if (shdra->virtual_address > shdrb->virtual_address) 203*af316fc4SDavid Howells return 1; 204*af316fc4SDavid Howells if (shdrb->virtual_address > shdra->virtual_address) 205*af316fc4SDavid Howells return -1; 206*af316fc4SDavid Howells 207*af316fc4SDavid Howells rc = strcmp(shdra->name, shdrb->name); 208*af316fc4SDavid Howells if (rc != 0) 209*af316fc4SDavid Howells return rc; 210*af316fc4SDavid Howells 211*af316fc4SDavid Howells if (shdra->virtual_size > shdrb->virtual_size) 212*af316fc4SDavid Howells return 1; 213*af316fc4SDavid Howells if (shdrb->virtual_size > shdra->virtual_size) 214*af316fc4SDavid Howells return -1; 215*af316fc4SDavid Howells 216*af316fc4SDavid Howells if (shdra->raw_data_size > shdrb->raw_data_size) 217*af316fc4SDavid Howells return 1; 218*af316fc4SDavid Howells if (shdrb->raw_data_size > shdra->raw_data_size) 219*af316fc4SDavid Howells return -1; 220*af316fc4SDavid Howells 221*af316fc4SDavid Howells return 0; 222*af316fc4SDavid Howells } 223*af316fc4SDavid Howells 224*af316fc4SDavid Howells /* 225*af316fc4SDavid Howells * Load the contents of the PE binary into the digest, leaving out the image 226*af316fc4SDavid Howells * checksum and the certificate data block. 227*af316fc4SDavid Howells */ 228*af316fc4SDavid Howells static int pefile_digest_pe_contents(const void *pebuf, unsigned int pelen, 229*af316fc4SDavid Howells struct pefile_context *ctx, 230*af316fc4SDavid Howells struct shash_desc *desc) 231*af316fc4SDavid Howells { 232*af316fc4SDavid Howells unsigned *canon, tmp, loop, i, hashed_bytes; 233*af316fc4SDavid Howells int ret; 234*af316fc4SDavid Howells 235*af316fc4SDavid Howells /* Digest the header and data directory, but leave out the image 236*af316fc4SDavid Howells * checksum and the data dirent for the signature. 237*af316fc4SDavid Howells */ 238*af316fc4SDavid Howells ret = crypto_shash_update(desc, pebuf, ctx->image_checksum_offset); 239*af316fc4SDavid Howells if (ret < 0) 240*af316fc4SDavid Howells return ret; 241*af316fc4SDavid Howells 242*af316fc4SDavid Howells tmp = ctx->image_checksum_offset + sizeof(uint32_t); 243*af316fc4SDavid Howells ret = crypto_shash_update(desc, pebuf + tmp, 244*af316fc4SDavid Howells ctx->cert_dirent_offset - tmp); 245*af316fc4SDavid Howells if (ret < 0) 246*af316fc4SDavid Howells return ret; 247*af316fc4SDavid Howells 248*af316fc4SDavid Howells tmp = ctx->cert_dirent_offset + sizeof(struct data_dirent); 249*af316fc4SDavid Howells ret = crypto_shash_update(desc, pebuf + tmp, ctx->header_size - tmp); 250*af316fc4SDavid Howells if (ret < 0) 251*af316fc4SDavid Howells return ret; 252*af316fc4SDavid Howells 253*af316fc4SDavid Howells canon = kcalloc(ctx->n_sections, sizeof(unsigned), GFP_KERNEL); 254*af316fc4SDavid Howells if (!canon) 255*af316fc4SDavid Howells return -ENOMEM; 256*af316fc4SDavid Howells 257*af316fc4SDavid Howells /* We have to canonicalise the section table, so we perform an 258*af316fc4SDavid Howells * insertion sort. 259*af316fc4SDavid Howells */ 260*af316fc4SDavid Howells canon[0] = 0; 261*af316fc4SDavid Howells for (loop = 1; loop < ctx->n_sections; loop++) { 262*af316fc4SDavid Howells for (i = 0; i < loop; i++) { 263*af316fc4SDavid Howells if (pefile_compare_shdrs(&ctx->secs[canon[i]], 264*af316fc4SDavid Howells &ctx->secs[loop]) > 0) { 265*af316fc4SDavid Howells memmove(&canon[i + 1], &canon[i], 266*af316fc4SDavid Howells (loop - i) * sizeof(canon[0])); 267*af316fc4SDavid Howells break; 268*af316fc4SDavid Howells } 269*af316fc4SDavid Howells } 270*af316fc4SDavid Howells canon[i] = loop; 271*af316fc4SDavid Howells } 272*af316fc4SDavid Howells 273*af316fc4SDavid Howells hashed_bytes = ctx->header_size; 274*af316fc4SDavid Howells for (loop = 0; loop < ctx->n_sections; loop++) { 275*af316fc4SDavid Howells i = canon[loop]; 276*af316fc4SDavid Howells if (ctx->secs[i].raw_data_size == 0) 277*af316fc4SDavid Howells continue; 278*af316fc4SDavid Howells ret = crypto_shash_update(desc, 279*af316fc4SDavid Howells pebuf + ctx->secs[i].data_addr, 280*af316fc4SDavid Howells ctx->secs[i].raw_data_size); 281*af316fc4SDavid Howells if (ret < 0) { 282*af316fc4SDavid Howells kfree(canon); 283*af316fc4SDavid Howells return ret; 284*af316fc4SDavid Howells } 285*af316fc4SDavid Howells hashed_bytes += ctx->secs[i].raw_data_size; 286*af316fc4SDavid Howells } 287*af316fc4SDavid Howells kfree(canon); 288*af316fc4SDavid Howells 289*af316fc4SDavid Howells if (pelen > hashed_bytes) { 290*af316fc4SDavid Howells tmp = hashed_bytes + ctx->certs_size; 291*af316fc4SDavid Howells ret = crypto_shash_update(desc, 292*af316fc4SDavid Howells pebuf + hashed_bytes, 293*af316fc4SDavid Howells pelen - tmp); 294*af316fc4SDavid Howells if (ret < 0) 295*af316fc4SDavid Howells return ret; 296*af316fc4SDavid Howells } 297*af316fc4SDavid Howells 298*af316fc4SDavid Howells return 0; 299*af316fc4SDavid Howells } 300*af316fc4SDavid Howells 301*af316fc4SDavid Howells /* 302*af316fc4SDavid Howells * Digest the contents of the PE binary, leaving out the image checksum and the 303*af316fc4SDavid Howells * certificate data block. 304*af316fc4SDavid Howells */ 305*af316fc4SDavid Howells static int pefile_digest_pe(const void *pebuf, unsigned int pelen, 306*af316fc4SDavid Howells struct pefile_context *ctx) 307*af316fc4SDavid Howells { 308*af316fc4SDavid Howells struct crypto_shash *tfm; 309*af316fc4SDavid Howells struct shash_desc *desc; 310*af316fc4SDavid Howells size_t digest_size, desc_size; 311*af316fc4SDavid Howells void *digest; 312*af316fc4SDavid Howells int ret; 313*af316fc4SDavid Howells 314*af316fc4SDavid Howells kenter(",%u", ctx->digest_algo); 315*af316fc4SDavid Howells 316*af316fc4SDavid Howells /* Allocate the hashing algorithm we're going to need and find out how 317*af316fc4SDavid Howells * big the hash operational data will be. 318*af316fc4SDavid Howells */ 319*af316fc4SDavid Howells tfm = crypto_alloc_shash(hash_algo_name[ctx->digest_algo], 0, 0); 320*af316fc4SDavid Howells if (IS_ERR(tfm)) 321*af316fc4SDavid Howells return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); 322*af316fc4SDavid Howells 323*af316fc4SDavid Howells desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); 324*af316fc4SDavid Howells digest_size = crypto_shash_digestsize(tfm); 325*af316fc4SDavid Howells 326*af316fc4SDavid Howells if (digest_size != ctx->digest_len) { 327*af316fc4SDavid Howells pr_debug("Digest size mismatch (%zx != %x)\n", 328*af316fc4SDavid Howells digest_size, ctx->digest_len); 329*af316fc4SDavid Howells ret = -EBADMSG; 330*af316fc4SDavid Howells goto error_no_desc; 331*af316fc4SDavid Howells } 332*af316fc4SDavid Howells pr_debug("Digest: desc=%zu size=%zu\n", desc_size, digest_size); 333*af316fc4SDavid Howells 334*af316fc4SDavid Howells ret = -ENOMEM; 335*af316fc4SDavid Howells desc = kzalloc(desc_size + digest_size, GFP_KERNEL); 336*af316fc4SDavid Howells if (!desc) 337*af316fc4SDavid Howells goto error_no_desc; 338*af316fc4SDavid Howells 339*af316fc4SDavid Howells desc->tfm = tfm; 340*af316fc4SDavid Howells desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; 341*af316fc4SDavid Howells ret = crypto_shash_init(desc); 342*af316fc4SDavid Howells if (ret < 0) 343*af316fc4SDavid Howells goto error; 344*af316fc4SDavid Howells 345*af316fc4SDavid Howells ret = pefile_digest_pe_contents(pebuf, pelen, ctx, desc); 346*af316fc4SDavid Howells if (ret < 0) 347*af316fc4SDavid Howells goto error; 348*af316fc4SDavid Howells 349*af316fc4SDavid Howells digest = (void *)desc + desc_size; 350*af316fc4SDavid Howells ret = crypto_shash_final(desc, digest); 351*af316fc4SDavid Howells if (ret < 0) 352*af316fc4SDavid Howells goto error; 353*af316fc4SDavid Howells 354*af316fc4SDavid Howells pr_debug("Digest calc = [%*ph]\n", ctx->digest_len, digest); 355*af316fc4SDavid Howells 356*af316fc4SDavid Howells /* Check that the PE file digest matches that in the MSCODE part of the 357*af316fc4SDavid Howells * PKCS#7 certificate. 358*af316fc4SDavid Howells */ 359*af316fc4SDavid Howells if (memcmp(digest, ctx->digest, ctx->digest_len) != 0) { 360*af316fc4SDavid Howells pr_debug("Digest mismatch\n"); 361*af316fc4SDavid Howells ret = -EKEYREJECTED; 362*af316fc4SDavid Howells } else { 363*af316fc4SDavid Howells pr_debug("The digests match!\n"); 364*af316fc4SDavid Howells } 365*af316fc4SDavid Howells 366*af316fc4SDavid Howells error: 367*af316fc4SDavid Howells kfree(desc); 368*af316fc4SDavid Howells error_no_desc: 369*af316fc4SDavid Howells crypto_free_shash(tfm); 370*af316fc4SDavid Howells kleave(" = %d", ret); 371*af316fc4SDavid Howells return ret; 372*af316fc4SDavid Howells } 373*af316fc4SDavid Howells 37426d1164bSDavid Howells /** 37526d1164bSDavid Howells * verify_pefile_signature - Verify the signature on a PE binary image 37626d1164bSDavid Howells * @pebuf: Buffer containing the PE binary image 37726d1164bSDavid Howells * @pelen: Length of the binary image 37826d1164bSDavid Howells * @trust_keyring: Signing certificates to use as starting points 37926d1164bSDavid Howells * @_trusted: Set to true if trustworth, false otherwise 38026d1164bSDavid Howells * 38126d1164bSDavid Howells * Validate that the certificate chain inside the PKCS#7 message inside the PE 38226d1164bSDavid Howells * binary image intersects keys we already know and trust. 38326d1164bSDavid Howells * 38426d1164bSDavid Howells * Returns, in order of descending priority: 38526d1164bSDavid Howells * 38626d1164bSDavid Howells * (*) -ELIBBAD if the image cannot be parsed, or: 38726d1164bSDavid Howells * 38826d1164bSDavid Howells * (*) -EKEYREJECTED if a signature failed to match for which we have a valid 38926d1164bSDavid Howells * key, or: 39026d1164bSDavid Howells * 39126d1164bSDavid Howells * (*) 0 if at least one signature chain intersects with the keys in the trust 39226d1164bSDavid Howells * keyring, or: 39326d1164bSDavid Howells * 39426d1164bSDavid Howells * (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a 39526d1164bSDavid Howells * chain. 39626d1164bSDavid Howells * 39726d1164bSDavid Howells * (*) -ENOKEY if we couldn't find a match for any of the signature chains in 39826d1164bSDavid Howells * the message. 39926d1164bSDavid Howells * 40026d1164bSDavid Howells * May also return -ENOMEM. 40126d1164bSDavid Howells */ 40226d1164bSDavid Howells int verify_pefile_signature(const void *pebuf, unsigned pelen, 40326d1164bSDavid Howells struct key *trusted_keyring, bool *_trusted) 40426d1164bSDavid Howells { 4053968280cSDavid Howells struct pkcs7_message *pkcs7; 40626d1164bSDavid Howells struct pefile_context ctx; 4073968280cSDavid Howells const void *data; 4083968280cSDavid Howells size_t datalen; 40926d1164bSDavid Howells int ret; 41026d1164bSDavid Howells 41126d1164bSDavid Howells kenter(""); 41226d1164bSDavid Howells 41326d1164bSDavid Howells memset(&ctx, 0, sizeof(ctx)); 41426d1164bSDavid Howells ret = pefile_parse_binary(pebuf, pelen, &ctx); 41526d1164bSDavid Howells if (ret < 0) 41626d1164bSDavid Howells return ret; 41726d1164bSDavid Howells 41809dacbbdSDavid Howells ret = pefile_strip_sig_wrapper(pebuf, &ctx); 41909dacbbdSDavid Howells if (ret < 0) 42009dacbbdSDavid Howells return ret; 42109dacbbdSDavid Howells 4223968280cSDavid Howells pkcs7 = pkcs7_parse_message(pebuf + ctx.sig_offset, ctx.sig_len); 4233968280cSDavid Howells if (IS_ERR(pkcs7)) 4243968280cSDavid Howells return PTR_ERR(pkcs7); 4253968280cSDavid Howells ctx.pkcs7 = pkcs7; 4263968280cSDavid Howells 4273968280cSDavid Howells ret = pkcs7_get_content_data(ctx.pkcs7, &data, &datalen, false); 4283968280cSDavid Howells if (ret < 0 || datalen == 0) { 4293968280cSDavid Howells pr_devel("PKCS#7 message does not contain data\n"); 4303968280cSDavid Howells ret = -EBADMSG; 4313968280cSDavid Howells goto error; 4323968280cSDavid Howells } 4333968280cSDavid Howells 4344c0b4b1dSDavid Howells ret = mscode_parse(&ctx); 4354c0b4b1dSDavid Howells if (ret < 0) 4364c0b4b1dSDavid Howells goto error; 4374c0b4b1dSDavid Howells 4384c0b4b1dSDavid Howells pr_debug("Digest: %u [%*ph]\n", 4394c0b4b1dSDavid Howells ctx.digest_len, ctx.digest_len, ctx.digest); 4404c0b4b1dSDavid Howells 441*af316fc4SDavid Howells /* Generate the digest and check against the PKCS7 certificate 442*af316fc4SDavid Howells * contents. 443*af316fc4SDavid Howells */ 444*af316fc4SDavid Howells ret = pefile_digest_pe(pebuf, pelen, &ctx); 445*af316fc4SDavid Howells if (ret < 0) 446*af316fc4SDavid Howells goto error; 447*af316fc4SDavid Howells 448*af316fc4SDavid Howells ret = pkcs7_verify(pkcs7); 449*af316fc4SDavid Howells if (ret < 0) 450*af316fc4SDavid Howells goto error; 451*af316fc4SDavid Howells 4523968280cSDavid Howells ret = -ENOANO; // Not yet complete 4533968280cSDavid Howells 4543968280cSDavid Howells error: 4553968280cSDavid Howells pkcs7_free_message(ctx.pkcs7); 4563968280cSDavid Howells return ret; 45726d1164bSDavid Howells } 458