~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/powerpc/platforms/pseries/plpks-secvar.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php