diff mbox series

[RFC,v2,3/3] powerpc/pseries: expose authenticated variables stored in LPAR PKS

Message ID 20220622215648.96723-4-nayna@linux.ibm.com
State New
Headers show
Series powerpc/pseries: add support for local secure storage called Platform KeyStore(PKS) | expand

Commit Message

Nayna Jain June 22, 2022, 9:56 p.m. UTC
PowerVM Guest Secure boot feature need to expose firmware managed
secure variables for user management. These variables store keys for
grub/kernel verification and also corresponding denied list.

Expose these variables to the userpace via fwsecurityfs.

Example:

Example:

# cd /sys/firmware/security/secvars

# pwd
/sys/firmware/security/secvars

# cat /tmp/PK.bin > PK

# ls -l
total 0
-rw-r--r-- 1 root root 2497 Jun 22 08:34 PK

# hexdump -C PK
00000000  00 00 00 00 00 08 00 00  a1 59 c0 a5 e4 94 a7 4a  |.........Y.....J|
00000010  87 b5 ab 15 5c 2b f0 72  3f 03 00 00 00 00 00 00  |....\+.r?.......|
00000020  23 03 00 00 ca 18 1d 1c  01 7d eb 11 9a 71 08 94  |#........}...q..|
00000030  ef 31 fb e4 30 82 03 0f  30 82 01 f7 a0 03 02 01  |.1..0...0.......|
00000040  02 02 14 22 ab 18 2f d5  aa dd c5 ba 98 27 60 26  |..."../......'`&|
00000050  f1 63 89 54 4c 52 d9 30  0d 06 09 2a 86 48 86 f7  |.c.TLR.0...*.H..|
...

Signed-off-by: Nayna Jain <nayna@linux.ibm.com>
---
 arch/powerpc/platforms/pseries/Kconfig        |  17 ++
 arch/powerpc/platforms/pseries/plpks/Makefile |   2 +
 .../pseries/plpks/fwsecurityfs_arch.c         |  16 ++
 .../platforms/pseries/plpks/internal.h        |  18 ++
 .../powerpc/platforms/pseries/plpks/secvars.c | 239 ++++++++++++++++++
 include/linux/fwsecurityfs.h                  |   4 +
 6 files changed, 296 insertions(+)
 create mode 100644 arch/powerpc/platforms/pseries/plpks/fwsecurityfs_arch.c
 create mode 100644 arch/powerpc/platforms/pseries/plpks/internal.h
 create mode 100644 arch/powerpc/platforms/pseries/plpks/secvars.c

Comments

Randy Dunlap June 23, 2022, 2:36 a.m. UTC | #1
On 6/22/22 14:56, Nayna Jain wrote:
> diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
> index 6c1ca487103f..9c52095e20c4 100644
> --- a/arch/powerpc/platforms/pseries/Kconfig
> +++ b/arch/powerpc/platforms/pseries/Kconfig
> @@ -152,6 +152,23 @@ config PSERIES_PLPKS
>  	  config to enable operating system interface to hypervisor to
>  	  access this space.
>  
> +config PSERIES_FWSECURITYFS_ARCH
> +	depends on FWSECURITYFS
> +	bool "Support fwsecurityfs for pseries"
> +	help
> +	  Enable fwsecuirtyfs arch specific code. This would initialize

	         fwsecurityfs                   . This initializes

> +	  the firmware security filesystem with initial platform specific
> +	  structure.
> +
> +config PSERIES_PLPKS_SECVARS
> +	depends on PSERIES_PLPKS
> +	select PSERIES_FWSECURITYFS_ARCH
> +	tristate "Support for secvars"
> +	help
> +	  This interface exposes authenticated variables stored in the LPAR
> +	  Platform KeyStore using fwsecurityfs interface.
> +	  If you are unsure how to use it, say N.
diff mbox series

Patch

diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index 6c1ca487103f..9c52095e20c4 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -152,6 +152,23 @@  config PSERIES_PLPKS
 	  config to enable operating system interface to hypervisor to
 	  access this space.
 
+config PSERIES_FWSECURITYFS_ARCH
+	depends on FWSECURITYFS
+	bool "Support fwsecurityfs for pseries"
+	help
+	  Enable fwsecuirtyfs arch specific code. This would initialize
+	  the firmware security filesystem with initial platform specific
+	  structure.
+
+config PSERIES_PLPKS_SECVARS
+	depends on PSERIES_PLPKS
+	select PSERIES_FWSECURITYFS_ARCH
+	tristate "Support for secvars"
+	help
+	  This interface exposes authenticated variables stored in the LPAR
+	  Platform KeyStore using fwsecurityfs interface.
+	  If you are unsure how to use it, say N.
+
 config PAPR_SCM
 	depends on PPC_PSERIES && MEMORY_HOTPLUG && LIBNVDIMM
 	tristate "Support for the PAPR Storage Class Memory interface"
