[v2,3/5] crash_dump: retrieve dm crypt key in kdump kernel
Commit Message
Crash kernel will retrieve the dm crypt volume key based on the
dmcryptkey command line parameter. When user space writes the key
description to /sys/kernel/crash_dm_crypt_key, the crash kernel will
save the encryption key to the user keyring. Then user space e.g.
cryptsetup's --volume-key-keyring API can use it to unlock the encrypted
device.
Signed-off-by: Coiby Xu <coxu@redhat.com>
---
include/linux/crash_dump.h | 2 +
kernel/crash_dump_dm_crypt.c | 115 ++++++++++++++++++++++++++++++++++-
2 files changed, 116 insertions(+), 1 deletion(-)
Comments
Hi Coiby,
kernel test robot noticed the following build warnings:
[auto build test WARNING on tip/x86/core]
[also build test WARNING on v6.7]
[cannot apply to linus/master next-20240112]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Coiby-Xu/kexec_file-allow-to-place-kexec_buf-randomly/20240110-151859
base: tip/x86/core
patch link: https://lore.kernel.org/r/20240110071522.1308935-4-coxu%40redhat.com
patch subject: [PATCH v2 3/5] crash_dump: retrieve dm crypt key in kdump kernel
config: i386-buildonly-randconfig-003-20240113 (https://download.01.org/0day-ci/archive/20240114/202401140240.upb0a18s-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240114/202401140240.upb0a18s-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202401140240.upb0a18s-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> kernel/crash_dump_dm_crypt.c:29:16: warning: no previous prototype for 'dm_crypt_key_read' [-Wmissing-prototypes]
29 | ssize_t __weak dm_crypt_key_read(char *buf, size_t count, u64 *ppos)
| ^~~~~~~~~~~~~~~~~
kernel/crash_dump_dm_crypt.c:83:6: warning: no previous prototype for 'wipe_dm_crypt_key' [-Wmissing-prototypes]
83 | void wipe_dm_crypt_key(void)
| ^~~~~~~~~~~~~~~~~
kernel/crash_dump_dm_crypt.c: In function 'crash_load_dm_crypt_key':
kernel/crash_dump_dm_crypt.c:207:16: error: variable 'kbuf' has initializer but incomplete type
207 | struct kexec_buf kbuf = {
| ^~~~~~~~~
kernel/crash_dump_dm_crypt.c:208:18: error: 'struct kexec_buf' has no member named 'image'
208 | .image = image,
| ^~~~~
kernel/crash_dump_dm_crypt.c:208:26: warning: excess elements in struct initializer
208 | .image = image,
| ^~~~~
kernel/crash_dump_dm_crypt.c:208:26: note: (near initialization for 'kbuf')
kernel/crash_dump_dm_crypt.c:209:18: error: 'struct kexec_buf' has no member named 'buf_min'
209 | .buf_min = 0,
| ^~~~~~~
kernel/crash_dump_dm_crypt.c:209:28: warning: excess elements in struct initializer
209 | .buf_min = 0,
| ^
kernel/crash_dump_dm_crypt.c:209:28: note: (near initialization for 'kbuf')
kernel/crash_dump_dm_crypt.c:210:18: error: 'struct kexec_buf' has no member named 'buf_max'
210 | .buf_max = ULONG_MAX,
| ^~~~~~~
In file included from include/linux/limits.h:7,
from include/linux/kernel.h:17,
from arch/x86/include/asm/percpu.h:27,
from arch/x86/include/asm/nospec-branch.h:14,
from arch/x86/include/asm/irqflags.h:9,
from include/linux/irqflags.h:17,
from include/linux/rcupdate.h:26,
from include/linux/rbtree.h:24,
from include/linux/key.h:15,
from kernel/crash_dump_dm_crypt.c:2:
include/vdso/limits.h:13:25: warning: excess elements in struct initializer
13 | #define ULONG_MAX (~0UL)
| ^
kernel/crash_dump_dm_crypt.c:210:28: note: in expansion of macro 'ULONG_MAX'
210 | .buf_max = ULONG_MAX,
| ^~~~~~~~~
include/vdso/limits.h:13:25: note: (near initialization for 'kbuf')
13 | #define ULONG_MAX (~0UL)
| ^
kernel/crash_dump_dm_crypt.c:210:28: note: in expansion of macro 'ULONG_MAX'
210 | .buf_max = ULONG_MAX,
| ^~~~~~~~~
kernel/crash_dump_dm_crypt.c:211:18: error: 'struct kexec_buf' has no member named 'top_down'
211 | .top_down = false,
| ^~~~~~~~
kernel/crash_dump_dm_crypt.c:211:29: warning: excess elements in struct initializer
211 | .top_down = false,
| ^~~~~
kernel/crash_dump_dm_crypt.c:211:29: note: (near initialization for 'kbuf')
kernel/crash_dump_dm_crypt.c:212:18: error: 'struct kexec_buf' has no member named 'random'
212 | .random = true,
| ^~~~~~
kernel/crash_dump_dm_crypt.c:212:27: warning: excess elements in struct initializer
212 | .random = true,
| ^~~~
kernel/crash_dump_dm_crypt.c:212:27: note: (near initialization for 'kbuf')
kernel/crash_dump_dm_crypt.c:207:26: error: storage size of 'kbuf' isn't known
207 | struct kexec_buf kbuf = {
| ^~~~
kernel/crash_dump_dm_crypt.c:222:20: error: 'KEXEC_BUF_MEM_UNKNOWN' undeclared (first use in this function)
222 | kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
| ^~~~~~~~~~~~~~~~~~~~~
kernel/crash_dump_dm_crypt.c:222:20: note: each undeclared identifier is reported only once for each function it appears in
kernel/crash_dump_dm_crypt.c:223:15: error: implicit declaration of function 'kexec_add_buffer' [-Werror=implicit-function-declaration]
223 | ret = kexec_add_buffer(&kbuf);
| ^~~~~~~~~~~~~~~~
kernel/crash_dump_dm_crypt.c:207:26: warning: unused variable 'kbuf' [-Wunused-variable]
207 | struct kexec_buf kbuf = {
| ^~~~
cc1: some warnings being treated as errors
vim +/dm_crypt_key_read +29 kernel/crash_dump_dm_crypt.c
25
26 /*
27 * Architectures may override this function to read dm crypt key
28 */
> 29 ssize_t __weak dm_crypt_key_read(char *buf, size_t count, u64 *ppos)
30 {
31 struct kvec kvec = { .iov_base = buf, .iov_len = count };
32 struct iov_iter iter;
33
34 iov_iter_kvec(&iter, READ, &kvec, 1, count);
35 return read_from_oldmem(&iter, count, ppos, false);
36 }
37
@@ -15,6 +15,8 @@
extern unsigned long long elfcorehdr_addr;
extern unsigned long long elfcorehdr_size;
+extern unsigned long long luks_volume_key_addr;
+
#ifdef CONFIG_CRASH_DUMP
extern int elfcorehdr_alloc(unsigned long long *addr, unsigned long long *size);
extern void elfcorehdr_free(unsigned long long addr);
@@ -1,7 +1,82 @@
// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/key.h>
+#include <linux/keyctl.h>
#include <keys/user-type.h>
#include <linux/crash_dump.h>
+unsigned long long dm_crypt_key_addr;
+EXPORT_SYMBOL_GPL(dm_crypt_key_addr);
+
+static int __init setup_dmcryptkey(char *arg)
+{
+ char *end;
+
+ if (!arg)
+ return -EINVAL;
+ dm_crypt_key_addr = memparse(arg, &end);
+ if (end > arg)
+ return 0;
+
+ dm_crypt_key_addr = 0;
+ return -EINVAL;
+}
+
+early_param("dmcryptkey", setup_dmcryptkey);
+
+/*
+ * Architectures may override this function to read dm crypt key
+ */
+ssize_t __weak dm_crypt_key_read(char *buf, size_t count, u64 *ppos)
+{
+ struct kvec kvec = { .iov_base = buf, .iov_len = count };
+ struct iov_iter iter;
+
+ iov_iter_kvec(&iter, READ, &kvec, 1, count);
+ return read_from_oldmem(&iter, count, ppos, false);
+}
+
+static int retrive_kdump_dm_crypt_key(u8 *buffer, unsigned int *sz)
+{
+ unsigned int key_size;
+ size_t dm_crypt_keybuf_sz;
+ unsigned int *size_ptr;
+ char *dm_crypt_keybuf;
+ u64 addr;
+ int r;
+
+ if (dm_crypt_key_addr == 0) {
+ pr_debug("dm crypt key memory address inaccessible");
+ return -EINVAL;
+ }
+
+ addr = dm_crypt_key_addr;
+
+ /* Read dm crypt key size */
+ r = dm_crypt_key_read((char *)&key_size, sizeof(unsigned int), &addr);
+
+ if (r < 0)
+ return r;
+
+ pr_debug("Retrieve dm crypt key: size=%u\n", key_size);
+ /* Read in dm cryptrkey */
+ dm_crypt_keybuf_sz = sizeof(unsigned int) + key_size * sizeof(u8);
+ dm_crypt_keybuf = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
+ get_order(dm_crypt_keybuf_sz));
+ if (!dm_crypt_keybuf)
+ return -ENOMEM;
+
+ addr = dm_crypt_key_addr;
+ r = dm_crypt_key_read((char *)dm_crypt_keybuf, dm_crypt_keybuf_sz, &addr);
+
+ if (r < 0)
+ return r;
+ size_ptr = (unsigned int *)dm_crypt_keybuf;
+ memcpy(buffer, size_ptr + 1, key_size * sizeof(u8));
+ pr_debug("Retrieve dm crypt key (size=%u): %48ph...\n", key_size, buffer);
+ *sz = key_size;
+ return 0;
+}
+
static u8 *dm_crypt_key;
static unsigned int dm_crypt_key_size;
@@ -23,6 +98,43 @@ static DECLARE_DELAYED_WORK(wipe_dm_crypt_key_work, _wipe_dm_crypt_key);
static unsigned __read_mostly wipe_key_delay = 120; /* 2 mins */
+static int retore_dm_crypt_key_to_thread_keyring(const char *key_desc)
+{
+ key_ref_t keyring_ref, key_ref;
+ int ret;
+
+ /* find the target keyring (which must be writable) */
+ keyring_ref = lookup_user_key(KEY_SPEC_USER_KEYRING, 0x01, KEY_NEED_WRITE);
+ if (IS_ERR(keyring_ref)) {
+ pr_alert("Failed to get keyring");
+ return PTR_ERR(keyring_ref);
+ }
+
+ dm_crypt_key = kmalloc(128, GFP_KERNEL);
+ ret = retrive_kdump_dm_crypt_key(dm_crypt_key, &dm_crypt_key_size);
+ if (ret) {
+ kfree(dm_crypt_key);
+ return ret;
+ }
+
+ /* create or update the requested key and add it to the target keyring */
+ key_ref = key_create_or_update(keyring_ref, "user", key_desc,
+ dm_crypt_key, dm_crypt_key_size,
+ KEY_USR_ALL, KEY_ALLOC_IN_QUOTA);
+
+ if (!IS_ERR(key_ref)) {
+ ret = key_ref_to_ptr(key_ref)->serial;
+ key_ref_put(key_ref);
+ pr_alert("Success adding key %s", key_desc);
+ } else {
+ ret = PTR_ERR(key_ref);
+ pr_alert("Error when adding key");
+ }
+
+ key_ref_put(keyring_ref);
+ return ret;
+}
+
static int crash_save_temp_dm_crypt_key(const char *key_desc, size_t count)
{
const struct user_key_payload *ukp;
@@ -60,7 +172,8 @@ int crash_sysfs_dm_crypt_key_write(const char *key_desc, size_t count)
{
if (!is_kdump_kernel())
return crash_save_temp_dm_crypt_key(key_desc, count);
- return -EINVAL;
+ else
+ return retore_dm_crypt_key_to_thread_keyring(key_desc);
}
EXPORT_SYMBOL(crash_sysfs_dm_crypt_key_write);