Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * mokvar-table.c
0004  *
0005  * Copyright (c) 2020 Red Hat
0006  * Author: Lenny Szubowicz <lszubowi@redhat.com>
0007  *
0008  * This module contains the kernel support for the Linux EFI Machine
0009  * Owner Key (MOK) variable configuration table, which is identified by
0010  * the LINUX_EFI_MOK_VARIABLE_TABLE_GUID.
0011  *
0012  * This EFI configuration table provides a more robust alternative to
0013  * EFI volatile variables by which an EFI boot loader can pass the
0014  * contents of the Machine Owner Key (MOK) certificate stores to the
0015  * kernel during boot. If both the EFI MOK config table and corresponding
0016  * EFI MOK variables are present, the table should be considered as
0017  * more authoritative.
0018  *
0019  * This module includes code that validates and maps the EFI MOK table,
0020  * if it's presence was detected very early in boot.
0021  *
0022  * Kernel interface routines are provided to walk through all the
0023  * entries in the MOK config table or to search for a specific named
0024  * entry.
0025  *
0026  * The contents of the individual named MOK config table entries are
0027  * made available to user space via read-only sysfs binary files under:
0028  *
0029  * /sys/firmware/efi/mok-variables/
0030  *
0031  */
0032 #define pr_fmt(fmt) "mokvar: " fmt
0033 
0034 #include <linux/capability.h>
0035 #include <linux/efi.h>
0036 #include <linux/init.h>
0037 #include <linux/io.h>
0038 #include <linux/kernel.h>
0039 #include <linux/kobject.h>
0040 #include <linux/list.h>
0041 #include <linux/slab.h>
0042 
0043 #include <asm/early_ioremap.h>
0044 
0045 /*
0046  * The LINUX_EFI_MOK_VARIABLE_TABLE_GUID config table is a packed
0047  * sequence of struct efi_mokvar_table_entry, one for each named
0048  * MOK variable. The sequence is terminated by an entry with a
0049  * completely NULL name and 0 data size.
0050  *
0051  * efi_mokvar_table_size is set to the computed size of the
0052  * MOK config table by efi_mokvar_table_init(). This will be
0053  * non-zero if and only if the table if present and has been
0054  * validated by efi_mokvar_table_init().
0055  */
0056 static size_t efi_mokvar_table_size;
0057 
0058 /*
0059  * efi_mokvar_table_va is the kernel virtual address at which the
0060  * EFI MOK config table has been mapped by efi_mokvar_sysfs_init().
0061  */
0062 static struct efi_mokvar_table_entry *efi_mokvar_table_va;
0063 
0064 /*
0065  * Each /sys/firmware/efi/mok-variables/ sysfs file is represented by
0066  * an instance of struct efi_mokvar_sysfs_attr on efi_mokvar_sysfs_list.
0067  * bin_attr.private points to the associated EFI MOK config table entry.
0068  *
0069  * This list is created during boot and then remains unchanged.
0070  * So no synchronization is currently required to walk the list.
0071  */
0072 struct efi_mokvar_sysfs_attr {
0073     struct bin_attribute bin_attr;
0074     struct list_head node;
0075 };
0076 
0077 static LIST_HEAD(efi_mokvar_sysfs_list);
0078 static struct kobject *mokvar_kobj;
0079 
0080 /*
0081  * efi_mokvar_table_init() - Early boot validation of EFI MOK config table
0082  *
0083  * If present, validate and compute the size of the EFI MOK variable
0084  * configuration table. This table may be provided by an EFI boot loader
0085  * as an alternative to ordinary EFI variables, due to platform-dependent
0086  * limitations. The memory occupied by this table is marked as reserved.
0087  *
0088  * This routine must be called before efi_free_boot_services() in order
0089  * to guarantee that it can mark the table as reserved.
0090  *
0091  * Implicit inputs:
0092  * efi.mokvar_table:    Physical address of EFI MOK variable config table
0093  *          or special value that indicates no such table.
0094  *
0095  * Implicit outputs:
0096  * efi_mokvar_table_size: Computed size of EFI MOK variable config table.
0097  *          The table is considered present and valid if this
0098  *          is non-zero.
0099  */
0100 void __init efi_mokvar_table_init(void)
0101 {
0102     efi_memory_desc_t md;
0103     void *va = NULL;
0104     unsigned long cur_offset = 0;
0105     unsigned long offset_limit;
0106     unsigned long map_size = 0;
0107     unsigned long map_size_needed = 0;
0108     unsigned long size;
0109     struct efi_mokvar_table_entry *mokvar_entry;
0110     int err;
0111 
0112     if (!efi_enabled(EFI_MEMMAP))
0113         return;
0114 
0115     if (efi.mokvar_table == EFI_INVALID_TABLE_ADDR)
0116         return;
0117     /*
0118      * The EFI MOK config table must fit within a single EFI memory
0119      * descriptor range.
0120      */
0121     err = efi_mem_desc_lookup(efi.mokvar_table, &md);
0122     if (err) {
0123         pr_warn("EFI MOKvar config table is not within the EFI memory map\n");
0124         return;
0125     }
0126 
0127     offset_limit = efi_mem_desc_end(&md) - efi.mokvar_table;
0128 
0129     /*
0130      * Validate the MOK config table. Since there is no table header
0131      * from which we could get the total size of the MOK config table,
0132      * we compute the total size as we validate each variably sized
0133      * entry, remapping as necessary.
0134      */
0135     err = -EINVAL;
0136     while (cur_offset + sizeof(*mokvar_entry) <= offset_limit) {
0137         mokvar_entry = va + cur_offset;
0138         map_size_needed = cur_offset + sizeof(*mokvar_entry);
0139         if (map_size_needed > map_size) {
0140             if (va)
0141                 early_memunmap(va, map_size);
0142             /*
0143              * Map a little more than the fixed size entry
0144              * header, anticipating some data. It's safe to
0145              * do so as long as we stay within current memory
0146              * descriptor.
0147              */
0148             map_size = min(map_size_needed + 2*EFI_PAGE_SIZE,
0149                        offset_limit);
0150             va = early_memremap(efi.mokvar_table, map_size);
0151             if (!va) {
0152                 pr_err("Failed to map EFI MOKvar config table pa=0x%lx, size=%lu.\n",
0153                        efi.mokvar_table, map_size);
0154                 return;
0155             }
0156             mokvar_entry = va + cur_offset;
0157         }
0158 
0159         /* Check for last sentinel entry */
0160         if (mokvar_entry->name[0] == '\0') {
0161             if (mokvar_entry->data_size != 0)
0162                 break;
0163             err = 0;
0164             break;
0165         }
0166 
0167         /* Sanity check that the name is null terminated */
0168         size = strnlen(mokvar_entry->name,
0169                    sizeof(mokvar_entry->name));
0170         if (size >= sizeof(mokvar_entry->name))
0171             break;
0172 
0173         /* Advance to the next entry */
0174         cur_offset = map_size_needed + mokvar_entry->data_size;
0175     }
0176 
0177     if (va)
0178         early_memunmap(va, map_size);
0179     if (err) {
0180         pr_err("EFI MOKvar config table is not valid\n");
0181         return;
0182     }
0183 
0184     if (md.type == EFI_BOOT_SERVICES_DATA)
0185         efi_mem_reserve(efi.mokvar_table, map_size_needed);
0186 
0187     efi_mokvar_table_size = map_size_needed;
0188 }
0189 
0190 /*
0191  * efi_mokvar_entry_next() - Get next entry in the EFI MOK config table
0192  *
0193  * mokvar_entry:    Pointer to current EFI MOK config table entry
0194  *          or null. Null indicates get first entry.
0195  *          Passed by reference. This is updated to the
0196  *          same value as the return value.
0197  *
0198  * Returns:     Pointer to next EFI MOK config table entry
0199  *          or null, if there are no more entries.
0200  *          Same value is returned in the mokvar_entry
0201  *          parameter.
0202  *
0203  * This routine depends on the EFI MOK config table being entirely
0204  * mapped with it's starting virtual address in efi_mokvar_table_va.
0205  */
0206 struct efi_mokvar_table_entry *efi_mokvar_entry_next(
0207             struct efi_mokvar_table_entry **mokvar_entry)
0208 {
0209     struct efi_mokvar_table_entry *mokvar_cur;
0210     struct efi_mokvar_table_entry *mokvar_next;
0211     size_t size_cur;
0212 
0213     mokvar_cur = *mokvar_entry;
0214     *mokvar_entry = NULL;
0215 
0216     if (efi_mokvar_table_va == NULL)
0217         return NULL;
0218 
0219     if (mokvar_cur == NULL) {
0220         mokvar_next = efi_mokvar_table_va;
0221     } else {
0222         if (mokvar_cur->name[0] == '\0')
0223             return NULL;
0224         size_cur = sizeof(*mokvar_cur) + mokvar_cur->data_size;
0225         mokvar_next = (void *)mokvar_cur + size_cur;
0226     }
0227 
0228     if (mokvar_next->name[0] == '\0')
0229         return NULL;
0230 
0231     *mokvar_entry = mokvar_next;
0232     return mokvar_next;
0233 }
0234 
0235 /*
0236  * efi_mokvar_entry_find() - Find EFI MOK config entry by name
0237  *
0238  * name:    Name of the entry to look for.
0239  *
0240  * Returns: Pointer to EFI MOK config table entry if found;
0241  *      null otherwise.
0242  *
0243  * This routine depends on the EFI MOK config table being entirely
0244  * mapped with it's starting virtual address in efi_mokvar_table_va.
0245  */
0246 struct efi_mokvar_table_entry *efi_mokvar_entry_find(const char *name)
0247 {
0248     struct efi_mokvar_table_entry *mokvar_entry = NULL;
0249 
0250     while (efi_mokvar_entry_next(&mokvar_entry)) {
0251         if (!strncmp(name, mokvar_entry->name,
0252                  sizeof(mokvar_entry->name)))
0253             return mokvar_entry;
0254     }
0255     return NULL;
0256 }
0257 
0258 /*
0259  * efi_mokvar_sysfs_read() - sysfs binary file read routine
0260  *
0261  * Returns: Count of bytes read.
0262  *
0263  * Copy EFI MOK config table entry data for this mokvar sysfs binary file
0264  * to the supplied buffer, starting at the specified offset into mokvar table
0265  * entry data, for the specified count bytes. The copy is limited by the
0266  * amount of data in this mokvar config table entry.
0267  */
0268 static ssize_t efi_mokvar_sysfs_read(struct file *file, struct kobject *kobj,
0269                  struct bin_attribute *bin_attr, char *buf,
0270                  loff_t off, size_t count)
0271 {
0272     struct efi_mokvar_table_entry *mokvar_entry = bin_attr->private;
0273 
0274     if (!capable(CAP_SYS_ADMIN))
0275         return 0;
0276 
0277     if (off >= mokvar_entry->data_size)
0278         return 0;
0279     if (count >  mokvar_entry->data_size - off)
0280         count = mokvar_entry->data_size - off;
0281 
0282     memcpy(buf, mokvar_entry->data + off, count);
0283     return count;
0284 }
0285 
0286 /*
0287  * efi_mokvar_sysfs_init() - Map EFI MOK config table and create sysfs
0288  *
0289  * Map the EFI MOK variable config table for run-time use by the kernel
0290  * and create the sysfs entries in /sys/firmware/efi/mok-variables/
0291  *
0292  * This routine just returns if a valid EFI MOK variable config table
0293  * was not found earlier during boot.
0294  *
0295  * This routine must be called during a "middle" initcall phase, i.e.
0296  * after efi_mokvar_table_init() but before UEFI certs are loaded
0297  * during late init.
0298  *
0299  * Implicit inputs:
0300  * efi.mokvar_table:    Physical address of EFI MOK variable config table
0301  *          or special value that indicates no such table.
0302  *
0303  * efi_mokvar_table_size: Computed size of EFI MOK variable config table.
0304  *          The table is considered present and valid if this
0305  *          is non-zero.
0306  *
0307  * Implicit outputs:
0308  * efi_mokvar_table_va: Start virtual address of the EFI MOK config table.
0309  */
0310 static int __init efi_mokvar_sysfs_init(void)
0311 {
0312     void *config_va;
0313     struct efi_mokvar_table_entry *mokvar_entry = NULL;
0314     struct efi_mokvar_sysfs_attr *mokvar_sysfs = NULL;
0315     int err = 0;
0316 
0317     if (efi_mokvar_table_size == 0)
0318         return -ENOENT;
0319 
0320     config_va = memremap(efi.mokvar_table, efi_mokvar_table_size,
0321                  MEMREMAP_WB);
0322     if (!config_va) {
0323         pr_err("Failed to map EFI MOKvar config table\n");
0324         return -ENOMEM;
0325     }
0326     efi_mokvar_table_va = config_va;
0327 
0328     mokvar_kobj = kobject_create_and_add("mok-variables", efi_kobj);
0329     if (!mokvar_kobj) {
0330         pr_err("Failed to create EFI mok-variables sysfs entry\n");
0331         return -ENOMEM;
0332     }
0333 
0334     while (efi_mokvar_entry_next(&mokvar_entry)) {
0335         mokvar_sysfs = kzalloc(sizeof(*mokvar_sysfs), GFP_KERNEL);
0336         if (!mokvar_sysfs) {
0337             err = -ENOMEM;
0338             break;
0339         }
0340 
0341         sysfs_bin_attr_init(&mokvar_sysfs->bin_attr);
0342         mokvar_sysfs->bin_attr.private = mokvar_entry;
0343         mokvar_sysfs->bin_attr.attr.name = mokvar_entry->name;
0344         mokvar_sysfs->bin_attr.attr.mode = 0400;
0345         mokvar_sysfs->bin_attr.size = mokvar_entry->data_size;
0346         mokvar_sysfs->bin_attr.read = efi_mokvar_sysfs_read;
0347 
0348         err = sysfs_create_bin_file(mokvar_kobj,
0349                        &mokvar_sysfs->bin_attr);
0350         if (err)
0351             break;
0352 
0353         list_add_tail(&mokvar_sysfs->node, &efi_mokvar_sysfs_list);
0354     }
0355 
0356     if (err) {
0357         pr_err("Failed to create some EFI mok-variables sysfs entries\n");
0358         kfree(mokvar_sysfs);
0359     }
0360     return err;
0361 }
0362 fs_initcall(efi_mokvar_sysfs_init);