Skip to content

Commit

Permalink
KEYS: Generalise system_verify_data() to provide access to internal c…
Browse files Browse the repository at this point in the history
…ontent

Generalise system_verify_data() to provide access to internal content
through a callback.  This allows all the PKCS#7 stuff to be hidden inside
this function and removed from the PE file parser and the PKCS#7 test key.

If external content is not required, NULL should be passed as data to the
function.  If the callback is not required, that can be set to NULL.

The function is now called verify_pkcs7_signature() to contrast with
verify_pefile_signature() and the definitions of both have been moved into
linux/verification.h along with the key_being_used_for enum.

Signed-off-by: David Howells <[email protected]>
  • Loading branch information
dhowells committed Apr 6, 2016
1 parent ad3043f commit e68503b
Show file tree
Hide file tree
Showing 15 changed files with 155 additions and 173 deletions.
18 changes: 4 additions & 14 deletions arch/x86/kernel/kexec-bzimage64.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/efi.h>
#include <linux/verify_pefile.h>
#include <keys/system_keyring.h>
#include <linux/verification.h>

#include <asm/bootparam.h>
#include <asm/setup.h>
Expand Down Expand Up @@ -529,18 +528,9 @@ static int bzImage64_cleanup(void *loader_data)
#ifdef CONFIG_KEXEC_BZIMAGE_VERIFY_SIG
static int bzImage64_verify_sig(const char *kernel, unsigned long kernel_len)
{
bool trusted;
int ret;

ret = verify_pefile_signature(kernel, kernel_len,
system_trusted_keyring,
VERIFYING_KEXEC_PE_SIGNATURE,
&trusted);
if (ret < 0)
return ret;
if (!trusted)
return -EKEYREJECTED;
return 0;
return verify_pefile_signature(kernel, kernel_len,
NULL,
VERIFYING_KEXEC_PE_SIGNATURE);
}
#endif

Expand Down
45 changes: 35 additions & 10 deletions certs/system_keyring.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,16 +108,25 @@ late_initcall(load_system_certificate_list);
#ifdef CONFIG_SYSTEM_DATA_VERIFICATION

