|
1 | 1 | // SPDX-License-Identifier: GPL-2.0-only
|
| 2 | +#include <linux/key.h> |
| 3 | +#include <linux/keyctl.h> |
2 | 4 | #include <keys/user-type.h>
|
3 | 5 | #include <linux/crash_dump.h>
|
4 | 6 | #include <linux/configfs.h>
|
5 | 7 | #include <linux/module.h>
|
6 | 8 |
|
7 | 9 | #define KEY_NUM_MAX 128 /* maximum dm crypt keys */
|
| 10 | +#define KEY_SIZE_MAX 256 /* maximum dm crypt key size */ |
8 | 11 | #define KEY_DESC_MAX_LEN 128 /* maximum dm crypt key description size */
|
9 | 12 |
|
10 | 13 | static unsigned int key_count;
|
11 | 14 |
|
| 15 | +struct dm_crypt_key { |
| 16 | + unsigned int key_size; |
| 17 | + char key_desc[KEY_DESC_MAX_LEN]; |
| 18 | + u8 data[KEY_SIZE_MAX]; |
| 19 | +}; |
| 20 | + |
| 21 | +static struct keys_header { |
| 22 | + unsigned int total_keys; |
| 23 | + struct dm_crypt_key keys[] __counted_by(total_keys); |
| 24 | +} *keys_header; |
| 25 | + |
| 26 | +static size_t get_keys_header_size(size_t total_keys) |
| 27 | +{ |
| 28 | + return struct_size(keys_header, keys, total_keys); |
| 29 | +} |
| 30 | + |
| 31 | +static int read_key_from_user_keying(struct dm_crypt_key *dm_key) |
| 32 | +{ |
| 33 | + const struct user_key_payload *ukp; |
| 34 | + struct key *key; |
| 35 | + |
| 36 | + kexec_dprintk("Requesting logon key %s", dm_key->key_desc); |
| 37 | + key = request_key(&key_type_logon, dm_key->key_desc, NULL); |
| 38 | + |
| 39 | + if (IS_ERR(key)) { |
| 40 | + pr_warn("No such logon key %s\n", dm_key->key_desc); |
| 41 | + return PTR_ERR(key); |
| 42 | + } |
| 43 | + |
| 44 | + ukp = user_key_payload_locked(key); |
| 45 | + if (!ukp) |
| 46 | + return -EKEYREVOKED; |
| 47 | + |
| 48 | + if (ukp->datalen > KEY_SIZE_MAX) { |
| 49 | + pr_err("Key size %u exceeds maximum (%u)\n", ukp->datalen, KEY_SIZE_MAX); |
| 50 | + return -EINVAL; |
| 51 | + } |
| 52 | + |
| 53 | + memcpy(dm_key->data, ukp->data, ukp->datalen); |
| 54 | + dm_key->key_size = ukp->datalen; |
| 55 | + kexec_dprintk("Get dm crypt key (size=%u) %s: %8ph\n", dm_key->key_size, |
| 56 | + dm_key->key_desc, dm_key->data); |
| 57 | + return 0; |
| 58 | +} |
| 59 | + |
12 | 60 | struct config_key {
|
13 | 61 | struct config_item item;
|
14 | 62 | const char *description;
|
@@ -130,6 +178,91 @@ static struct configfs_subsystem config_keys_subsys = {
|
130 | 178 | },
|
131 | 179 | };
|
132 | 180 |
|
| 181 | +static int build_keys_header(void) |
| 182 | +{ |
| 183 | + struct config_item *item = NULL; |
| 184 | + struct config_key *key; |
| 185 | + int i, r; |
| 186 | + |
| 187 | + if (keys_header != NULL) |
| 188 | + kvfree(keys_header); |
| 189 | + |
| 190 | + keys_header = kzalloc(get_keys_header_size(key_count), GFP_KERNEL); |
| 191 | + if (!keys_header) |
| 192 | + return -ENOMEM; |
| 193 | + |
| 194 | + keys_header->total_keys = key_count; |
| 195 | + |
| 196 | + i = 0; |
| 197 | + list_for_each_entry(item, &config_keys_subsys.su_group.cg_children, |
| 198 | + ci_entry) { |
| 199 | + if (item->ci_type != &config_key_type) |
| 200 | + continue; |
| 201 | + |
| 202 | + key = to_config_key(item); |
| 203 | + |
| 204 | + if (!key->description) { |
| 205 | + pr_warn("No key description for key %s\n", item->ci_name); |
| 206 | + return -EINVAL; |
| 207 | + } |
| 208 | + |
| 209 | + strscpy(keys_header->keys[i].key_desc, key->description, |
| 210 | + KEY_DESC_MAX_LEN); |
| 211 | + r = read_key_from_user_keying(&keys_header->keys[i]); |
| 212 | + if (r != 0) { |
| 213 | + kexec_dprintk("Failed to read key %s\n", |
| 214 | + keys_header->keys[i].key_desc); |
| 215 | + return r; |
| 216 | + } |
| 217 | + i++; |
| 218 | + kexec_dprintk("Found key: %s\n", item->ci_name); |
| 219 | + } |
| 220 | + |
| 221 | + return 0; |
| 222 | +} |
| 223 | + |
| 224 | +int crash_load_dm_crypt_keys(struct kimage *image) |
| 225 | +{ |
| 226 | + struct kexec_buf kbuf = { |
| 227 | + .image = image, |
| 228 | + .buf_min = 0, |
| 229 | + .buf_max = ULONG_MAX, |
| 230 | + .top_down = false, |
| 231 | + .random = true, |
| 232 | + }; |
| 233 | + int r; |
| 234 | + |
| 235 | + |
| 236 | + if (key_count <= 0) { |
| 237 | + kexec_dprintk("No dm-crypt keys\n"); |
| 238 | + return -ENOENT; |
| 239 | + } |
| 240 | + |
| 241 | + image->dm_crypt_keys_addr = 0; |
| 242 | + r = build_keys_header(); |
| 243 | + if (r) |
| 244 | + return r; |
| 245 | + |
| 246 | + kbuf.buffer = keys_header; |
| 247 | + kbuf.bufsz = get_keys_header_size(key_count); |
| 248 | + |
| 249 | + kbuf.memsz = kbuf.bufsz; |
| 250 | + kbuf.buf_align = ELF_CORE_HEADER_ALIGN; |
| 251 | + kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; |
| 252 | + r = kexec_add_buffer(&kbuf); |
| 253 | + if (r) { |
| 254 | + kvfree((void *)kbuf.buffer); |
| 255 | + return r; |
| 256 | + } |
| 257 | + image->dm_crypt_keys_addr = kbuf.mem; |
| 258 | + image->dm_crypt_keys_sz = kbuf.bufsz; |
| 259 | + kexec_dprintk( |
| 260 | + "Loaded dm crypt keys to kexec_buffer bufsz=0x%lx memsz=0x%lx\n", |
| 261 | + kbuf.bufsz, kbuf.memsz); |
| 262 | + |
| 263 | + return r; |
| 264 | +} |
| 265 | + |
133 | 266 | static int __init configfs_dmcrypt_keys_init(void)
|
134 | 267 | {
|
135 | 268 | int ret;
|
|
0 commit comments