1 // SPDX-License-Identifier: GPL-2.0-only 2 3 // Secure variable implementation using the PowerVM LPAR Platform KeyStore (PLPKS) 4 // 5 // Copyright 2022, 2023 IBM Corporation 6 // Authors: Russell Currey 7 // Andrew Donnellan 8 // Nayna Jain 9 10 #define pr_fmt(fmt) "secvar: "fmt 11 12 #include <linux/printk.h> 13 #include <linux/init.h> 14 #include <linux/types.h> 15 #include <linux/slab.h> 16 #include <linux/string.h> 17 #include <linux/kobject.h> 18 #include <linux/nls.h> 19 #include <asm/machdep.h> 20 #include <asm/secvar.h> 21 #include <asm/plpks.h> 22 23 // Config attributes for sysfs 24 #define PLPKS_CONFIG_ATTR(name, fmt, func) \ 25 static ssize_t name##_show(struct kobject *kobj, \ 26 struct kobj_attribute *attr, \ 27 char *buf) \ 28 { \ 29 return sysfs_emit(buf, fmt, func()); \ 30 } \ 31 static struct kobj_attribute attr_##name = __ATTR_RO(name) 32 33 PLPKS_CONFIG_ATTR(version, "%u\n", plpks_get_version); 34 PLPKS_CONFIG_ATTR(max_object_size, "%u\n", plpks_get_maxobjectsize); 35 PLPKS_CONFIG_ATTR(total_size, "%u\n", plpks_get_totalsize); 36 PLPKS_CONFIG_ATTR(used_space, "%u\n", plpks_get_usedspace); 37 PLPKS_CONFIG_ATTR(supported_policies, "%08x\n", plpks_get_supportedpolicies); 38 PLPKS_CONFIG_ATTR(signed_update_algorithms, "%016llx\n", plpks_get_signedupdatealgorithms); 39 40 static const struct attribute *config_attrs[] = { 41 &attr_version.attr, 42 &attr_max_object_size.attr, 43 &attr_total_size.attr, 44 &attr_used_space.attr, 45 &attr_supported_policies.attr, 46 &attr_signed_update_algorithms.attr, 47 NULL, 48 }; 49 50 static u32 get_policy(const char *name) 51 { 52 if ((strcmp(name, "db") == 0) || 53 (strcmp(name, "dbx") == 0) || 54 (strcmp(name, "grubdb") == 0) || 55 (strcmp(name, "grubdbx") == 0) || 56 (strcmp(name, "sbat") == 0)) 57 return (PLPKS_WORLDREADABLE | PLPKS_SIGNEDUPDATE); 58 else 59 return PLPKS_SIGNEDUPDATE; 60 } 61 62 static const char * const plpks_var_names[] = { 63 "PK", 64 "KEK", 65 "db", 66 "dbx", 67 "grubdb", 68 "grubdbx", 69 "sbat", 70 "moduledb", 71 "trustedcadb", 72 NULL, 73 }; 74 75 static int plpks_get_variable(const char *key, u64 key_len, u8 *data, 76 u64 *data_size) 77 { 78 struct plpks_var var = {0}; 79 int rc = 0; 80 81 // We subtract 1 from key_len because we don't need to include the 82 // null terminator at the end of the string 83 var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL); 84 if (!var.name) 85 return -ENOMEM; 86 rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name, 87 key_len - 1); 88 if (rc < 0) 89 goto err; 90 var.namelen = rc * 2; 91 92 var.os = PLPKS_VAR_LINUX; 93 if (data) { 94 var.data = data; 95 var.datalen = *data_size; 96 } 97 rc = plpks_read_os_var(&var); 98 99 if (rc) 100 goto err; 101 102 *data_size = var.datalen; 103 104 err: 105 kfree(var.name); 106 if (rc && rc != -ENOENT) { 107 pr_err("Failed to read variable '%s': %d\n", key, rc); 108 // Return -EIO since userspace probably doesn't care about the 109 // specific error 110 rc = -EIO; 111 } 112 return rc; 113 } 114 115 static int plpks_set_variable(const char *key, u64 key_len, u8 *data, 116 u64 data_size) 117 { 118 struct plpks_var var = {0}; 119 int rc = 0; 120 u64 flags; 121 122 // Secure variables need to be prefixed with 8 bytes of flags. 123 // We only want to perform the write if we have at least one byte of data. 124 if (data_size <= sizeof(flags)) 125 return -EINVAL; 126 127 // We subtract 1 from key_len because we don't need to include the 128 // null terminator at the end of the string 129 var.name = kcalloc(key_len - 1, sizeof(wchar_t), GFP_KERNEL); 130 if (!var.name) 131 return -ENOMEM; 132 rc = utf8s_to_utf16s(key, key_len - 1, UTF16_LITTLE_ENDIAN, (wchar_t *)var.name, 133 key_len - 1); 134 if (rc < 0) 135 goto err; 136 var.namelen = rc * 2; 137 138 // Flags are contained in the first 8 bytes of the buffer, and are always big-endian 139 flags = be64_to_cpup((__be64 *)data); 140 141 var.datalen = data_size - sizeof(flags); 142 var.data = data + sizeof(flags); 143 var.os = PLPKS_VAR_LINUX; 144 var.policy = get_policy(key); 145 146 // Unlike in the read case, the plpks error code can be useful to 147 // userspace on write, so we return it rather than just -EIO 148 rc = plpks_signed_update_var(&var, flags); 149 150 err: 151 kfree(var.name); 152 return rc; 153 } 154 155 // PLPKS dynamic secure boot doesn't give us a format string in the same way OPAL does. 156 // Instead, report the format using the SB_VERSION variable in the keystore. 157 // The string is made up by us, and takes the form "ibm,plpks-sb-v<n>" (or "ibm,plpks-sb-unknown" 158 // if the SB_VERSION variable doesn't exist). Hypervisor defines the SB_VERSION variable as a 159 // "1 byte unsigned integer value". 160 static ssize_t plpks_secvar_format(char *buf, size_t bufsize) 161 { 162 struct plpks_var var = {0}; 163 ssize_t ret; 164 u8 version; 165 166 var.component = NULL; 167 // Only the signed variables have null bytes in their names, this one doesn't 168 var.name = "SB_VERSION"; 169 var.namelen = strlen(var.name); 170 var.datalen = 1; 171 var.data = &version; 172 173 // Unlike the other vars, SB_VERSION is owned by firmware instead of the OS 174 ret = plpks_read_fw_var(&var); 175 if (ret) { 176 if (ret == -ENOENT) { 177 ret = snprintf(buf, bufsize, "ibm,plpks-sb-unknown"); 178 } else { 179 pr_err("Error %ld reading SB_VERSION from firmware\n", ret); 180 ret = -EIO; 181 } 182 goto err; 183 } 184 185 ret = snprintf(buf, bufsize, "ibm,plpks-sb-v%hhu", version); 186 err: 187 return ret; 188 } 189 190 static int plpks_max_size(u64 *max_size) 191 { 192 // The max object size reported by the hypervisor is accurate for the 193 // object itself, but we use the first 8 bytes of data on write as the 194 // signed update flags, so the max size a user can write is larger. 195 *max_size = (u64)plpks_get_maxobjectsize() + sizeof(u64); 196 197 return 0; 198 } 199 200 201 static const struct secvar_operations plpks_secvar_ops = { 202 .get = plpks_get_variable, 203 .set = plpks_set_variable, 204 .format = plpks_secvar_format, 205 .max_size = plpks_max_size, 206 .config_attrs = config_attrs, 207 .var_names = plpks_var_names, 208 }; 209 210 static int plpks_secvar_init(void) 211 { 212 if (!plpks_is_available()) 213 return -ENODEV; 214 215 return set_secvar_ops(&plpks_secvar_ops); 216 } 217 machine_device_initcall(pseries, plpks_secvar_init); 218
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.