/**
* Verify a PKCS#7-based signature on system data.
* @data: The data to be verified.
* verify_pkcs7_signature - Verify a PKCS#7-based signature on system data.
* @data: The data to be verified (NULL if expecting internal data).
* @len: Size of @data.
* @raw_pkcs7: The PKCS#7 message that is the signature.
* @pkcs7_len: The size of @raw_pkcs7.
* @trusted_keys: Trusted keys to use (NULL for system_trusted_keyring).
* @usage: The use to which the key is being put.
* @view_content: Callback to gain access to content.
* @ctx: Context for callback.
*/
int system_verify_data(const void *data, unsigned long len,
const void *raw_pkcs7, size_t pkcs7_len,
enum key_being_used_for usage)
int verify_pkcs7_signature(const void *data, size_t len,
const void *raw_pkcs7, size_t pkcs7_len,
struct key *trusted_keys,
int untrusted_error,
enum key_being_used_for usage,
int (*view_content)(void *ctx,
const void *data, size_t len,
size_t asn1hdrlen),
void *ctx)
{
struct pkcs7_message *pkcs7;
bool trusted;
Expand All @@ -128,7 +137,7 @@ int system_verify_data(const void *data, unsigned long len,
return PTR_ERR(pkcs7);

/* The data should be detached - so we need to supply it. */
if (pkcs7_supply_detached_data(pkcs7, data, len) < 0) {
if (data && pkcs7_supply_detached_data(pkcs7, data, len) < 0) {
pr_err("PKCS#7 signature with non-detached data\n");
ret = -EBADMSG;
goto error;
Expand All @@ -138,20 +147,36 @@ int system_verify_data(const void *data, unsigned long len,
if (ret < 0)
goto error;

ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted);
if (!trusted_keys)
trusted_keys = system_trusted_keyring;
ret = pkcs7_validate_trust(pkcs7, trusted_keys, &trusted);
if (ret < 0)
goto error;

if (!trusted) {
if (!trusted && untrusted_error) {
pr_err("PKCS#7 signature not signed with a trusted key\n");
ret = -ENOKEY;
ret = untrusted_error;
goto error;
}

if (view_content) {
size_t asn1hdrlen;

ret = pkcs7_get_content_data(pkcs7, &data, &len, &asn1hdrlen);
if (ret < 0) {
if (ret == -ENODATA)
pr_devel("PKCS#7 message does not contain data\n");
goto error;
}

ret = view_content(ctx, data, len, asn1hdrlen);
}

error:
pkcs7_free_message(pkcs7);
pr_devel("<==%s() = %d\n", __func__, ret);
return ret;
}
EXPORT_SYMBOL_GPL(system_verify_data);
EXPORT_SYMBOL_GPL(verify_pkcs7_signature);

#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
4 changes: 2 additions & 2 deletions crypto/asymmetric_keys/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ config PKCS7_MESSAGE_PARSER

config PKCS7_TEST_KEY
tristate "PKCS#7 testing key type"
depends on PKCS7_MESSAGE_PARSER
select SYSTEM_TRUSTED_KEYRING
depends on SYSTEM_DATA_VERIFICATION
help
This option provides a type of key that can be loaded up from a
PKCS#7 message - provided the message is signed by a trusted key. If
Expand All @@ -54,6 +53,7 @@ config PKCS7_TEST_KEY
config SIGNED_PE_FILE_VERIFICATION
bool "Support for PE file signature verification"
depends on PKCS7_MESSAGE_PARSER=y
depends on SYSTEM_DATA_VERIFICATION
select ASN1
select OID_REGISTRY
help
Expand Down
21 changes: 7 additions & 14 deletions crypto/asymmetric_keys/mscode_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,13 @@
/*
* Parse a Microsoft Individual Code Signing blob
*/
int mscode_parse(struct pefile_context *ctx)
int mscode_parse(void *_ctx, const void *content_data, size_t data_len,
size_t asn1hdrlen)
{
const void *content_data;
size_t data_len;
int ret;

ret = pkcs7_get_content_data(ctx->pkcs7, &content_data, &data_len, 1);

if (ret) {
pr_debug("PKCS#7 message does not contain data\n");
return ret;
}
struct pefile_context *ctx = _ctx;

content_data -= asn1hdrlen;
data_len += asn1hdrlen;
pr_devel("Data: %zu [%*ph]\n", data_len, (unsigned)(data_len),
content_data);

Expand Down Expand Up @@ -129,7 +123,6 @@ int mscode_note_digest(void *context, size_t hdrlen,
{
struct pefile_context *ctx = context;

ctx->digest = value;
ctx->digest_len = vlen;
return 0;
ctx->digest = kmemdup(value, vlen, GFP_KERNEL);
return ctx->digest ? 0 : -ENOMEM;
}
72 changes: 28 additions & 44 deletions crypto/asymmetric_keys/pkcs7_key_type.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,9 @@
#include <linux/key.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/verification.h>
#include <linux/key-type.h>
#include <keys/asymmetric-type.h>
#include <crypto/pkcs7.h>
#include <keys/user-type.h>
#include <keys/system_keyring.h>
#include "pkcs7_parser.h"

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("PKCS#7 testing key type");
Expand All @@ -29,59 +26,46 @@ MODULE_PARM_DESC(pkcs7_usage,
"Usage to specify when verifying the PKCS#7 message");

/*
* Preparse a PKCS#7 wrapped and validated data blob.
* Retrieve the PKCS#7 message content.
*/
static int pkcs7_preparse(struct key_preparsed_payload *prep)
static int pkcs7_view_content(void *ctx, const void *data, size_t len,
size_t asn1hdrlen)
{
enum key_being_used_for usage = pkcs7_usage;
struct pkcs7_message *pkcs7;
const void *data, *saved_prep_data;
size_t datalen, saved_prep_datalen;
bool trusted;
struct key_preparsed_payload *prep = ctx;
const void *saved_prep_data;
size_t saved_prep_datalen;
int ret;

kenter("");

if (usage >= NR__KEY_BEING_USED_FOR) {
pr_err("Invalid usage type %d\n", usage);
return -EINVAL;
}

saved_prep_data = prep->data;
saved_prep_datalen = prep->datalen;
pkcs7 = pkcs7_parse_message(saved_prep_data, saved_prep_datalen);
if (IS_ERR(pkcs7)) {
ret = PTR_ERR(pkcs7);
goto error;
}

ret = pkcs7_verify(pkcs7, usage);
if (ret < 0)
goto error_free;

ret = pkcs7_validate_trust(pkcs7, system_trusted_keyring, &trusted);
if (ret < 0)
goto error_free;
if (!trusted)
pr_warn("PKCS#7 message doesn't chain back to a trusted key\n");

ret = pkcs7_get_content_data(pkcs7, &data, &datalen, false);
if (ret < 0)
goto error_free;

prep->data = data;
prep->datalen = datalen;
prep->datalen = len;

ret = user_preparse(prep);

prep->data = saved_prep_data;
prep->datalen = saved_prep_datalen;

error_free:
pkcs7_free_message(pkcs7);
error:
kleave(" = %d", ret);
return ret;
}

/*
* Preparse a PKCS#7 wrapped and validated data blob.
*/
static int pkcs7_preparse(struct key_preparsed_payload *prep)
{
enum key_being_used_for usage = pkcs7_usage;

if (usage >= NR__KEY_BEING_USED_FOR) {
pr_err("Invalid usage type %d\n", usage);
return -EINVAL;
}

return verify_pkcs7_signature(NULL, 0,
prep->data, prep->datalen,
NULL, -ENOKEY, usage,
pkcs7_view_content, prep);
}

/*
* user defined keys take an arbitrary string as the description and an
* arbitrary blob of data as the payload
Expand Down
21 changes: 11 additions & 10 deletions crypto/asymmetric_keys/pkcs7_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,24 +168,25 @@ EXPORT_SYMBOL_GPL(pkcs7_parse_message);
* @pkcs7: The preparsed PKCS#7 message to access
* @_data: Place to return a pointer to the data
* @_data_len: Place to return the data length
* @want_wrapper: True if the ASN.1 object header should be included in the data
* @_headerlen: Size of ASN.1 header not included in _data
*
* Get access to the data content of the PKCS#7 message, including, optionally,
* the header of the ASN.1 object that contains it. Returns -ENODATA if the
* data object was missing from the message.
* Get access to the data content of the PKCS#7 message. The size of the
* header of the ASN.1 object that contains it is also provided and can be used
* to adjust *_data and *_data_len to get the entire object.
*
* Returns -ENODATA if the data object was missing from the message.
*/
int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
const void **_data, size_t *_data_len,
bool want_wrapper)
size_t *_headerlen)
{
size_t wrapper;

if (!pkcs7->data)
return -ENODATA;

wrapper = want_wrapper ? pkcs7->data_hdrlen : 0;
*_data = pkcs7->data - wrapper;
*_data_len = pkcs7->data_len + wrapper;
*_data = pkcs7->data;
*_data_len = pkcs7->data_len;
if (_headerlen)
*_headerlen = pkcs7->data_hdrlen;
return 0;
}
EXPORT_SYMBOL_GPL(pkcs7_get_content_data);
Expand Down
40 changes: 9 additions & 31 deletions crypto/asymmetric_keys/verify_pefile.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
#include <linux/err.h>
#include <linux/pe.h>
#include <linux/asn1.h>
#include <crypto/pkcs7.h>
#include <linux/verification.h>
#include <crypto/hash.h>
#include "verify_pefile.h"

Expand Down Expand Up @@ -392,9 +392,8 @@ static int pefile_digest_pe(const void *pebuf, unsigned int pelen,
* 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
* @trust_keys: Signing certificate(s) to use as starting points
* @usage: The use to which the key is being put.
* @_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.
Expand All @@ -418,14 +417,10 @@ static int pefile_digest_pe(const void *pebuf, unsigned int pelen,
* May also return -ENOMEM.
*/
int verify_pefile_signature(const void *pebuf, unsigned pelen,
struct key *trusted_keyring,
enum key_being_used_for usage,
bool *_trusted)
struct key *trusted_keys,
enum key_being_used_for usage)
{
struct pkcs7_message *pkcs7;
struct pefile_context ctx;
const void *data;
size_t datalen;
int ret;

kenter("");
Expand All @@ -439,19 +434,10 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen,
if (ret < 0)
return ret;

pkcs7 = pkcs7_parse_message(pebuf + ctx.sig_offset, ctx.sig_len);
if (IS_ERR(pkcs7))
return PTR_ERR(pkcs7);
ctx.pkcs7 = pkcs7;

ret = pkcs7_get_content_data(ctx.pkcs7, &data, &datalen, false);
if (ret < 0 || datalen == 0) {
pr_devel("PKCS#7 message does not contain data\n");
ret = -EBADMSG;
goto error;
}

ret = mscode_parse(&ctx);
ret = verify_pkcs7_signature(NULL, 0,
pebuf + ctx.sig_offset, ctx.sig_len,
trusted_keys, -EKEYREJECTED, usage,
mscode_parse, &ctx);
if (ret < 0)
goto error;

Expand All @@ -462,16 +448,8 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen,
* contents.
*/
ret = pefile_digest_pe(pebuf, pelen, &ctx);
if (ret < 0)
goto error;

ret = pkcs7_verify(pkcs7, usage);
if (ret < 0)
goto error;

ret = pkcs7_validate_trust(pkcs7, trusted_keyring, _trusted);

error:
pkcs7_free_message(ctx.pkcs7);
kfree(ctx.digest);
return ret;
}
Loading

0 comments on commit e68503b

Please sign in to comment.