KEYS: Generalise system_verify_data() to provide access to internal content
authorDavid Howells <dhowells@redhat.com>
Wed, 6 Apr 2016 15:14:24 +0000 (16:14 +0100)
committerDavid Howells <dhowells@redhat.com>
Wed, 6 Apr 2016 15:14:24 +0000 (16:14 +0100)
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 <dhowells@redhat.com>
15 files changed:
arch/x86/kernel/kexec-bzimage64.c
certs/system_keyring.c
crypto/asymmetric_keys/Kconfig
crypto/asymmetric_keys/mscode_parser.c
crypto/asymmetric_keys/pkcs7_key_type.c
crypto/asymmetric_keys/pkcs7_parser.c
crypto/asymmetric_keys/verify_pefile.c
crypto/asymmetric_keys/verify_pefile.h
include/crypto/pkcs7.h
include/crypto/public_key.h
include/keys/asymmetric-type.h
include/keys/system_keyring.h
include/linux/verification.h [new file with mode: 0644]
include/linux/verify_pefile.h [deleted file]
kernel/module_signing.c

index 2af478e3fd4e2a5c9565adbe781eb06ed9ca5b60..f2356bda2b0546c245f9274f22cd2e429c29b52b 100644 (file)
@@ -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>
@@ -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
 
index f4180326c2e158129ed7527224c3ab33cacb5503..a83bffedc0aa64a1754b302c9b6519e7703cadcc 100644 (file)
@@ -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;
@@ -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;
@@ -138,13 +147,29 @@ 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:
@@ -152,6 +177,6 @@ error:
        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 */
index 91a7e047a765ebfe24e969e15f3e85114dd7445b..f7d2ef9789d8d7d8006be0051002fb4bb4eb4e19 100644 (file)
@@ -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
@@ -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
index 3242cbfaeaa277cacb167ac950782b16e2762b6d..6a76d5c70ef6e1ddb742bfc94671a499184c891d 100644 (file)
 /*
  * 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);
 
@@ -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;
 }
index e2d0edbbc71acd7be8878817b86e53af5a921e04..ab9bf5363ecd12bf58fbd70709f549a9b6563da1 100644 (file)
 #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");
@@ -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
index 8357016131250b254085c7d2c0f7717948ad6cb6..af4cd864911752478ba5f3c2732273f9624d434f 100644 (file)
@@ -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);
index 7e8c2338ae256631ae3a219f3a7686295f96f3cb..265351075b0e3086ca203e29a8632971ef141761 100644 (file)
@@ -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"
 
@@ -392,9 +392,8 @@ error_no_desc:
  * 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.
@@ -418,14 +417,10 @@ error_no_desc:
  * 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("");
@@ -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;
 
@@ -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;
 }
index a133eb81a49256b60529011ea189edd8f3d9c169..cd4d2093072844c87eeaa577220f4df844217b6c 100644 (file)
@@ -9,7 +9,6 @@
  * 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>
 
@@ -23,7 +22,6 @@ struct pefile_context {
        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 */
