Skip to content

Commit 4c29f17

Browse files
committed
crash_dump: make dm crypt keys persist for the kdump kernel
JIRA: https://issues.redhat.com/browse/RHEL-104939 Upstream Status: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git commit 180cf31 Author: Coiby Xu <[email protected]> Date: Fri May 2 09:12:36 2025 +0800 crash_dump: make dm crypt keys persist for the kdump kernel A configfs /sys/kernel/config/crash_dm_crypt_keys is provided for user space to make the dm crypt keys persist for the kdump kernel. Take the case of dumping to a LUKS-encrypted target as an example, here is the life cycle of the kdump copies of LUKS volume keys, 1. After the 1st kernel loads the initramfs during boot, systemd uses an user-input passphrase to de-crypt the LUKS volume keys or simply TPM-sealed volume keys and then save the volume keys to specified keyring (using the --link-vk-to-keyring API) and the keys will expire within specified time. 2. A user space tool (kdump initramfs loader like kdump-utils) create key items inside /sys/kernel/config/crash_dm_crypt_keys to inform the 1st kernel which keys are needed. 3. When the kdump initramfs is loaded by the kexec_file_load syscall, the 1st kernel will iterate created key items, save the keys to kdump reserved memory. 4. When the 1st kernel crashes and the kdump initramfs is booted, the kdump initramfs asks the kdump kernel to create a user key using the key stored in kdump reserved memory by writing yes to /sys/kernel/crash_dm_crypt_keys/restore. Then the LUKS encrypted device is unlocked with libcryptsetup's --volume-key-keyring API. 5. The system gets rebooted to the 1st kernel after dumping vmcore to the LUKS encrypted device is finished Eventually the keys have to stay in the kdump reserved memory for the kdump kernel to unlock encrypted volumes. During this process, some measures like letting the keys expire within specified time are desirable to reduce security risk. This patch assumes, 1) there are 128 LUKS devices at maximum to be unlocked thus MAX_KEY_NUM=128. 2) a key description won't exceed 128 bytes thus KEY_DESC_MAX_LEN=128. And here is a demo on how to interact with /sys/kernel/config/crash_dm_crypt_keys, # Add key #1 mkdir /sys/kernel/config/crash_dm_crypt_keys/7d26b7b4-e342-4d2d-b660-7426b0996720 # Add key #1's description echo cryptsetup:7d26b7b4-e342-4d2d-b660-7426b0996720 > /sys/kernel/config/crash_dm_crypt_keys/description # how many keys do we have now? cat /sys/kernel/config/crash_dm_crypt_keys/count 1 # Add key# 2 in the same way # how many keys do we have now? cat /sys/kernel/config/crash_dm_crypt_keys/count 2 # the tree structure of /crash_dm_crypt_keys configfs tree /sys/kernel/config/crash_dm_crypt_keys/ /sys/kernel/config/crash_dm_crypt_keys/ ├── 7d26b7b4-e342-4d2d-b660-7426b0996720 │   └── description ├── count ├── fce2cd38-4d59-4317-8ce2-1fd24d52c46a │   └── description Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Coiby Xu <[email protected]> Acked-by: Baoquan He <[email protected]> Cc: "Daniel P. Berrange" <[email protected]> Cc: Dave Hansen <[email protected]> Cc: Dave Young <[email protected]> Cc: Jan Pazdziora <[email protected]> Cc: Liu Pingfan <[email protected]> Cc: Milan Broz <[email protected]> Cc: Ondrej Kozina <[email protected]> Cc: Vitaly Kuznetsov <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Coiby Xu <[email protected]>
1 parent dfd2d9b commit 4c29f17

File tree

4 files changed

+194
-0
lines changed

4 files changed

+194
-0
lines changed

Documentation/admin-guide/kdump/kdump.rst

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,34 @@ from within add_taint() whenever the value set in this bitmask matches with the
582582
bit flag being set by add_taint().
583583
This will cause a kdump to occur at the add_taint()->panic() call.
584584

