Skip to content

Commit 180cf31

Browse files
coibyakpm00
authored andcommitted
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]>
1 parent bf454ec commit 180cf31

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
@@ -547,6 +547,34 @@ from within add_taint() whenever the value set in this bitmask matches with the
547547
bit flag being set by add_taint().
548548
This will cause a kdump to occur at the add_taint()->panic() call.
549549

550+
Write the dump file to encrypted disk volume
551+
============================================
552+
553+
CONFIG_CRASH_DM_CRYPT can be enabled to support saving the dump file to an
554+
encrypted disk volume. User space can interact with
555+
/sys/kernel/config/crash_dm_crypt_keys for setup,
556+
557+
1. Tell the first kernel what logon keys are needed to unlock the disk volumes,
558+
# Add key #1
559+
mkdir /sys/kernel/config/crash_dm_crypt_keys/7d26b7b4-e342-4d2d-b660-7426b0996720
560+
# Add key #1's description
561+
echo cryptsetup:7d26b7b4-e342-4d2d-b660-7426b0996720 > /sys/kernel/config/crash_dm_crypt_keys/description
562+
563+
# how many keys do we have now?
564+
cat /sys/kernel/config/crash_dm_crypt_keys/count
565+
1
566+
567+
# Add key #2 in the same way
568+
569+
# how many keys do we have now?
570+
cat /sys/kernel/config/crash_dm_crypt_keys/count
571+
2
572+
573+
2. Load the dump-capture kernel
574+
575+
3. After the dump-capture kerne get booted, restore the keys to user keyring
576+
echo yes > /sys/kernel/crash_dm_crypt_keys/restore
577+
550578
Contact
551579
=======
552580

kernel/Kconfig.kexec

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

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

kernel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ obj-$(CONFIG_VMCORE_INFO) += vmcore_info.o elfcorehdr.o
7777
obj-$(CONFIG_CRASH_RESERVE) += crash_reserve.o
7878
obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
7979
obj-$(CONFIG_CRASH_DUMP) += crash_core.o
80+
obj-$(CONFIG_CRASH_DM_CRYPT) += crash_dump_dm_crypt.o
8081
obj-$(CONFIG_KEXEC) += kexec.o
8182
obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
8283
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)