diff --git a/arch/powerpc/platforms/pseries/plpks/Makefile b/arch/powerpc/platforms/pseries/plpks/Makefile
index e651ace920db..ff3d4b4cd3d7 100644
--- a/arch/powerpc/platforms/pseries/plpks/Makefile
+++ b/arch/powerpc/platforms/pseries/plpks/Makefile
@@ -5,3 +5,5 @@ 
 #
 
 obj-$(CONFIG_PSERIES_PLPKS)  += plpks.o
+obj-$(CONFIG_PSERIES_FWSECURITYFS_ARCH) += fwsecurityfs_arch.o
+obj-$(CONFIG_PSERIES_PLPKS_SECVARS) += secvars.o
diff --git a/arch/powerpc/platforms/pseries/plpks/fwsecurityfs_arch.c b/arch/powerpc/platforms/pseries/plpks/fwsecurityfs_arch.c
new file mode 100644
index 000000000000..6ccdfe4000a6
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/plpks/fwsecurityfs_arch.c
@@ -0,0 +1,16 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * POWER LPAR Platform KeyStore (PLPKS)
+ * Copyright (C) 2022 IBM Corporation
+ * Author: Nayna Jain <nayna@linux.ibm.com>
+ *
+ */
+
+#include <linux/fwsecurityfs.h>
+
+#include "internal.h"
+
+int arch_fwsecurity_init(void)
+{
+	return plpks_secvars_init();
+}
diff --git a/arch/powerpc/platforms/pseries/plpks/internal.h b/arch/powerpc/platforms/pseries/plpks/internal.h
new file mode 100644
index 000000000000..6061ffd37677
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/plpks/internal.h
@@ -0,0 +1,18 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2022 IBM Corporation
+ * Author: Nayna Jain <nayna@linux.ibm.com>
+ *
+ */
+#ifndef PKS_FWSEC_INTERNAL
+#define PKS_FWSEC_INTERNAL
+
+#ifdef CONFIG_PSERIES_PLPKS_SECVARS
+int plpks_secvars_init(void);
+#else
+int plpks_secvars_init(void)
+{
+	return 0;
+}
+#endif
+#endif
diff --git a/arch/powerpc/platforms/pseries/plpks/secvars.c b/arch/powerpc/platforms/pseries/plpks/secvars.c
new file mode 100644
index 000000000000..8852cb8f2f3c
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/plpks/secvars.c
@@ -0,0 +1,239 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * POWER LPAR Platform KeyStore (PLPKS)
+ * Copyright (C) 2022 IBM Corporation
+ * Author: Nayna Jain <nayna@linux.ibm.com>
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/fs_context.h>
+#include <linux/mount.h>
+#include <linux/pagemap.h>
+#include <linux/init.h>
+#include <linux/namei.h>
+#include <linux/ctype.h>
+#include <linux/fwsecurityfs.h>
+#include <asm/plpks.h>
+
+#include "internal.h"
+
+static struct dentry *secvar_dir;
+
+static const char * const names[] = {
+	"PK",
+	"KEK",
+	"db",
+	"dbx",
+	"grubdb",
+	"sbat",
+	"moduledb",
+	"trustedcadb",
+	NULL
+};
+
+static int validate_name(char *name)
+{
+	int i = 0;
+
+	while (names[i] != NULL) {
+		if ((strlen(names[i]) == strlen(name))
+		&& (strncmp(name, names[i], strlen(names[i])) == 0))
+			return 0;
+		i++;
+	}
+	pr_err("Invalid name, allowed ones are (dbx,grubdb,sbat,moduledb,trustedcadb)\n");
+
+	return -EINVAL;
+}
+
+static u32 get_policy(char *name)
+{
+	if ((strncmp(name, "PK", 2) == 0)
+	|| (strncmp(name, "KEK", 3) == 0)
+	|| (strncmp(name, "db", 2) == 0)
+	|| (strncmp(name, "dbx", 3) == 0)
+	|| (strncmp(name, "grubdb", 6) == 0)
+	|| (strncmp(name, "sbat", 4) == 0))
+		return (WORLDREADABLE & SIGNEDUPDATE);
+	else
+		return SIGNEDUPDATE;
+}
+
+static ssize_t plpks_secvar_file_write(struct file *file,
+				     const char __user *userbuf,
+				     size_t count, loff_t *ppos)
+{
+	struct plpks_var var;
+	void *data;
+	struct inode *inode = file->f_mapping->host;
+	u16 datasize = count;
+	ssize_t bytes;
+
+	data = memdup_user(userbuf, datasize);
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	var.component = NULL;
+	var.name = file->f_path.dentry->d_iname;
+	var.namelen = strlen(var.name);
+	var.policy = get_policy(var.name);
+	var.datalen = datasize;
+	var.data = data;
+	bytes = plpks_signed_update_var(var);
+
+	if (bytes) {
+		pr_err("Update of the variable failed with error %ld\n", bytes);
+		goto out;
+	}
+
+	inode_lock(inode);
+	i_size_write(inode, datasize);
+	inode->i_mtime = current_time(inode);
+	inode_unlock(inode);
+
+	bytes = count;
+out:
+	kfree(data);
+
+	return bytes;
+
+}
+
+static ssize_t plpks_secvar_file_read(struct file *file, char __user *userbuf,
+				    size_t count, loff_t *ppos)
+{
+	struct plpks_var var;
+	char *out;
+	u32 outlen;
+	int rc;
+	size_t size;
+
+	var.name = file->f_path.dentry->d_iname;
+	var.namelen = strlen(var.name);
+	var.component = NULL;
+	rc = plpks_read_os_var(&var);
+	if (rc) {
+		pr_err("Error reading object %d\n", rc);
+		return rc;
+	}
+
+	outlen = sizeof(var.policy) + var.datalen;
+	out = kzalloc(outlen, GFP_KERNEL);
+	memcpy(out, &var.policy, sizeof(var.policy));
+
+	memcpy(out + sizeof(var.policy), var.data, var.datalen);
+
+	size = simple_read_from_buffer(userbuf, count, ppos,
+				       out, outlen);
+	kfree(out);
+	return size;
+}
+
+
+static const struct file_operations plpks_secvar_file_operations = {
+	.open   = simple_open,
+	.read   = plpks_secvar_file_read,
+	.write  = plpks_secvar_file_write,
+	.llseek = no_llseek,
+};
+
+static int plpks_secvar_create(struct user_namespace *mnt_userns, struct inode *dir,
+			     struct dentry *dentry, umode_t mode, bool excl)
+{
+	int namelen, i = 0;
+	char *varname;
+	int rc = 0;
+
+	namelen = dentry->d_name.len;
+
+	varname = kzalloc(namelen + 1, GFP_KERNEL);
+	if (!varname)
+		return -ENOMEM;
+
+	for (i = 0; i < namelen; i++)
+		varname[i] = dentry->d_name.name[i];
+	varname[i] = '\0';
+
+	rc = validate_name(varname);
+	if (rc)
+		goto out;
+
+	rc = validate_name(varname);
+	if (rc)
+		goto out;
+
+	rc = fwsecurityfs_create_file(varname, S_IFREG|0644, 0, secvar_dir,
+				      dentry, &plpks_secvar_file_operations);
+	if (rc)
+		pr_err("Error creating file\n");
+
+out:
+	kfree(varname);
+
+	return rc;
+}
+
+static const struct inode_operations plpks_secvar_dir_inode_operations = {
+	.lookup = simple_lookup,
+	.create = plpks_secvar_create,
+};
+
+static int plpks_fill_secvars(struct super_block *sb)
+{
+	struct plpks_var *var = NULL;
+	int err;
+	int i = 0;
+
+	while (names[i] != NULL) {
+		var = kzalloc(sizeof(struct plpks_var), GFP_KERNEL);
+		var->name = (char *)names[i];
+		var->namelen = strlen(names[i]);
+		pr_debug("name is %s\n", var->name);
+		var->component = NULL;
+		i++;
+		err = plpks_read_os_var(var);
+		if (err) {
+			kfree(var);
+			continue;
+		}
+
+		err = fwsecurityfs_create_file(var->name, S_IFREG|0644,
+					       var->datalen, secvar_dir,
+					       NULL,
+					       &plpks_secvar_file_operations);
+
+		kfree(var);
+		if (err) {
+			pr_err("Error creating file\n");
+			break;
+		}
+	}
+	return  err;
+};
+
+int plpks_secvars_init(void)
+{
+	int error;
+
+	struct super_block *sb;
+
+	secvar_dir = fwsecurityfs_create_dir("secvars", S_IFDIR | 0755, NULL,
+					     &plpks_secvar_dir_inode_operations);
+	if (IS_ERR(secvar_dir)) {
+		int ret = PTR_ERR(secvar_dir);
+
+		if (ret != -ENODEV)
+			pr_err("Unable to create integrity sysfs dir: %d\n",
+					ret);
+		secvar_dir = NULL;
+		return ret;
+	}
+
+	sb = fwsecurityfs_get_superblock();
+	error = plpks_fill_secvars(sb);
+	if (error)
+		pr_err("Filling secvars failed\n");
+
+	return 0;
+};
diff --git a/include/linux/fwsecurityfs.h b/include/linux/fwsecurityfs.h
index c079ce939f42..d5fd51aa6322 100644
--- a/include/linux/fwsecurityfs.h
+++ b/include/linux/fwsecurityfs.h
@@ -21,9 +21,13 @@  struct dentry *fwsecurityfs_create_dir(const char *name, umode_t mode,
 				       const struct inode_operations *iops);
 int fwsecurityfs_remove_dir(struct dentry *dentry);
 
+#ifdef CONFIG_PSERIES_FWSECURITYFS_ARCH
+int arch_fwsecurity_init(void);
+#else
 static int arch_fwsecurity_init(void)
 {
 	return 0;
 }
+#endif
 
 #endif /* _FWSECURITYFS_H_ */