[PATCH v1 1/1] Return the verified kernel image signature in kexec_file_load

From: nramas
Date: Tue Apr 23 2019 - 22:15:10 EST



From: Lakshmi Ramasubramanian <nramas@xxxxxxxxxxxxx>

Signed-off-by: Lakshmi Ramasubramanian <nramas@xxxxxxxxxxxxx>
---
When CONFIG_KEXEC_VERIFY_SIG is selected the signature on the new kernel image is verified in kexec_file_load.
The signature is embedded in the kernel image file.

This change returns the pointer to the verified signature and the length of that signature. kexec_file_load can log this
signature for attestation (To attest the signer of the new kernel).
The change to log the kernel signature for attestation will be added
in a future change set.


arch/x86/kernel/kexec-bzimage64.c | 7 +++++--
arch/x86/kernel/machine_kexec_64.c | 10 +++++++---
crypto/asymmetric_keys/verify_pefile.c | 18 +++++++++++++++++-
include/linux/kexec.h | 8 ++++++--
include/linux/verification.h | 4 +++-
kernel/kexec_file.c | 14 +++++++++++---
6 files changed, 49 insertions(+), 12 deletions(-)

diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c
index 9d7fd5e6689a..030abd8adbce 100644
--- a/arch/x86/kernel/kexec-bzimage64.c
+++ b/arch/x86/kernel/kexec-bzimage64.c
@@ -530,11 +530,14 @@ 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)
+static int bzImage64_verify_sig(const char *kernel, unsigned long kernel_len,
+ unsigned int *signature_len, void **signature)
{
return verify_pefile_signature(kernel, kernel_len,
NULL,
- VERIFYING_KEXEC_PE_SIGNATURE);
+ VERIFYING_KEXEC_PE_SIGNATURE,
+ signature_len,
+ signature);
}
#endif

diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index 6f5ca4ebe6e5..b556a9750dd4 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -405,15 +405,19 @@ int arch_kimage_file_post_load_cleanup(struct kimage *image)
}

#ifdef CONFIG_KEXEC_VERIFY_SIG
-int arch_kexec_kernel_verify_sig(struct kimage *image, void *kernel,
- unsigned long kernel_len)
+int arch_kexec_kernel_verify_sig(struct kimage *image,
+ void *kernel,
+ unsigned long kernel_len,
+ unsigned int *signature_len,
+ void **signature)
{
if (!image->fops || !image->fops->verify_sig) {
pr_debug("kernel loader does not support signature verification.");
return -EKEYREJECTED;
}

- return image->fops->verify_sig(kernel, kernel_len);
+ return image->fops->verify_sig(kernel, kernel_len,
+ signature_len, signature);
}
#endif

diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c
index 672a94c2c3ff..588a7966922f 100644
--- a/crypto/asymmetric_keys/verify_pefile.c
+++ b/crypto/asymmetric_keys/verify_pefile.c
@@ -394,6 +394,12 @@ static int pefile_digest_pe(const void *pebuf, unsigned int pelen,
* @pelen: Length of the binary image
* @trust_keys: Signing certificate(s) to use as starting points
* @usage: The use to which the key is being put.
+ * @signature_len: If non-NULL the number of bytes in the signature
+ * will be returned in this out parameter
+ * @signature: If non-NULL a pointer to the buffer containing
+ * the file signature will be returned in this out parameter.
+ * The pointer being returned is actually within the buffer
+ * pointed to by pebuf. So the caller should not try to free it.
*
* Validate that the certificate chain inside the PKCS#7 message inside the PE
* binary image intersects keys we already know and trust.
@@ -418,7 +424,9 @@ static int pefile_digest_pe(const void *pebuf, unsigned int pelen,
*/
int verify_pefile_signature(const void *pebuf, unsigned pelen,
struct key *trusted_keys,
- enum key_being_used_for usage)
+ enum key_being_used_for usage,
+ unsigned int *signature_len,
+ void **signature)
{
struct pefile_context ctx;
int ret;
@@ -448,6 +456,14 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen,
* contents.
*/
ret = pefile_digest_pe(pebuf, pelen, &ctx);
+ if (ret < 0)
+ goto error;
+
+ /* Check if the caller needs the file signature */
+ if (signature_len != NULL && signature != NULL) {
+ *signature_len = ctx.sig_len;
+ *signature = pebuf + ctx.sig_offset;
+ }

error:
kfree(ctx.digest);
diff --git a/include/linux/kexec.h b/include/linux/kexec.h
index c9481ebcbc0c..1c790a5b03a0 100644
--- a/include/linux/kexec.h
+++ b/include/linux/kexec.h
@@ -131,7 +131,9 @@ typedef int (kexec_cleanup_t)(void *loader_data);

#ifdef CONFIG_KEXEC_VERIFY_SIG
typedef int (kexec_verify_sig_t)(const char *kernel_buf,
- unsigned long kernel_len);
+ unsigned long kernel_len,
+ unsigned int *signature_len,
+ void **signature);
#endif

struct kexec_file_ops {
@@ -288,7 +290,9 @@ int __weak arch_kexec_kernel_image_probe(struct kimage *image, void *buf,
void * __weak arch_kexec_kernel_image_load(struct kimage *image);
int __weak arch_kimage_file_post_load_cleanup(struct kimage *image);
int __weak arch_kexec_kernel_verify_sig(struct kimage *image, void *buf,
- unsigned long buf_len);
+ unsigned long buf_len,
+ unsigned int *signature_len,
+ void **signature);
int __weak arch_kexec_apply_relocations_add(const Elf_Ehdr *ehdr,
Elf_Shdr *sechdrs, unsigned int relsec);
int __weak arch_kexec_apply_relocations(const Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
diff --git a/include/linux/verification.h b/include/linux/verification.h
index a10549a6c7cd..2ad9ff1dab5e 100644
--- a/include/linux/verification.h
+++ b/include/linux/verification.h
@@ -42,7 +42,9 @@ extern int verify_pkcs7_signature(const void *data, size_t len,
#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);
+ enum key_being_used_for usage,
+ unsigned int *signature_len,
+ void **signature);
#endif

#endif /* CONFIG_SYSTEM_DATA_VERIFICATION */
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index b118735fea9d..fb0c94b8e52b 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -54,7 +54,9 @@ int __weak arch_kimage_file_post_load_cleanup(struct kimage *image)

#ifdef CONFIG_KEXEC_VERIFY_SIG
int __weak arch_kexec_kernel_verify_sig(struct kimage *image, void *buf,
- unsigned long buf_len)
+ unsigned long buf_len,
+ unsigned int *signature_len,
+ void **signature)
{
return -EKEYREJECTED;
}
@@ -126,6 +128,10 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
int ret = 0;
void *ldata;
loff_t size;
+#ifdef CONFIG_KEXEC_VERIFY_SIG
+ unsigned int signature_len;
+ void *signature;
+#endif

ret = kernel_read_file_from_fd(kernel_fd, &image->kernel_buf,
&size, INT_MAX, READING_KEXEC_IMAGE);
@@ -144,12 +150,14 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,

#ifdef CONFIG_KEXEC_VERIFY_SIG
ret = arch_kexec_kernel_verify_sig(image, image->kernel_buf,
- image->kernel_buf_len);
+ image->kernel_buf_len,
+ &signature_len,
+ &signature);
if (ret) {
pr_debug("kernel signature verification failed.\n");
goto out;
}
- pr_debug("kernel signature verification successful.\n");
+ pr_debug("Verified kernel signature. Sig len %d\n", signature_len);
#endif
/* It is possible that there no initramfs is being loaded */
if (!(flags & KEXEC_FILE_NO_INITRAMFS)) {
--
2.17.1