Verify kexec image signatures on arm64

Bug #1851412 reported by Tyler Hicks
264
This bug affects 1 person
Affects Status Importance Assigned to Milestone
linux (Ubuntu)
Invalid
Undecided
dann frazier
Disco
Invalid
Undecided
Unassigned
Eoan
Invalid
Undecided
Unassigned
Focal
Invalid
Undecided
dann frazier

Bug Description

While reviewing our kernel configs in Focal, I noticed that we produce signed arm64 kernels since Disco but don't seem to be verifying any signatures during kexec. Specifically, CONFIG_KEXEC_IMAGE_VERIFY_SIG is not enabled.

== Disco ==
$ git grep CONFIG_KEXEC_IMAGE_VERIFY_SIG debian.master/debian.master/config/annotations:CONFIG_KEXEC_IMAGE_VERIFY_SIG policy<{'arm64': 'n'}>
debian.master/config/annotations:CONFIG_KEXEC_IMAGE_VERIFY_SIG flag<REVIEW>
debian.master/config/config.common.ubuntu:# CONFIG_KEXEC_IMAGE_VERIFY_SIG is not set

== Eoan ==
$ git grep CONFIG_KEXEC_IMAGE_VERIFY_SIG debian.master/
debian.master/config/annotations:CONFIG_KEXEC_IMAGE_VERIFY_SIG policy<{'arm64': 'n'}>
debian.master/config/annotations:CONFIG_KEXEC_IMAGE_VERIFY_SIG flag<REVIEW>
debian.master/config/config.common.ubuntu:# CONFIG_KEXEC_IMAGE_VERIFY_SIG is not set

Looking at the Ubuntu-5.3.0-19.20 tag in Eoan, it looks like the CONFIG_KEXEC_IMAGE_VERIFY_SIG option should be enabled to perform signature verification of kexec images:

$ cat -n arch/arm64/kernel/kexec_image.c | tail -n 15
   116 #ifdef CONFIG_KEXEC_IMAGE_VERIFY_SIG
   117 static int image_verify_sig(const char *kernel, unsigned long kernel_len)
   118 {
   119 return verify_pefile_signature(kernel, kernel_len, NULL,
   120 VERIFYING_KEXEC_PE_SIGNATURE);
   121 }
   122 #endif
   123
   124 const struct kexec_file_ops kexec_image_ops = {
   125 .probe = image_probe,
   126 .load = image_load,
   127 #ifdef CONFIG_KEXEC_IMAGE_VERIFY_SIG
   128 .verify_sig = image_verify_sig,
   129 #endif
   130 };

Revision history for this message
Tyler Hicks (tyhicks) wrote :

Dann, can you please take a look at this and offer your opinion? Thanks!

dann frazier (dannf)
Changed in linux (Ubuntu):
assignee: nobody → dann frazier (dannf)
Revision history for this message
dann frazier (dannf) wrote :

I worked with Seth to do some kexec testing back when we added this support. I need to go through my IRC logs to recall the details. However, a quick test back on the 5.0.0-7 kernel (the first one in the archive w/ signatures) shows the kernel refusing to kexec a (self-)signed image:

ubuntu@ubuntu:~$ sudo kexec -l /boot/vmlinuz-5.0.0-7-generic
kexec_load failed: Operation not permitted
entry = 0x41904690 flags = 0xb70000
nr_segments = 3
segment[0].buf = 0xffff7f6b3010
segment[0].bufsz = 0x17979c0
segment[0].mem = 0x40080000
segment[0].memsz = 0x1883000
segment[1].buf = 0xaaaaf6b7d9c0
segment[1].bufsz = 0x245
segment[1].mem = 0x41903000
segment[1].memsz = 0x1000
segment[2].buf = 0xaaaaf6b7e4c0
segment[2].bufsz = 0x3458
segment[2].mem = 0x41904000
segment[2].memsz = 0x4000

dmesg shows:
[ 208.518288] Lockdown: kexec: kexec of unsigned images is restricted; see man kernel_lockdown.

Revision history for this message
Tyler Hicks (tyhicks) wrote :

That error message comes from this patch:

 https://git.launchpad.net/~ubuntu-kernel/ubuntu/+source/linux/+git/disco/commit/?id=207a511d08a3b587f9737ca930ad1ee5f289bc64

I agree that kexec_load_check() blocks the usage of the kexec_load(2) syscall when lockdown is enabled. There's no way to do signing of the kexec_segments that are passed to kexec_load(2) so it just needs to be outright blocked under lockdown.

Now we need to ensure that the usage of the kexec_file_load(2) syscall is properly handled.

Revision history for this message
Tyler Hicks (tyhicks) wrote :
Download full text (3.7 KiB)

In Eoan, the kernel implementation of kexec_file_load(2) calls kimage_file_alloc_init() which calls kimage_file_prepare_segments().