585+
Write the dump file to encrypted disk volume
586+
============================================
587+
588+
CONFIG_CRASH_DM_CRYPT can be enabled to support saving the dump file to an
589+
encrypted disk volume. User space can interact with
590+
/sys/kernel/config/crash_dm_crypt_keys for setup,
591+
592+
1. Tell the first kernel what logon keys are needed to unlock the disk volumes,
593+
# Add key #1
594+
mkdir /sys/kernel/config/crash_dm_crypt_keys/7d26b7b4-e342-4d2d-b660-7426b0996720
595+
# Add key #1's description
596+
echo cryptsetup:7d26b7b4-e342-4d2d-b660-7426b0996720 > /sys/kernel/config/crash_dm_crypt_keys/description
597+
598+
# how many keys do we have now?
599+
cat /sys/kernel/config/crash_dm_crypt_keys/count
600+
1
601+
602+
# Add key #2 in the same way
603+
604+
# how many keys do we have now?
605+
cat /sys/kernel/config/crash_dm_crypt_keys/count
606+
2
607+
608+
2. Load the dump-capture kernel
609+
610+
3. After the dump-capture kerne get booted, restore the keys to user keyring
611+
echo yes > /sys/kernel/crash_dm_crypt_keys/restore
612+
585613
Contact
586614
=======
587615

kernel/Kconfig.kexec

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,17 @@ config CRASH_DUMP
116116
For s390, this option also enables zfcpdump.
117117
See also <file:Documentation/s390/zfcpdump.rst>
118118

119+
config CRASH_DM_CRYPT
120+
bool "Support saving crash dump to dm-crypt encrypted volume"
121+
depends on KEXEC_FILE
122+
depends on CRASH_DUMP
123+
depends on DM_CRYPT
124+
depends on CONFIGFS_FS
125+
help
126+
With this option enabled, user space can intereact with
127+
/sys/kernel/config/crash_dm_crypt_keys to make the dm crypt keys
128+
persistent for the dump-capture kernel.
129+
119130
config CRASH_HOTPLUG
120131
bool "Update the crash elfcorehdr on system configuration changes"
121132
default y

kernel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ obj-$(CONFIG_VMCORE_INFO) += vmcore_info.o elfcorehdr.o
7575
obj-$(CONFIG_CRASH_RESERVE) += crash_reserve.o
7676
obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
7777
obj-$(CONFIG_CRASH_DUMP) += crash_core.o
78+
obj-$(CONFIG_CRASH_DM_CRYPT) += crash_dump_dm_crypt.o
7879
obj-$(CONFIG_KEXEC) += kexec.o
7980
obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
8081
obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o

