Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478// 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>#include <linux/cc_platform.h>#include <linux/configfs.h>#include <linux/module.h>#include <linux/sysfs.h>
#define KEY_NUM_MAX 128 /* maximum dm crypt keys */#define KEY_SIZE_MAX 256 /* maximum dm crypt key size */#define KEY_DESC_MAX_LEN 128 /* maximum dm crypt key description size */
static unsigned int key_count;
struct dm_crypt_key { unsigned int key_size; char key_desc[KEY_DESC_MAX_LEN]; u8 data[KEY_SIZE_MAX];};
static struct keys_header { unsigned int total_keys; struct dm_crypt_key keys[] __counted_by(total_keys);} *keys_header;
static size_t get_keys_header_size(size_t total_keys){ return struct_size(keys_header, keys, total_keys);}
unsigned long long dm_crypt_keys_addr;EXPORT_SYMBOL_GPL(dm_crypt_keys_addr);
static int __init setup_dmcryptkeys(char *arg){ char *end;
if (!arg) return -EINVAL; dm_crypt_keys_addr = memparse(arg, &end); if (end > arg) return 0;
dm_crypt_keys_addr = 0; return -EINVAL;}
early_param("dmcryptkeys", setup_dmcryptkeys);
/* * Architectures may override this function to read dm crypt keys */ssize_t __weak dm_crypt_keys_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, cc_platform_has(CC_ATTR_MEM_ENCRYPT));}
static int add_key_to_keyring(struct dm_crypt_key *dm_key, key_ref_t keyring_ref){ key_ref_t key_ref; int r;
/* create or update the requested key and add it to the target keyring */ key_ref = key_create_or_update(keyring_ref, "user", dm_key->key_desc, dm_key->data, dm_key->key_size, KEY_USR_ALL, KEY_ALLOC_IN_QUOTA);
if (!IS_ERR(key_ref)) { r = key_ref_to_ptr(key_ref)->serial; key_ref_put(key_ref); kexec_dprintk("Success adding key %s", dm_key->key_desc); } else { r = PTR_ERR(key_ref); kexec_dprintk("Error when adding key"); }
key_ref_put(keyring_ref); return r;}
static void get_keys_from_kdump_reserved_memory(void){ struct keys_header *keys_header_loaded;
arch_kexec_unprotect_crashkres();
keys_header_loaded = kmap_local_page(pfn_to_page( kexec_crash_image->dm_crypt_keys_addr >> PAGE_SHIFT));
memcpy(keys_header, keys_header_loaded, get_keys_header_size(key_count)); kunmap_local(keys_header_loaded); arch_kexec_protect_crashkres();}
static int restore_dm_crypt_keys_to_thread_keyring(void){ struct dm_crypt_key *key; size_t keys_header_size; key_ref_t keyring_ref; u64 addr;
/* 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)) { kexec_dprintk("Failed to get the user keyring\n"); return PTR_ERR(keyring_ref); }
addr = dm_crypt_keys_addr; dm_crypt_keys_read((char *)&key_count, sizeof(key_count), &addr); if (key_count > KEY_NUM_MAX) { kexec_dprintk("Failed to read the number of dm-crypt keys\n"); return -1; }
kexec_dprintk("There are %u keys\n", key_count); addr = dm_crypt_keys_addr;
keys_header_size = get_keys_header_size(key_count); keys_header = kzalloc(keys_header_size, GFP_KERNEL); if (!keys_header) return -ENOMEM;
dm_crypt_keys_read((char *)keys_header, keys_header_size, &addr);
for (int i = 0; i < keys_header->total_keys; i++) { key = &keys_header->keys[i]; kexec_dprintk("Get key (size=%u)\n", key->key_size); add_key_to_keyring(key, keyring_ref); }
return 0;}
static int read_key_from_user_keyring(struct dm_crypt_key *dm_key){ const struct user_key_payload *ukp; struct key *key; int ret = 0;
kexec_dprintk("Requesting logon key %s", dm_key->key_desc); key = request_key(&key_type_logon, dm_key->key_desc, NULL);
if (IS_ERR(key)) { pr_warn("No such logon key %s\n", dm_key->key_desc); return PTR_ERR(key); }
down_read(&key->sem); ukp = user_key_payload_locked(key); if (!ukp) { ret = -EKEYREVOKED; goto out; }
if (ukp->datalen > KEY_SIZE_MAX) { pr_err("Key size %u exceeds maximum (%u)\n", ukp->datalen, KEY_SIZE_MAX); ret = -EINVAL; goto out; }
memcpy(dm_key->data, ukp->data, ukp->datalen); dm_key->key_size = ukp->datalen; kexec_dprintk("Get dm crypt key (size=%u) %s\n", dm_key->key_size, dm_key->key_desc);
out: up_read(&key->sem); key_put(key); return ret;}
struct config_key { struct config_item item; const char *description;};
static inline struct config_key *to_config_key(struct config_item *item){ return container_of(item, struct config_key, item);}
static ssize_t config_key_description_show(struct config_item *item, char *page){ return sysfs_emit(page, "%s\n", to_config_key(item)->description);}
static ssize_t config_key_description_store(struct config_item *item, const char *page, size_t count){ struct config_key *config_key = to_config_key(item); size_t len; int ret;
ret = -EINVAL; len = strcspn(page, "\n");
if (len > KEY_DESC_MAX_LEN) { pr_err("The key description shouldn't exceed %u characters", KEY_DESC_MAX_LEN); return ret; }
if (!len) return ret;
kfree(config_key->description); ret = -ENOMEM; config_key->description = kmemdup_nul(page, len, GFP_KERNEL); if (!config_key->description) return ret;
return count;}
CONFIGFS_ATTR(config_key_, description);
static struct configfs_attribute *config_key_attrs[] = { &config_key_attr_description, NULL,};
static void config_key_release(struct config_item *item){ kfree(to_config_key(item)); key_count--;}
static const struct configfs_item_operations config_key_item_ops = { .release = config_key_release,};
static const struct config_item_type config_key_type = { .ct_item_ops = &config_key_item_ops, .ct_attrs = config_key_attrs, .ct_owner = THIS_MODULE,};
static struct config_item *config_keys_make_item(struct config_group *group, const char *name){ struct config_key *config_key;
if (key_count > KEY_NUM_MAX) { pr_err("Only %u keys at maximum to be created\n", KEY_NUM_MAX); return ERR_PTR(-EINVAL); }
config_key = kzalloc_obj(struct config_key); if (!config_key) return ERR_PTR(-ENOMEM);
config_item_init_type_name(&config_key->item, name, &config_key_type);
key_count++;
return &config_key->item;}
static ssize_t config_keys_count_show(struct config_item *item, char *page){ return sysfs_emit(page, "%d\n", key_count);}
CONFIGFS_ATTR_RO(config_keys_, count);
static bool is_dm_key_reused;
static ssize_t config_keys_reuse_show(struct config_item *item, char *page){ return sysfs_emit(page, "%d\n", is_dm_key_reused);}
static ssize_t config_keys_reuse_store(struct config_item *item, const char *page, size_t count){ if (!kexec_crash_image || !kexec_crash_image->dm_crypt_keys_addr) { kexec_dprintk( "dm-crypt keys haven't be saved to crash-reserved memory\n"); return -EINVAL; }
if (kstrtobool(page, &is_dm_key_reused)) return -EINVAL;
if (is_dm_key_reused) get_keys_from_kdump_reserved_memory();
return count;}
CONFIGFS_ATTR(config_keys_, reuse);
static struct configfs_attribute *config_keys_attrs[] = { &config_keys_attr_count, &config_keys_attr_reuse, NULL,};
/* * Note that, since no extra work is required on ->drop_item(), * no ->drop_item() is provided. */static const struct configfs_group_operations config_keys_group_ops = { .make_item = config_keys_make_item,};
static const struct config_item_type config_keys_type = { .ct_group_ops = &config_keys_group_ops, .ct_attrs = config_keys_attrs, .ct_owner = THIS_MODULE,};
static bool restore;
static ssize_t config_keys_restore_show(struct config_item *item, char *page){ return sysfs_emit(page, "%d\n", restore);}
static ssize_t config_keys_restore_store(struct config_item *item, const char *page, size_t count){ if (!restore) restore_dm_crypt_keys_to_thread_keyring();
if (kstrtobool(page, &restore)) return -EINVAL;
return count;}
CONFIGFS_ATTR(config_keys_, restore);
static struct configfs_attribute *kdump_config_keys_attrs[] = { &config_keys_attr_restore, NULL,};
static const struct config_item_type kdump_config_keys_type = { .ct_attrs = kdump_config_keys_attrs, .ct_owner = THIS_MODULE,};
static struct configfs_subsystem config_keys_subsys = { .su_group = { .cg_item = { .ci_namebuf = "crash_dm_crypt_keys", .ci_type = &config_keys_type, }, },};
static int build_keys_header(void){ struct config_item *item = NULL; struct config_key *key; int i, r;
if (keys_header != NULL) kvfree(keys_header);
keys_header = kzalloc(get_keys_header_size(key_count), GFP_KERNEL); if (!keys_header) return -ENOMEM;
keys_header->total_keys = key_count;
i = 0; list_for_each_entry(item, &config_keys_subsys.su_group.cg_children, ci_entry) { if (item->ci_type != &config_key_type) continue;
key = to_config_key(item);
if (!key->description) { pr_warn("No key description for key %s\n", item->ci_name); return -EINVAL; }
strscpy(keys_header->keys[i].key_desc, key->description, KEY_DESC_MAX_LEN); r = read_key_from_user_keyring(&keys_header->keys[i]); if (r != 0) { kexec_dprintk("Failed to read key %s\n", keys_header->keys[i].key_desc); return r; } i++; kexec_dprintk("Found key: %s\n", item->ci_name); }
return 0;}
int crash_load_dm_crypt_keys(struct kimage *image){ struct kexec_buf kbuf = { .image = image, .buf_min = 0, .buf_max = ULONG_MAX, .top_down = false, .random = true, }; int r;
if (key_count <= 0) { kexec_dprintk("No dm-crypt keys\n"); return 0; }
if (!is_dm_key_reused) { image->dm_crypt_keys_addr = 0; r = build_keys_header(); if (r) { pr_err("Failed to build dm-crypt keys header, ret=%d\n", r); return r; } }
kbuf.buffer = keys_header; kbuf.bufsz = get_keys_header_size(key_count);
kbuf.memsz = kbuf.bufsz; kbuf.buf_align = ELF_CORE_HEADER_ALIGN; kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; r = kexec_add_buffer(&kbuf); if (r) { pr_err("Failed to call kexec_add_buffer, ret=%d\n", r); kvfree((void *)kbuf.buffer); return r; } image->dm_crypt_keys_addr = kbuf.mem; image->dm_crypt_keys_sz = kbuf.bufsz; kexec_dprintk( "Loaded dm crypt keys to kexec_buffer bufsz=0x%lx memsz=0x%lx\n", kbuf.bufsz, kbuf.memsz);
return r;}
static int __init configfs_dmcrypt_keys_init(void){ int ret;
if (is_kdump_kernel()) { config_keys_subsys.su_group.cg_item.ci_type = &kdump_config_keys_type; }
config_group_init(&config_keys_subsys.su_group); mutex_init(&config_keys_subsys.su_mutex); ret = configfs_register_subsystem(&config_keys_subsys); if (ret) { pr_err("Error %d while registering subsystem %s\n", ret, config_keys_subsys.su_group.cg_item.ci_namebuf); goto out_unregister; }
return 0;
out_unregister: configfs_unregister_subsystem(&config_keys_subsys);
return ret;}
module_init(configfs_dmcrypt_keys_init);