If CONFIG_KEXEC_SIG is enabled (it is), arch_kexec_kernel_verify_sig() from kernel/kexec_file.c is called:

    91 #ifdef CONFIG_KEXEC_SIG
    92 static int kexec_image_verify_sig_default(struct kimage *image, void *buf,
    93 unsigned long buf_len)
    94 {
    95 if (!image->fops || !image->fops->verify_sig) {
    96 pr_debug("kernel loader does not support signature verification.\n");
    97 return -EKEYREJECTED;
    98 }
    99
   100 return image->fops->verify_sig(buf, buf_len);
   101 }
   102
   103 int __weak arch_kexec_kernel_verify_sig(struct kimage *image, void *buf,
   104 unsigned long buf_len)
   105 {
   106 return kexec_image_verify_sig_default(image, buf, buf_len);
   107 }
   108 #endif

That will return -EKEYREJECTED since we don't enable CONFIG_KEXEC_IMAGE_VERIFY_SIG on arm64. Execution will return to kimage_file_prepare_segments():

   184 static int
   185 kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
   186 const char __user *cmdline_ptr,
   187 unsigned long cmdline_len, unsigned flags)
   188 {
   ...
   206 #ifdef CONFIG_KEXEC_SIG
   207 ret = arch_kexec_kernel_verify_sig(image, image->kernel_buf,
   208 image->kernel_buf_len);
   209 #else
   210 ret = -ENODATA;
   211 #endif
   212
   213 switch (ret) {
   214 case 0:
   215 break;
   216
   217 /* Certain verification errors are non-fatal if we're not
   218 * checking errors, provided we aren't mandating that there
   219 * must be a valid signature.
   220 */
   221 case -ENODATA:
   222 reason = "kexec of unsigned image";
   223 goto decide;
   224 case -ENOPKG:
   225 reason = "kexec of image with unsupported crypto";
   226 goto decide;
   227 case -ENOKEY:
   228 reason = "kexec of image with unavailable key";
   229 decide:
   230 if (IS_ENABLED(CONFIG_KEXEC_SIG_FORCE)) {
   231 pr_notice("%s rejected\n", reason);
   232 ret = -EKEYREJECTED;
   233 goto out;
   234 }
   235
   236 ret = 0;
   237
   238 /* If IMA is guaranteed to appraise a signature on the kexec
   239 * image, permit it even if the kernel is otherwise locked
   240 * down.
   241 */
   242 if (!ima_appraise_signature(READING_KEXEC_IMAGE) &&
   243 kernel_is_locked_down(reason)) {
   244 ret = -EPERM;
   245 goto out;
   246 ...

Read more...

Revision history for this message
Tyler Hicks (tyhicks) wrote :

Bah, I just noticed that the tabs weren't preserved in the paste above.

The Disco version of kimage_file_prepare_segments() is much more straightforward. It bails out immediately when arch_kexec_kernel_verify_sig() returns -EKEYREJECTED due to CONFIG_KEXEC_VERIFY_SIG being off.

So, in summary, I think kexec_file_load(2) will fail in Eoan under lockdown and will always fail in Disco. I don't think there's a lockdown bypass in either release.

It does seem like CONFIG_KEXEC_IMAGE_VERIFY_SIG could be enabled to allow for kexec to work under lockdown when dealing with signed kexec images but that's not important from a security standpoint.

Revision history for this message
dann frazier (dannf) wrote :

I verified that the above is still true when running 5.4.0-3.4. The way I read the code, the kexec_load() syscall just refuses to load anything (signed or not) in lockdown mode. However, we also have the kexec_file_load() syscall. That interface also refuses to load the kernel, but with a different message:

ubuntu@ubuntu:~/kexec-tools$ sudo ./build/sbin/kexec -s -l /boot/vmlinuz-5.4.0-3-generic
kexec_file_load failed: Key was rejected by service

dmesg shows:
[ 2721.130163] kexec_file: kernel signature verification failed (-129).

Revision history for this message
Tyler Hicks (tyhicks) wrote :

Nice! I was just reviewing 5.4.0-3 and thought that it did the right thing, as well. -129 is -EKEYREJECTED on arm64 and that error message is what I'd expect from kimage_validate_signature().

So, I think we've reviewed and/or tested all releases where we do arm64 signing and verified that they're all doing the right thing despite the confusing mess of kexec sig verification kconfig options and the out-of-tree lockdown patches.

Thanks!

Changed in linux (Ubuntu Disco):
status: New → Invalid
Changed in linux (Ubuntu Eoan):
status: New → Incomplete
status: Incomplete → Invalid
Changed in linux (Ubuntu Focal):
status: New → Invalid
Revision history for this message
dann frazier (dannf) wrote :

fyi, 5.0.0-7-generic did have CONFIG_KEXEC_VERIFY_SIG=y (but CONFIG_KEXEC_IMAGE_VERIFY_SIG=n), but it still kexec_file_load() still rejects it:

ubuntu@ubuntu:~$ sudo ./kexec-tools/build/sbin/kexec -s -l /tmp/x/boot/vmlinuz-5.4.0-3-generic
kexec_file_load failed: Key was rejected by service

Tyler Hicks (tyhicks)
information type: Private Security → Public Security
To post a comment you must log in.
This report contains Public Security information  
Everyone can see this security related information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.