kernel/crash_dump_dm_crypt.c

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
#include <keys/user-type.h>
3+
#include <linux/crash_dump.h>
4+
#include <linux/configfs.h>
5+
#include <linux/module.h>
6+
7+
#define KEY_NUM_MAX 128 /* maximum dm crypt keys */
8+
#define KEY_DESC_MAX_LEN 128 /* maximum dm crypt key description size */
9+
10+
static unsigned int key_count;
11+
12+
struct config_key {
13+
struct config_item item;
14+
const char *description;
15+
};
16+
17+
static inline struct config_key *to_config_key(struct config_item *item)
18+
{
19+
return container_of(item, struct config_key, item);
20+
}
21+
22+
static ssize_t config_key_description_show(struct config_item *item, char *page)
23+
{
24+
return sprintf(page, "%s\n", to_config_key(item)->description);
25+
}
26+
27+
static ssize_t config_key_description_store(struct config_item *item,
28+
const char *page, size_t count)
29+
{
30+
struct config_key *config_key = to_config_key(item);
31+
size_t len;
32+
int ret;
33+
34+
ret = -EINVAL;
35+
len = strcspn(page, "\n");
36+
37+
if (len > KEY_DESC_MAX_LEN) {
38+
pr_err("The key description shouldn't exceed %u characters", KEY_DESC_MAX_LEN);
39+
return ret;
40+
}
41+
42+
if (!len)
43+
return ret;
44+
45+
kfree(config_key->description);
46+
ret = -ENOMEM;
47+
config_key->description = kmemdup_nul(page, len, GFP_KERNEL);
48+
if (!config_key->description)
49+
return ret;
50+
51+
return count;
52+
}
53+
54+
CONFIGFS_ATTR(config_key_, description);
55+
56+
static struct configfs_attribute *config_key_attrs[] = {
57+
&config_key_attr_description,
58+
NULL,
59+
};
60+
61+
static void config_key_release(struct config_item *item)
62+
{
63+
kfree(to_config_key(item));
64+
key_count--;
65+
}
66+
67+
static struct configfs_item_operations config_key_item_ops = {
68+
.release = config_key_release,
69+
};
70+
71+
static const struct config_item_type config_key_type = {
72+
.ct_item_ops = &config_key_item_ops,
73+
.ct_attrs = config_key_attrs,
74+
.ct_owner = THIS_MODULE,
75+
};
76+
77+
static struct config_item *config_keys_make_item(struct config_group *group,
78+
const char *name)
79+
{
80+
struct config_key *config_key;
81+
82+
if (key_count > KEY_NUM_MAX) {
83+
pr_err("Only %u keys at maximum to be created\n", KEY_NUM_MAX);
84+
return ERR_PTR(-EINVAL);
85+
}
86+
87+
config_key = kzalloc(sizeof(struct config_key), GFP_KERNEL);
88+
if (!config_key)
89+
return ERR_PTR(-ENOMEM);
90+
91+
config_item_init_type_name(&config_key->item, name, &config_key_type);
92+
93+
key_count++;
94+
95+
return &config_key->item;
96+
}
97+
98+
static ssize_t config_keys_count_show(struct config_item *item, char *page)
99+
{
100+
return sprintf(page, "%d\n", key_count);
101+
}
102+
103+
CONFIGFS_ATTR_RO(config_keys_, count);
104+
105+
static struct configfs_attribute *config_keys_attrs[] = {
106+
&config_keys_attr_count,
107+
NULL,
108+
};
109+
110+
/*
111+
* Note that, since no extra work is required on ->drop_item(),
112+
* no ->drop_item() is provided.
113+
*/
114+
static struct configfs_group_operations config_keys_group_ops = {
115+
.make_item = config_keys_make_item,
116+
};
117+
118+
static const struct config_item_type config_keys_type = {
119+
.ct_group_ops = &config_keys_group_ops,
120+
.ct_attrs = config_keys_attrs,
121+
.ct_owner = THIS_MODULE,
122+
};
123+
124+
static struct configfs_subsystem config_keys_subsys = {
125+
.su_group = {
126+
.cg_item = {
127+
.ci_namebuf = "crash_dm_crypt_keys",
128+
.ci_type = &config_keys_type,
129+
},
130+
},
131+
};
132+
133+
static int __init configfs_dmcrypt_keys_init(void)
134+
{
135+
int ret;
136+
137+
config_group_init(&config_keys_subsys.su_group);
138+
mutex_init(&config_keys_subsys.su_mutex);
139+
ret = configfs_register_subsystem(&config_keys_subsys);
140+
if (ret) {
141+
pr_err("Error %d while registering subsystem %s\n", ret,
142+
config_keys_subsys.su_group.cg_item.ci_namebuf);
143+
goto out_unregister;
144+
}
145+
146+
return 0;
147+
148+
out_unregister:
149+
configfs_unregister_subsystem(&config_keys_subsys);
150+
151+
return ret;
152+
}
153+
154+
module_init(configfs_dmcrypt_keys_init);

0 commit comments

Comments
 (0)