forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
pefile: Parse a PE binary to find a key and a signature contained the…
…rein Parse a PE binary to find a key and a signature contained therein. Later patches will check the signature and add the key if the signature checks out. Signed-off-by: David Howells <[email protected]> Acked-by: Vivek Goyal <[email protected]> Reviewed-by: Kees Cook <[email protected]>
- Loading branch information
Showing
5 changed files
with
235 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
/* Parse a signed PE binary | ||
* | ||
* Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. | ||
* Written by David Howells ([email protected]) | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU General Public Licence | ||
* as published by the Free Software Foundation; either version | ||
* 2 of the Licence, or (at your option) any later version. | ||
*/ | ||
|
||
#define pr_fmt(fmt) "PEFILE: "fmt | ||
#include <linux/module.h> | ||
#include <linux/kernel.h> | ||
#include <linux/slab.h> | ||
#include <linux/err.h> | ||
#include <linux/pe.h> | ||
#include <crypto/pkcs7.h> | ||
#include <crypto/hash.h> | ||
#include "verify_pefile.h" | ||
|
||
/* | ||
* Parse a PE binary. | ||
*/ | ||
static int pefile_parse_binary(const void *pebuf, unsigned int pelen, | ||
struct pefile_context *ctx) | ||
{ | ||
const struct mz_hdr *mz = pebuf; | ||
const struct pe_hdr *pe; | ||
const struct pe32_opt_hdr *pe32; | ||
const struct pe32plus_opt_hdr *pe64; | ||
const struct data_directory *ddir; | ||
const struct data_dirent *dde; | ||
const struct section_header *secs, *sec; | ||
size_t cursor, datalen = pelen; | ||
|
||
kenter(""); | ||
|
||
#define chkaddr(base, x, s) \ | ||
do { \ | ||
if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \ | ||
return -ELIBBAD; \ | ||
} while (0) | ||
|
||
chkaddr(0, 0, sizeof(*mz)); | ||
if (mz->magic != MZ_MAGIC) | ||
return -ELIBBAD; | ||
cursor = sizeof(*mz); | ||
|
||
chkaddr(cursor, mz->peaddr, sizeof(*pe)); | ||
pe = pebuf + mz->peaddr; | ||
if (pe->magic != PE_MAGIC) | ||
return -ELIBBAD; | ||
cursor = mz->peaddr + sizeof(*pe); | ||
|
||
chkaddr(0, cursor, sizeof(pe32->magic)); | ||
pe32 = pebuf + cursor; | ||
pe64 = pebuf + cursor; | ||
|
||
switch (pe32->magic) { | ||
case PE_OPT_MAGIC_PE32: | ||
chkaddr(0, cursor, sizeof(*pe32)); | ||
ctx->image_checksum_offset = | ||
(unsigned long)&pe32->csum - (unsigned long)pebuf; | ||
ctx->header_size = pe32->header_size; | ||
cursor += sizeof(*pe32); | ||
ctx->n_data_dirents = pe32->data_dirs; | ||
break; | ||
|
||
case PE_OPT_MAGIC_PE32PLUS: | ||
chkaddr(0, cursor, sizeof(*pe64)); | ||
ctx->image_checksum_offset = | ||
(unsigned long)&pe64->csum - (unsigned long)pebuf; | ||
ctx->header_size = pe64->header_size; | ||
cursor += sizeof(*pe64); | ||
ctx->n_data_dirents = pe64->data_dirs; | ||
break; | ||
|
||
default: | ||
pr_debug("Unknown PEOPT magic = %04hx\n", pe32->magic); | ||
return -ELIBBAD; | ||
} | ||
|
||
pr_debug("checksum @ %x\n", ctx->image_checksum_offset); | ||
pr_debug("header size = %x\n", ctx->header_size); | ||
|
||
if (cursor >= ctx->header_size || ctx->header_size >= datalen) | ||
return -ELIBBAD; | ||
|
||
if (ctx->n_data_dirents > (ctx->header_size - cursor) / sizeof(*dde)) | ||
return -ELIBBAD; | ||
|
||
ddir = pebuf + cursor; | ||
cursor += sizeof(*dde) * ctx->n_data_dirents; | ||
|
||
ctx->cert_dirent_offset = | ||
(unsigned long)&ddir->certs - (unsigned long)pebuf; | ||
ctx->certs_size = ddir->certs.size; | ||
|
||
if (!ddir->certs.virtual_address || !ddir->certs.size) { | ||
pr_debug("Unsigned PE binary\n"); | ||
return -EKEYREJECTED; | ||
} | ||
|
||
chkaddr(ctx->header_size, ddir->certs.virtual_address, | ||
ddir->certs.size); | ||
ctx->sig_offset = ddir->certs.virtual_address; | ||
ctx->sig_len = ddir->certs.size; | ||
pr_debug("cert = %x @%x [%*ph]\n", | ||
ctx->sig_len, ctx->sig_offset, | ||
ctx->sig_len, pebuf + ctx->sig_offset); | ||
|
||
ctx->n_sections = pe->sections; | ||
if (ctx->n_sections > (ctx->header_size - cursor) / sizeof(*sec)) | ||
return -ELIBBAD; | ||
ctx->secs = secs = pebuf + cursor; | ||
|
||
return 0; | ||
} | ||
|
||
/** | ||
* verify_pefile_signature - Verify the signature on a PE binary image | ||
* @pebuf: Buffer containing the PE binary image | ||
* @pelen: Length of the binary image | ||
* @trust_keyring: Signing certificates to use as starting points | ||
* @_trusted: Set to true if trustworth, false otherwise | ||
* | ||
* 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: | ||
* | ||
* (*) -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(const void *pebuf, unsigned pelen, | ||
struct key *trusted_keyring, bool *_trusted) | ||
{ | ||
struct pefile_context ctx; | ||
int ret; | ||
|
||
kenter(""); | ||
|
||
memset(&ctx, 0, sizeof(ctx)); | ||
ret = pefile_parse_binary(pebuf, pelen, &ctx); | ||
if (ret < 0) | ||
return ret; | ||
|
||
return -ENOANO; // Not yet complete | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/* PE Binary parser bits | ||
* | ||
* Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. | ||
* Written by David Howells ([email protected]) | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU General Public Licence | ||
* as published by the Free Software Foundation; either version | ||
* 2 of the Licence, or (at your option) any later version. | ||
*/ | ||
|
||
#include <linux/verify_pefile.h> | ||
#include <crypto/pkcs7.h> | ||
#include <crypto/hash_info.h> | ||
|
||
struct pefile_context { | ||
unsigned header_size; | ||
unsigned image_checksum_offset; | ||
unsigned cert_dirent_offset; | ||
unsigned n_data_dirents; | ||
unsigned n_sections; | ||
unsigned certs_size; | ||
unsigned sig_offset; | ||
unsigned sig_len; | ||
const struct section_header *secs; | ||
struct pkcs7_message *pkcs7; | ||
|
||
/* PKCS#7 MS Individual Code Signing content */ | ||
const void *digest; /* Digest */ | ||
unsigned digest_len; /* Digest length */ | ||
enum hash_algo digest_algo; /* Digest algorithm */ | ||
}; | ||
|
||
#define kenter(FMT, ...) \ | ||
pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__) | ||
#define kleave(FMT, ...) \ | ||
pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* Signed PE file verification | ||
* | ||
* Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. | ||
* Written by David Howells ([email protected]) | ||
* | ||
* This program is free software; you can redistribute it and/or | ||
* modify it under the terms of the GNU General Public Licence | ||
* as published by the Free Software Foundation; either version | ||
* 2 of the Licence, or (at your option) any later version. | ||
*/ | ||
|
||
#ifndef _LINUX_VERIFY_PEFILE_H | ||
#define _LINUX_VERIFY_PEFILE_H | ||
|
||
extern int verify_pefile_signature(const void *pebuf, unsigned pelen, | ||
struct key *trusted_keyring, bool *_trusted); | ||
|
||
#endif /* _LINUX_VERIFY_PEFILE_H */ |