@@ -39,4 +37,5 @@ struct pefile_context {
 /*
  * mscode_parser.c
  */
-extern int mscode_parse(struct pefile_context *ctx);
+extern int mscode_parse(void *_ctx, const void *content_data, size_t data_len,
+                       size_t asn1hdrlen);
index 441aff9b5aa75923e66566d93fdcd544d948b00f..8323e3e571311b4c32794809eb8b2569c0e80b77 100644 (file)
@@ -12,6 +12,7 @@
 #ifndef _CRYPTO_PKCS7_H
 #define _CRYPTO_PKCS7_H
 
+#include <linux/verification.h>
 #include <crypto/public_key.h>
 
 struct key;
@@ -26,7 +27,7 @@ extern void pkcs7_free_message(struct pkcs7_message *pkcs7);
 
 extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7,
                                  const void **_data, size_t *_datalen,
-                                 bool want_wrapper);
+                                 size_t *_headerlen);
 
 /*
  * pkcs7_trust.c
index 2f5de5c1a3a08014f8448f7a4f7210fe35b48b23..b3928e801b8cbb410bc67ba243a0ee61fc8f28a2 100644 (file)
 #ifndef _LINUX_PUBLIC_KEY_H
 #define _LINUX_PUBLIC_KEY_H
 
-/*
- * The use to which an asymmetric key is being put.
- */
-enum key_being_used_for {
-       VERIFYING_MODULE_SIGNATURE,
-       VERIFYING_FIRMWARE_SIGNATURE,
-       VERIFYING_KEXEC_PE_SIGNATURE,
-       VERIFYING_KEY_SIGNATURE,
-       VERIFYING_KEY_SELF_SIGNATURE,
-       VERIFYING_UNSPECIFIED_SIGNATURE,
-       NR__KEY_BEING_USED_FOR
-};
-extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR];
-
 /*
  * Cryptographic data for the public-key subtype of the asymmetric key type.
  *
index 70a8775bb444ac465ba1369654a78646def6f87e..d1e23dda43633e5427ce641681eb83c7f530c2ef 100644 (file)
@@ -15,6 +15,7 @@
 #define _KEYS_ASYMMETRIC_TYPE_H
 
 #include <linux/key-type.h>
+#include <linux/verification.h>
 
 extern struct key_type key_type_asymmetric;
 
index 39fd38cfa8c96512e59360f151eebb07eb74be1f..b2d645ac35a04c0eaabdd3636b2f22d81c1185aa 100644 (file)
@@ -15,6 +15,7 @@
 #ifdef CONFIG_SYSTEM_TRUSTED_KEYRING
 
 #include <linux/key.h>
+#include <linux/verification.h>
 #include <crypto/public_key.h>
 
 extern struct key *system_trusted_keyring;
@@ -29,12 +30,6 @@ static inline struct key *get_system_trusted_keyring(void)
 }
 #endif
 
-#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
-extern int system_verify_data(const void *data, unsigned long len,
-                             const void *raw_pkcs7, size_t pkcs7_len,
-                             enum key_being_used_for usage);
-#endif
-
 #ifdef CONFIG_IMA_MOK_KEYRING
 extern struct key *ima_mok_keyring;
 extern struct key *ima_blacklist_keyring;
diff --git a/include/linux/verification.h b/include/linux/verification.h
new file mode 100644 (file)
index 0000000..bb0fcf9
--- /dev/null
@@ -0,0 +1,50 @@
+/* Signature verification
+ *
+ * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * 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_VERIFICATION_H
+#define _LINUX_VERIFICATION_H
+
+/*
+ * The use to which an asymmetric key is being put.
+ */
+enum key_being_used_for {
+       VERIFYING_MODULE_SIGNATURE,
+       VERIFYING_FIRMWARE_SIGNATURE,
+       VERIFYING_KEXEC_PE_SIGNATURE,
+       VERIFYING_KEY_SIGNATURE,
+       VERIFYING_KEY_SELF_SIGNATURE,
+       VERIFYING_UNSPECIFIED_SIGNATURE,
+       NR__KEY_BEING_USED_FOR
+};
+extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR];
+
+#ifdef CONFIG_SYSTEM_DATA_VERIFICATION
+
+struct key;
+
+extern 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);
+
+#ifdef CONFIG_SIGNED_PE_FILE_VERIFICATION
+extern int verify_pefile_signature(const void *pebuf, unsigned pelen,
+                                  struct key *trusted_keys,
+                                  enum key_being_used_for usage);
+#endif
+
+#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
+#endif /* _LINUX_VERIFY_PEFILE_H */
diff --git a/include/linux/verify_pefile.h b/include/linux/verify_pefile.h
deleted file mode 100644 (file)
index da2049b..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-/* Signed PE file verification
- *
- * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved.
- * Written by David Howells (dhowells@redhat.com)
- *
- * 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
-
-#include <crypto/public_key.h>
-
-extern int verify_pefile_signature(const void *pebuf, unsigned pelen,
-                                  struct key *trusted_keyring,
-                                  enum key_being_used_for usage,
-                                  bool *_trusted);
-
-#endif /* _LINUX_VERIFY_PEFILE_H */
index 64b9dead4a0763f2ad9e853a6221b31a0fa6f938..593aace88a02f80cc59c46f5e474b866ebdf2150 100644 (file)
@@ -80,6 +80,7 @@ int mod_verify_sig(const void *mod, unsigned long *_modlen)
                return -EBADMSG;
        }
 
-       return system_verify_data(mod, modlen, mod + modlen, sig_len,
-                                 VERIFYING_MODULE_SIGNATURE);
+       return verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
+                                     NULL, -ENOKEY, VERIFYING_MODULE_SIGNATURE,
+                                     NULL, NULL);
 }