// SPDX-License-Identifier: GPL-2.0-only #include #include #include #include #include #include #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); } 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 read_key_from_user_keying(struct dm_crypt_key *dm_key) { const struct user_key_payload *ukp; struct key *key; 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); } ukp = user_key_payload_locked(key); if (!ukp) return -EKEYREVOKED; if (ukp->datalen > KEY_SIZE_MAX) { pr_err("Key size %u exceeds maximum (%u)\n", ukp->datalen, KEY_SIZE_MAX); return -EINVAL; } memcpy(dm_key->data, ukp->data, ukp->datalen); dm_key->key_size = ukp->datalen; kexec_dprintk("Get dm crypt key (size=%u) %s: %8ph\n", dm_key->key_size, dm_key->key_desc, dm_key->data); return 0; } 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 sprintf(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 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(sizeof(struct config_key), GFP_KERNEL); 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 sprintf(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 sprintf(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 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 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_keying(&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 -ENOENT; } if (!is_dm_key_reused) { image->dm_crypt_keys_addr = 0; r = build_keys_header(); if (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) { 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; 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);