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> 19*e68503bdSDavid Howells #include <linux/verification.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; 1310aa04094SDavid Howells unsigned len; 13209dacbbdSDavid Howells 13309dacbbdSDavid Howells if (ctx->sig_len < sizeof(wrapper)) { 13409dacbbdSDavid Howells pr_debug("Signature wrapper too short\n"); 13509dacbbdSDavid Howells return -ELIBBAD; 13609dacbbdSDavid Howells } 13709dacbbdSDavid Howells 13809dacbbdSDavid Howells memcpy(&wrapper, pebuf + ctx->sig_offset, sizeof(wrapper)); 13909dacbbdSDavid Howells pr_debug("sig wrapper = { %x, %x, %x }\n", 14009dacbbdSDavid Howells wrapper.length, wrapper.revision, wrapper.cert_type); 14109dacbbdSDavid Howells 14209dacbbdSDavid Howells /* Both pesign and sbsign round up the length of certificate table 14309dacbbdSDavid Howells * (in optional header data directories) to 8 byte alignment. 14409dacbbdSDavid Howells */ 14509dacbbdSDavid Howells if (round_up(wrapper.length, 8) != ctx->sig_len) { 14609dacbbdSDavid Howells pr_debug("Signature wrapper len wrong\n"); 14709dacbbdSDavid Howells return -ELIBBAD; 14809dacbbdSDavid Howells } 14909dacbbdSDavid Howells if (wrapper.revision != WIN_CERT_REVISION_2_0) { 15009dacbbdSDavid Howells pr_debug("Signature is not revision 2.0\n"); 15109dacbbdSDavid Howells return -ENOTSUPP; 15209dacbbdSDavid Howells } 15309dacbbdSDavid Howells if (wrapper.cert_type != WIN_CERT_TYPE_PKCS_SIGNED_DATA) { 15409dacbbdSDavid Howells pr_debug("Signature certificate type is not PKCS\n"); 15509dacbbdSDavid Howells return -ENOTSUPP; 15609dacbbdSDavid Howells } 15709dacbbdSDavid Howells 1580aa04094SDavid Howells /* It looks like the pkcs signature length in wrapper->length and the 1590aa04094SDavid Howells * size obtained from the data dir entries, which lists the total size 1600aa04094SDavid Howells * of certificate table, are both aligned to an octaword boundary, so 1610aa04094SDavid Howells * we may have to deal with some padding. 16209dacbbdSDavid Howells */ 16309dacbbdSDavid Howells ctx->sig_len = wrapper.length; 16409dacbbdSDavid Howells ctx->sig_offset += sizeof(wrapper); 16509dacbbdSDavid Howells ctx->sig_len -= sizeof(wrapper); 1660aa04094SDavid Howells if (ctx->sig_len < 4) { 16709dacbbdSDavid Howells pr_debug("Signature data missing\n"); 16809dacbbdSDavid Howells return -EKEYREJECTED; 16909dacbbdSDavid Howells } 17009dacbbdSDavid Howells 1710aa04094SDavid Howells /* What's left should be a PKCS#7 cert */ 17209dacbbdSDavid Howells pkcs7 = pebuf + ctx->sig_offset; 1730aa04094SDavid Howells if (pkcs7[0] != (ASN1_CONS_BIT | ASN1_SEQ)) 1740aa04094SDavid Howells goto not_pkcs7; 1750aa04094SDavid Howells 1760aa04094SDavid Howells switch (pkcs7[1]) { 1770aa04094SDavid Howells case 0 ... 0x7f: 1780aa04094SDavid Howells len = pkcs7[1] + 2; 1790aa04094SDavid Howells goto check_len; 1800aa04094SDavid Howells case ASN1_INDEFINITE_LENGTH: 18109dacbbdSDavid Howells return 0; 1820aa04094SDavid Howells case 0x81: 1830aa04094SDavid Howells len = pkcs7[2] + 3; 1840aa04094SDavid Howells goto check_len; 1850aa04094SDavid Howells case 0x82: 1860aa04094SDavid Howells len = ((pkcs7[2] << 8) | pkcs7[3]) + 4; 1870aa04094SDavid Howells goto check_len; 1880aa04094SDavid Howells case 0x83 ... 0xff: 18909dacbbdSDavid Howells return -EMSGSIZE; 1900aa04094SDavid Howells default: 1910aa04094SDavid Howells goto not_pkcs7; 19209dacbbdSDavid Howells } 19309dacbbdSDavid Howells 1940aa04094SDavid Howells check_len: 1950aa04094SDavid Howells if (len <= ctx->sig_len) { 1960aa04094SDavid Howells /* There may be padding */ 1970aa04094SDavid Howells ctx->sig_len = len; 1980aa04094SDavid Howells return 0; 1990aa04094SDavid Howells } 2000aa04094SDavid Howells not_pkcs7: 20109dacbbdSDavid Howells pr_debug("Signature data not PKCS#7\n"); 20209dacbbdSDavid Howells return -ELIBBAD; 20309dacbbdSDavid Howells } 20409dacbbdSDavid Howells 205af316fc4SDavid Howells /* 206af316fc4SDavid Howells * Compare two sections for canonicalisation. 207af316fc4SDavid Howells */ 208af316fc4SDavid Howells static int pefile_compare_shdrs(const void *a, const void *b) 209af316fc4SDavid Howells { 210af316fc4SDavid Howells const struct section_header *shdra = a; 211af316fc4SDavid Howells const struct section_header *shdrb = b; 212af316fc4SDavid Howells int rc; 213af316fc4SDavid Howells 214af316fc4SDavid Howells if (shdra->data_addr > shdrb->data_addr) 215af316fc4SDavid Howells return 1; 216af316fc4SDavid Howells if (shdrb->data_addr > shdra->data_addr) 217af316fc4SDavid Howells return -1; 218af316fc4SDavid Howells 219af316fc4SDavid Howells if (shdra->virtual_address > shdrb->virtual_address) 220af316fc4SDavid Howells return 1; 221af316fc4SDavid Howells if (shdrb->virtual_address > shdra->virtual_address) 222af316fc4SDavid Howells return -1; 223af316fc4SDavid Howells 224af316fc4SDavid Howells rc = strcmp(shdra->name, shdrb->name); 225af316fc4SDavid Howells if (rc != 0) 226af316fc4SDavid Howells return rc; 227af316fc4SDavid Howells 228af316fc4SDavid Howells if (shdra->virtual_size > shdrb->virtual_size) 229af316fc4SDavid Howells return 1; 230af316fc4SDavid Howells if (shdrb->virtual_size > shdra->virtual_size) 231af316fc4SDavid Howells return -1; 232af316fc4SDavid Howells 233af316fc4SDavid Howells if (shdra->raw_data_size > shdrb->raw_data_size) 234af316fc4SDavid Howells return 1; 235af316fc4SDavid Howells if (shdrb->raw_data_size > shdra->raw_data_size) 236af316fc4SDavid Howells return -1; 237af316fc4SDavid Howells 238af316fc4SDavid Howells return 0; 239af316fc4SDavid Howells } 240af316fc4SDavid Howells 241af316fc4SDavid Howells /* 242af316fc4SDavid Howells * Load the contents of the PE binary into the digest, leaving out the image 243af316fc4SDavid Howells * checksum and the certificate data block. 244af316fc4SDavid Howells */ 245af316fc4SDavid Howells static int pefile_digest_pe_contents(const void *pebuf, unsigned int pelen, 246af316fc4SDavid Howells struct pefile_context *ctx, 247af316fc4SDavid Howells struct shash_desc *desc) 248af316fc4SDavid Howells { 249af316fc4SDavid Howells unsigned *canon, tmp, loop, i, hashed_bytes; 250af316fc4SDavid Howells int ret; 251af316fc4SDavid Howells 252af316fc4SDavid Howells /* Digest the header and data directory, but leave out the image 253af316fc4SDavid Howells * checksum and the data dirent for the signature. 254af316fc4SDavid Howells */ 255af316fc4SDavid Howells ret = crypto_shash_update(desc, pebuf, ctx->image_checksum_offset); 256af316fc4SDavid Howells if (ret < 0) 257af316fc4SDavid Howells return ret; 258af316fc4SDavid Howells 259af316fc4SDavid Howells tmp = ctx->image_checksum_offset + sizeof(uint32_t); 260af316fc4SDavid Howells ret = crypto_shash_update(desc, pebuf + tmp, 261af316fc4SDavid Howells ctx->cert_dirent_offset - tmp); 262af316fc4SDavid Howells if (ret < 0) 263af316fc4SDavid Howells return ret; 264af316fc4SDavid Howells 265af316fc4SDavid Howells tmp = ctx->cert_dirent_offset + sizeof(struct data_dirent); 266af316fc4SDavid Howells ret = crypto_shash_update(desc, pebuf + tmp, ctx->header_size - tmp); 267af316fc4SDavid Howells if (ret < 0) 268af316fc4SDavid Howells return ret; 269af316fc4SDavid Howells 270af316fc4SDavid Howells canon = kcalloc(ctx->n_sections, sizeof(unsigned), GFP_KERNEL); 271af316fc4SDavid Howells if (!canon) 272af316fc4SDavid Howells return -ENOMEM; 273af316fc4SDavid Howells 274af316fc4SDavid Howells /* We have to canonicalise the section table, so we perform an 275af316fc4SDavid Howells * insertion sort. 276af316fc4SDavid Howells */ 277af316fc4SDavid Howells canon[0] = 0; 278af316fc4SDavid Howells for (loop = 1; loop < ctx->n_sections; loop++) { 279af316fc4SDavid Howells for (i = 0; i < loop; i++) { 280af316fc4SDavid Howells if (pefile_compare_shdrs(&ctx->secs[canon[i]], 281af316fc4SDavid Howells &ctx->secs[loop]) > 0) { 282af316fc4SDavid Howells memmove(&canon[i + 1], &canon[i], 283af316fc4SDavid Howells (loop - i) * sizeof(canon[0])); 284af316fc4SDavid Howells break; 285af316fc4SDavid Howells } 286af316fc4SDavid Howells } 287af316fc4SDavid Howells canon[i] = loop; 288af316fc4SDavid Howells } 289af316fc4SDavid Howells 290af316fc4SDavid Howells hashed_bytes = ctx->header_size; 291af316fc4SDavid Howells for (loop = 0; loop < ctx->n_sections; loop++) { 292af316fc4SDavid Howells i = canon[loop]; 293af316fc4SDavid Howells if (ctx->secs[i].raw_data_size == 0) 294af316fc4SDavid Howells continue; 295af316fc4SDavid Howells ret = crypto_shash_update(desc, 296af316fc4SDavid Howells pebuf + ctx->secs[i].data_addr, 297af316fc4SDavid Howells ctx->secs[i].raw_data_size); 298af316fc4SDavid Howells if (ret < 0) { 299af316fc4SDavid Howells kfree(canon); 300af316fc4SDavid Howells return ret; 301af316fc4SDavid Howells } 302af316fc4SDavid Howells hashed_bytes += ctx->secs[i].raw_data_size; 303af316fc4SDavid Howells } 304af316fc4SDavid Howells kfree(canon); 305af316fc4SDavid Howells 306af316fc4SDavid Howells if (pelen > hashed_bytes) { 307af316fc4SDavid Howells tmp = hashed_bytes + ctx->certs_size; 308af316fc4SDavid Howells ret = crypto_shash_update(desc, 309af316fc4SDavid Howells pebuf + hashed_bytes, 310af316fc4SDavid Howells pelen - tmp); 311af316fc4SDavid Howells if (ret < 0) 312af316fc4SDavid Howells return ret; 313af316fc4SDavid Howells } 314af316fc4SDavid Howells 315af316fc4SDavid Howells return 0; 316af316fc4SDavid Howells } 317af316fc4SDavid Howells 318af316fc4SDavid Howells /* 319af316fc4SDavid Howells * Digest the contents of the PE binary, leaving out the image checksum and the 320af316fc4SDavid Howells * certificate data block. 321af316fc4SDavid Howells */ 322af316fc4SDavid Howells static int pefile_digest_pe(const void *pebuf, unsigned int pelen, 323af316fc4SDavid Howells struct pefile_context *ctx) 324af316fc4SDavid Howells { 325af316fc4SDavid Howells struct crypto_shash *tfm; 326af316fc4SDavid Howells struct shash_desc *desc; 327af316fc4SDavid Howells size_t digest_size, desc_size; 328af316fc4SDavid Howells void *digest; 329af316fc4SDavid Howells int ret; 330af316fc4SDavid Howells 3314e8ae72aSDavid Howells kenter(",%s", ctx->digest_algo); 332af316fc4SDavid Howells 333af316fc4SDavid Howells /* Allocate the hashing algorithm we're going to need and find out how 334af316fc4SDavid Howells * big the hash operational data will be. 335af316fc4SDavid Howells */ 3364e8ae72aSDavid Howells tfm = crypto_alloc_shash(ctx->digest_algo, 0, 0); 337af316fc4SDavid Howells if (IS_ERR(tfm)) 338af316fc4SDavid Howells return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm); 339af316fc4SDavid Howells 340af316fc4SDavid Howells desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); 341af316fc4SDavid Howells digest_size = crypto_shash_digestsize(tfm); 342af316fc4SDavid Howells 343af316fc4SDavid Howells if (digest_size != ctx->digest_len) { 344af316fc4SDavid Howells pr_debug("Digest size mismatch (%zx != %x)\n", 345af316fc4SDavid Howells digest_size, ctx->digest_len); 346af316fc4SDavid Howells ret = -EBADMSG; 347af316fc4SDavid Howells goto error_no_desc; 348af316fc4SDavid Howells } 349af316fc4SDavid Howells pr_debug("Digest: desc=%zu size=%zu\n", desc_size, digest_size); 350af316fc4SDavid Howells 351af316fc4SDavid Howells ret = -ENOMEM; 352af316fc4SDavid Howells desc = kzalloc(desc_size + digest_size, GFP_KERNEL); 353af316fc4SDavid Howells if (!desc) 354af316fc4SDavid Howells goto error_no_desc; 355af316fc4SDavid Howells 356af316fc4SDavid Howells desc->tfm = tfm; 357af316fc4SDavid Howells desc->flags = CRYPTO_TFM_REQ_MAY_SLEEP; 358af316fc4SDavid Howells ret = crypto_shash_init(desc); 359af316fc4SDavid Howells if (ret < 0) 360af316fc4SDavid Howells goto error; 361af316fc4SDavid Howells 362af316fc4SDavid Howells ret = pefile_digest_pe_contents(pebuf, pelen, ctx, desc); 363af316fc4SDavid Howells if (ret < 0) 364af316fc4SDavid Howells goto error; 365af316fc4SDavid Howells 366af316fc4SDavid Howells digest = (void *)desc + desc_size; 367af316fc4SDavid Howells ret = crypto_shash_final(desc, digest); 368af316fc4SDavid Howells if (ret < 0) 369af316fc4SDavid Howells goto error; 370af316fc4SDavid Howells 371af316fc4SDavid Howells pr_debug("Digest calc = [%*ph]\n", ctx->digest_len, digest); 372af316fc4SDavid Howells 373af316fc4SDavid Howells /* Check that the PE file digest matches that in the MSCODE part of the 374af316fc4SDavid Howells * PKCS#7 certificate. 375af316fc4SDavid Howells */ 376af316fc4SDavid Howells if (memcmp(digest, ctx->digest, ctx->digest_len) != 0) { 377af316fc4SDavid Howells pr_debug("Digest mismatch\n"); 378af316fc4SDavid Howells ret = -EKEYREJECTED; 379af316fc4SDavid Howells } else { 380af316fc4SDavid Howells pr_debug("The digests match!\n"); 381af316fc4SDavid Howells } 382af316fc4SDavid Howells 383af316fc4SDavid Howells error: 384af316fc4SDavid Howells kfree(desc); 385af316fc4SDavid Howells error_no_desc: 386af316fc4SDavid Howells crypto_free_shash(tfm); 387af316fc4SDavid Howells kleave(" = %d", ret); 388af316fc4SDavid Howells return ret; 389af316fc4SDavid Howells } 390af316fc4SDavid Howells 39126d1164bSDavid Howells /** 39226d1164bSDavid Howells * verify_pefile_signature - Verify the signature on a PE binary image 39326d1164bSDavid Howells * @pebuf: Buffer containing the PE binary image 39426d1164bSDavid Howells * @pelen: Length of the binary image 395*e68503bdSDavid Howells * @trust_keys: Signing certificate(s) to use as starting points 39699db4435SDavid Howells * @usage: The use to which the key is being put. 39726d1164bSDavid Howells * 39826d1164bSDavid Howells * Validate that the certificate chain inside the PKCS#7 message inside the PE 39926d1164bSDavid Howells * binary image intersects keys we already know and trust. 40026d1164bSDavid Howells * 40126d1164bSDavid Howells * Returns, in order of descending priority: 40226d1164bSDavid Howells * 40326d1164bSDavid Howells * (*) -ELIBBAD if the image cannot be parsed, or: 40426d1164bSDavid Howells * 40526d1164bSDavid Howells * (*) -EKEYREJECTED if a signature failed to match for which we have a valid 40626d1164bSDavid Howells * key, or: 40726d1164bSDavid Howells * 40826d1164bSDavid Howells * (*) 0 if at least one signature chain intersects with the keys in the trust 40926d1164bSDavid Howells * keyring, or: 41026d1164bSDavid Howells * 41126d1164bSDavid Howells * (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a 41226d1164bSDavid Howells * chain. 41326d1164bSDavid Howells * 41426d1164bSDavid Howells * (*) -ENOKEY if we couldn't find a match for any of the signature chains in 41526d1164bSDavid Howells * the message. 41626d1164bSDavid Howells * 41726d1164bSDavid Howells * May also return -ENOMEM. 41826d1164bSDavid Howells */ 41926d1164bSDavid Howells int verify_pefile_signature(const void *pebuf, unsigned pelen, 420*e68503bdSDavid Howells struct key *trusted_keys, 421*e68503bdSDavid Howells enum key_being_used_for usage) 42226d1164bSDavid Howells { 42326d1164bSDavid Howells struct pefile_context ctx; 42426d1164bSDavid Howells int ret; 42526d1164bSDavid Howells 42626d1164bSDavid Howells kenter(""); 42726d1164bSDavid Howells 42826d1164bSDavid Howells memset(&ctx, 0, sizeof(ctx)); 42926d1164bSDavid Howells ret = pefile_parse_binary(pebuf, pelen, &ctx); 43026d1164bSDavid Howells if (ret < 0) 43126d1164bSDavid Howells return ret; 43226d1164bSDavid Howells 43309dacbbdSDavid Howells ret = pefile_strip_sig_wrapper(pebuf, &ctx); 43409dacbbdSDavid Howells if (ret < 0) 43509dacbbdSDavid Howells return ret; 43609dacbbdSDavid Howells 437*e68503bdSDavid Howells ret = verify_pkcs7_signature(NULL, 0, 438*e68503bdSDavid Howells pebuf + ctx.sig_offset, ctx.sig_len, 439*e68503bdSDavid Howells trusted_keys, -EKEYREJECTED, usage, 440*e68503bdSDavid Howells mscode_parse, &ctx); 4414c0b4b1dSDavid Howells if (ret < 0) 4424c0b4b1dSDavid Howells goto error; 4434c0b4b1dSDavid Howells 4444c0b4b1dSDavid Howells pr_debug("Digest: %u [%*ph]\n", 4454c0b4b1dSDavid Howells ctx.digest_len, ctx.digest_len, ctx.digest); 4464c0b4b1dSDavid Howells 447af316fc4SDavid Howells /* Generate the digest and check against the PKCS7 certificate 448af316fc4SDavid Howells * contents. 449af316fc4SDavid Howells */ 450af316fc4SDavid Howells ret = pefile_digest_pe(pebuf, pelen, &ctx); 4513968280cSDavid Howells 4523968280cSDavid Howells error: 453*e68503bdSDavid Howells kfree(ctx.digest); 4543968280cSDavid Howells return ret; 45526d1164bSDavid Howells } 456