162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Module and Firmware Pinning Security Module 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2011-2016 Google Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Kees Cook <keescook@chromium.org> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#define pr_fmt(fmt) "LoadPin: " fmt 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/module.h> 1362306a36Sopenharmony_ci#include <linux/fs.h> 1462306a36Sopenharmony_ci#include <linux/kernel_read_file.h> 1562306a36Sopenharmony_ci#include <linux/lsm_hooks.h> 1662306a36Sopenharmony_ci#include <linux/mount.h> 1762306a36Sopenharmony_ci#include <linux/blkdev.h> 1862306a36Sopenharmony_ci#include <linux/path.h> 1962306a36Sopenharmony_ci#include <linux/sched.h> /* current */ 2062306a36Sopenharmony_ci#include <linux/string_helpers.h> 2162306a36Sopenharmony_ci#include <linux/dm-verity-loadpin.h> 2262306a36Sopenharmony_ci#include <uapi/linux/loadpin.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#define VERITY_DIGEST_FILE_HEADER "# LOADPIN_TRUSTED_VERITY_ROOT_DIGESTS" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistatic void report_load(const char *origin, struct file *file, char *operation) 2762306a36Sopenharmony_ci{ 2862306a36Sopenharmony_ci char *cmdline, *pathname; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci pathname = kstrdup_quotable_file(file, GFP_KERNEL); 3162306a36Sopenharmony_ci cmdline = kstrdup_quotable_cmdline(current, GFP_KERNEL); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci pr_notice("%s %s obj=%s%s%s pid=%d cmdline=%s%s%s\n", 3462306a36Sopenharmony_ci origin, operation, 3562306a36Sopenharmony_ci (pathname && pathname[0] != '<') ? "\"" : "", 3662306a36Sopenharmony_ci pathname, 3762306a36Sopenharmony_ci (pathname && pathname[0] != '<') ? "\"" : "", 3862306a36Sopenharmony_ci task_pid_nr(current), 3962306a36Sopenharmony_ci cmdline ? "\"" : "", cmdline, cmdline ? "\"" : ""); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci kfree(cmdline); 4262306a36Sopenharmony_ci kfree(pathname); 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int enforce = IS_ENABLED(CONFIG_SECURITY_LOADPIN_ENFORCE); 4662306a36Sopenharmony_cistatic char *exclude_read_files[READING_MAX_ID]; 4762306a36Sopenharmony_cistatic int ignore_read_file_id[READING_MAX_ID] __ro_after_init; 4862306a36Sopenharmony_cistatic struct super_block *pinned_root; 4962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(pinned_root_spinlock); 5062306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_LOADPIN_VERITY 5162306a36Sopenharmony_cistatic bool deny_reading_verity_digests; 5262306a36Sopenharmony_ci#endif 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 5562306a36Sopenharmony_cistatic struct ctl_table loadpin_sysctl_table[] = { 5662306a36Sopenharmony_ci { 5762306a36Sopenharmony_ci .procname = "enforce", 5862306a36Sopenharmony_ci .data = &enforce, 5962306a36Sopenharmony_ci .maxlen = sizeof(int), 6062306a36Sopenharmony_ci .mode = 0644, 6162306a36Sopenharmony_ci .proc_handler = proc_dointvec_minmax, 6262306a36Sopenharmony_ci .extra1 = SYSCTL_ONE, 6362306a36Sopenharmony_ci .extra2 = SYSCTL_ONE, 6462306a36Sopenharmony_ci }, 6562306a36Sopenharmony_ci { } 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_cistatic void set_sysctl(bool is_writable) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci /* 7162306a36Sopenharmony_ci * If load pinning is not enforced via a read-only block 7262306a36Sopenharmony_ci * device, allow sysctl to change modes for testing. 7362306a36Sopenharmony_ci */ 7462306a36Sopenharmony_ci if (is_writable) 7562306a36Sopenharmony_ci loadpin_sysctl_table[0].extra1 = SYSCTL_ZERO; 7662306a36Sopenharmony_ci else 7762306a36Sopenharmony_ci loadpin_sysctl_table[0].extra1 = SYSCTL_ONE; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci#else 8062306a36Sopenharmony_cistatic inline void set_sysctl(bool is_writable) { } 8162306a36Sopenharmony_ci#endif 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void report_writable(struct super_block *mnt_sb, bool writable) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci if (mnt_sb->s_bdev) { 8662306a36Sopenharmony_ci pr_info("%pg (%u:%u): %s\n", mnt_sb->s_bdev, 8762306a36Sopenharmony_ci MAJOR(mnt_sb->s_bdev->bd_dev), 8862306a36Sopenharmony_ci MINOR(mnt_sb->s_bdev->bd_dev), 8962306a36Sopenharmony_ci writable ? "writable" : "read-only"); 9062306a36Sopenharmony_ci } else 9162306a36Sopenharmony_ci pr_info("mnt_sb lacks block device, treating as: writable\n"); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (!writable) 9462306a36Sopenharmony_ci pr_info("load pinning engaged.\n"); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* 9862306a36Sopenharmony_ci * This must be called after early kernel init, since then the rootdev 9962306a36Sopenharmony_ci * is available. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_cistatic bool sb_is_writable(struct super_block *mnt_sb) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci bool writable = true; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (mnt_sb->s_bdev) 10662306a36Sopenharmony_ci writable = !bdev_read_only(mnt_sb->s_bdev); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci return writable; 10962306a36Sopenharmony_ci} 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_cistatic void loadpin_sb_free_security(struct super_block *mnt_sb) 11262306a36Sopenharmony_ci{ 11362306a36Sopenharmony_ci /* 11462306a36Sopenharmony_ci * When unmounting the filesystem we were using for load 11562306a36Sopenharmony_ci * pinning, we acknowledge the superblock release, but make sure 11662306a36Sopenharmony_ci * no other modules or firmware can be loaded when we are in 11762306a36Sopenharmony_ci * enforcing mode. Otherwise, allow the root to be reestablished. 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(pinned_root) && mnt_sb == pinned_root) { 12062306a36Sopenharmony_ci if (enforce) { 12162306a36Sopenharmony_ci pinned_root = ERR_PTR(-EIO); 12262306a36Sopenharmony_ci pr_info("umount pinned fs: refusing further loads\n"); 12362306a36Sopenharmony_ci } else { 12462306a36Sopenharmony_ci pinned_root = NULL; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic int loadpin_check(struct file *file, enum kernel_read_file_id id) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci struct super_block *load_root; 13262306a36Sopenharmony_ci const char *origin = kernel_read_file_id_str(id); 13362306a36Sopenharmony_ci bool first_root_pin = false; 13462306a36Sopenharmony_ci bool load_root_writable; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* If the file id is excluded, ignore the pinning. */ 13762306a36Sopenharmony_ci if ((unsigned int)id < ARRAY_SIZE(ignore_read_file_id) && 13862306a36Sopenharmony_ci ignore_read_file_id[id]) { 13962306a36Sopenharmony_ci report_load(origin, file, "pinning-excluded"); 14062306a36Sopenharmony_ci return 0; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* This handles the older init_module API that has a NULL file. */ 14462306a36Sopenharmony_ci if (!file) { 14562306a36Sopenharmony_ci if (!enforce) { 14662306a36Sopenharmony_ci report_load(origin, NULL, "old-api-pinning-ignored"); 14762306a36Sopenharmony_ci return 0; 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci report_load(origin, NULL, "old-api-denied"); 15162306a36Sopenharmony_ci return -EPERM; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci load_root = file->f_path.mnt->mnt_sb; 15562306a36Sopenharmony_ci load_root_writable = sb_is_writable(load_root); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci /* First loaded module/firmware defines the root for all others. */ 15862306a36Sopenharmony_ci spin_lock(&pinned_root_spinlock); 15962306a36Sopenharmony_ci /* 16062306a36Sopenharmony_ci * pinned_root is only NULL at startup or when the pinned root has 16162306a36Sopenharmony_ci * been unmounted while we are not in enforcing mode. Otherwise, it 16262306a36Sopenharmony_ci * is either a valid reference, or an ERR_PTR. 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_ci if (!pinned_root) { 16562306a36Sopenharmony_ci pinned_root = load_root; 16662306a36Sopenharmony_ci first_root_pin = true; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci spin_unlock(&pinned_root_spinlock); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci if (first_root_pin) { 17162306a36Sopenharmony_ci report_writable(pinned_root, load_root_writable); 17262306a36Sopenharmony_ci set_sysctl(load_root_writable); 17362306a36Sopenharmony_ci report_load(origin, file, "pinned"); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci if (IS_ERR_OR_NULL(pinned_root) || 17762306a36Sopenharmony_ci ((load_root != pinned_root) && !dm_verity_loadpin_is_bdev_trusted(load_root->s_bdev))) { 17862306a36Sopenharmony_ci if (unlikely(!enforce)) { 17962306a36Sopenharmony_ci report_load(origin, file, "pinning-ignored"); 18062306a36Sopenharmony_ci return 0; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci report_load(origin, file, "denied"); 18462306a36Sopenharmony_ci return -EPERM; 18562306a36Sopenharmony_ci } 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic int loadpin_read_file(struct file *file, enum kernel_read_file_id id, 19162306a36Sopenharmony_ci bool contents) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci /* 19462306a36Sopenharmony_ci * LoadPin only cares about the _origin_ of a file, not its 19562306a36Sopenharmony_ci * contents, so we can ignore the "are full contents available" 19662306a36Sopenharmony_ci * argument here. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_ci return loadpin_check(file, id); 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic int loadpin_load_data(enum kernel_load_data_id id, bool contents) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci /* 20462306a36Sopenharmony_ci * LoadPin only cares about the _origin_ of a file, not its 20562306a36Sopenharmony_ci * contents, so a NULL file is passed, and we can ignore the 20662306a36Sopenharmony_ci * state of "contents". 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_ci return loadpin_check(NULL, (enum kernel_read_file_id) id); 20962306a36Sopenharmony_ci} 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_cistatic struct security_hook_list loadpin_hooks[] __ro_after_init = { 21262306a36Sopenharmony_ci LSM_HOOK_INIT(sb_free_security, loadpin_sb_free_security), 21362306a36Sopenharmony_ci LSM_HOOK_INIT(kernel_read_file, loadpin_read_file), 21462306a36Sopenharmony_ci LSM_HOOK_INIT(kernel_load_data, loadpin_load_data), 21562306a36Sopenharmony_ci}; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic void __init parse_exclude(void) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci int i, j; 22062306a36Sopenharmony_ci char *cur; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* 22362306a36Sopenharmony_ci * Make sure all the arrays stay within expected sizes. This 22462306a36Sopenharmony_ci * is slightly weird because kernel_read_file_str[] includes 22562306a36Sopenharmony_ci * READING_MAX_ID, which isn't actually meaningful here. 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(exclude_read_files) != 22862306a36Sopenharmony_ci ARRAY_SIZE(ignore_read_file_id)); 22962306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(kernel_read_file_str) < 23062306a36Sopenharmony_ci ARRAY_SIZE(ignore_read_file_id)); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(exclude_read_files); i++) { 23362306a36Sopenharmony_ci cur = exclude_read_files[i]; 23462306a36Sopenharmony_ci if (!cur) 23562306a36Sopenharmony_ci break; 23662306a36Sopenharmony_ci if (*cur == '\0') 23762306a36Sopenharmony_ci continue; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci for (j = 0; j < ARRAY_SIZE(ignore_read_file_id); j++) { 24062306a36Sopenharmony_ci if (strcmp(cur, kernel_read_file_str[j]) == 0) { 24162306a36Sopenharmony_ci pr_info("excluding: %s\n", 24262306a36Sopenharmony_ci kernel_read_file_str[j]); 24362306a36Sopenharmony_ci ignore_read_file_id[j] = 1; 24462306a36Sopenharmony_ci /* 24562306a36Sopenharmony_ci * Can not break, because one read_file_str 24662306a36Sopenharmony_ci * may map to more than on read_file_id. 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic int __init loadpin_init(void) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci pr_info("ready to pin (currently %senforcing)\n", 25662306a36Sopenharmony_ci enforce ? "" : "not "); 25762306a36Sopenharmony_ci parse_exclude(); 25862306a36Sopenharmony_ci#ifdef CONFIG_SYSCTL 25962306a36Sopenharmony_ci if (!register_sysctl("kernel/loadpin", loadpin_sysctl_table)) 26062306a36Sopenharmony_ci pr_notice("sysctl registration failed!\n"); 26162306a36Sopenharmony_ci#endif 26262306a36Sopenharmony_ci security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin"); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return 0; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ciDEFINE_LSM(loadpin) = { 26862306a36Sopenharmony_ci .name = "loadpin", 26962306a36Sopenharmony_ci .init = loadpin_init, 27062306a36Sopenharmony_ci}; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci#ifdef CONFIG_SECURITY_LOADPIN_VERITY 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cienum loadpin_securityfs_interface_index { 27562306a36Sopenharmony_ci LOADPIN_DM_VERITY, 27662306a36Sopenharmony_ci}; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_cistatic int read_trusted_verity_root_digests(unsigned int fd) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci struct fd f; 28162306a36Sopenharmony_ci void *data; 28262306a36Sopenharmony_ci int rc; 28362306a36Sopenharmony_ci char *p, *d; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci if (deny_reading_verity_digests) 28662306a36Sopenharmony_ci return -EPERM; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* The list of trusted root digests can only be set up once */ 28962306a36Sopenharmony_ci if (!list_empty(&dm_verity_loadpin_trusted_root_digests)) 29062306a36Sopenharmony_ci return -EPERM; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci f = fdget(fd); 29362306a36Sopenharmony_ci if (!f.file) 29462306a36Sopenharmony_ci return -EINVAL; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci data = kzalloc(SZ_4K, GFP_KERNEL); 29762306a36Sopenharmony_ci if (!data) { 29862306a36Sopenharmony_ci rc = -ENOMEM; 29962306a36Sopenharmony_ci goto err; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci rc = kernel_read_file(f.file, 0, (void **)&data, SZ_4K - 1, NULL, READING_POLICY); 30362306a36Sopenharmony_ci if (rc < 0) 30462306a36Sopenharmony_ci goto err; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci p = data; 30762306a36Sopenharmony_ci p[rc] = '\0'; 30862306a36Sopenharmony_ci p = strim(p); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci p = strim(data); 31162306a36Sopenharmony_ci while ((d = strsep(&p, "\n")) != NULL) { 31262306a36Sopenharmony_ci int len; 31362306a36Sopenharmony_ci struct dm_verity_loadpin_trusted_root_digest *trd; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (d == data) { 31662306a36Sopenharmony_ci /* first line, validate header */ 31762306a36Sopenharmony_ci if (strcmp(d, VERITY_DIGEST_FILE_HEADER)) { 31862306a36Sopenharmony_ci rc = -EPROTO; 31962306a36Sopenharmony_ci goto err; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci continue; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci len = strlen(d); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci if (len % 2) { 32862306a36Sopenharmony_ci rc = -EPROTO; 32962306a36Sopenharmony_ci goto err; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci len /= 2; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci trd = kzalloc(struct_size(trd, data, len), GFP_KERNEL); 33562306a36Sopenharmony_ci if (!trd) { 33662306a36Sopenharmony_ci rc = -ENOMEM; 33762306a36Sopenharmony_ci goto err; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci trd->len = len; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (hex2bin(trd->data, d, len)) { 34262306a36Sopenharmony_ci kfree(trd); 34362306a36Sopenharmony_ci rc = -EPROTO; 34462306a36Sopenharmony_ci goto err; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci list_add_tail(&trd->node, &dm_verity_loadpin_trusted_root_digests); 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (list_empty(&dm_verity_loadpin_trusted_root_digests)) { 35162306a36Sopenharmony_ci rc = -EPROTO; 35262306a36Sopenharmony_ci goto err; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci kfree(data); 35662306a36Sopenharmony_ci fdput(f); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return 0; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_cierr: 36162306a36Sopenharmony_ci kfree(data); 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci /* any failure in loading/parsing invalidates the entire list */ 36462306a36Sopenharmony_ci { 36562306a36Sopenharmony_ci struct dm_verity_loadpin_trusted_root_digest *trd, *tmp; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci list_for_each_entry_safe(trd, tmp, &dm_verity_loadpin_trusted_root_digests, node) { 36862306a36Sopenharmony_ci list_del(&trd->node); 36962306a36Sopenharmony_ci kfree(trd); 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci } 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci /* disallow further attempts after reading a corrupt/invalid file */ 37462306a36Sopenharmony_ci deny_reading_verity_digests = true; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci fdput(f); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci return rc; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci/******************************** securityfs ********************************/ 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic long dm_verity_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci void __user *uarg = (void __user *)arg; 38662306a36Sopenharmony_ci unsigned int fd; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci switch (cmd) { 38962306a36Sopenharmony_ci case LOADPIN_IOC_SET_TRUSTED_VERITY_DIGESTS: 39062306a36Sopenharmony_ci if (copy_from_user(&fd, uarg, sizeof(fd))) 39162306a36Sopenharmony_ci return -EFAULT; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci return read_trusted_verity_root_digests(fd); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci default: 39662306a36Sopenharmony_ci return -EINVAL; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic const struct file_operations loadpin_dm_verity_ops = { 40162306a36Sopenharmony_ci .unlocked_ioctl = dm_verity_ioctl, 40262306a36Sopenharmony_ci .compat_ioctl = compat_ptr_ioctl, 40362306a36Sopenharmony_ci}; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci/** 40662306a36Sopenharmony_ci * init_loadpin_securityfs - create the securityfs directory for LoadPin 40762306a36Sopenharmony_ci * 40862306a36Sopenharmony_ci * We can not put this method normally under the loadpin_init() code path since 40962306a36Sopenharmony_ci * the security subsystem gets initialized before the vfs caches. 41062306a36Sopenharmony_ci * 41162306a36Sopenharmony_ci * Returns 0 if the securityfs directory creation was successful. 41262306a36Sopenharmony_ci */ 41362306a36Sopenharmony_cistatic int __init init_loadpin_securityfs(void) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci struct dentry *loadpin_dir, *dentry; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci loadpin_dir = securityfs_create_dir("loadpin", NULL); 41862306a36Sopenharmony_ci if (IS_ERR(loadpin_dir)) { 41962306a36Sopenharmony_ci pr_err("LoadPin: could not create securityfs dir: %ld\n", 42062306a36Sopenharmony_ci PTR_ERR(loadpin_dir)); 42162306a36Sopenharmony_ci return PTR_ERR(loadpin_dir); 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci dentry = securityfs_create_file("dm-verity", 0600, loadpin_dir, 42562306a36Sopenharmony_ci (void *)LOADPIN_DM_VERITY, &loadpin_dm_verity_ops); 42662306a36Sopenharmony_ci if (IS_ERR(dentry)) { 42762306a36Sopenharmony_ci pr_err("LoadPin: could not create securityfs entry 'dm-verity': %ld\n", 42862306a36Sopenharmony_ci PTR_ERR(dentry)); 42962306a36Sopenharmony_ci return PTR_ERR(dentry); 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cifs_initcall(init_loadpin_securityfs); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci#endif /* CONFIG_SECURITY_LOADPIN_VERITY */ 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci/* Should not be mutable after boot, so not listed in sysfs (perm == 0). */ 44062306a36Sopenharmony_cimodule_param(enforce, int, 0); 44162306a36Sopenharmony_ciMODULE_PARM_DESC(enforce, "Enforce module/firmware pinning"); 44262306a36Sopenharmony_cimodule_param_array_named(exclude, exclude_read_files, charp, NULL, 0); 44362306a36Sopenharmony_ciMODULE_PARM_DESC(exclude, "Exclude pinning specific read file types"); 444