[PATCH v4 2/4] powerpc: expose secure variables to userspace via sysfs

Oliver O'Halloran oohall at gmail.com
Tue Oct 15 19:34:12 AEDT 2019


On Tue, 2019-10-01 at 19:41 -0400, Nayna Jain wrote:
> PowerNV secure variables, which store the keys used for OS kernel
> verification, are managed by the firmware. These secure variables need to
> be accessed by the userspace for addition/deletion of the certificates.
> 
> This patch adds the sysfs interface to expose secure variables for PowerNV
> secureboot. The users shall use this interface for manipulating
> the keys stored in the secure variables.
> 
> Signed-off-by: Nayna Jain <nayna at linux.ibm.com>
> Reviewed-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
> ---
>  Documentation/ABI/testing/sysfs-secvar |  37 +++++
>  arch/powerpc/Kconfig                   |  10 ++
>  arch/powerpc/kernel/Makefile           |   1 +
>  arch/powerpc/kernel/secvar-sysfs.c     | 198 +++++++++++++++++++++++++
>  4 files changed, 246 insertions(+)
>  create mode 100644 Documentation/ABI/testing/sysfs-secvar
>  create mode 100644 arch/powerpc/kernel/secvar-sysfs.c
> 
> diff --git a/Documentation/ABI/testing/sysfs-secvar b/Documentation/ABI/testing/sysfs-secvar
> new file mode 100644
> index 000000000000..815bd8ec4d5e
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-secvar
> @@ -0,0 +1,37 @@
> +What:		/sys/firmware/secvar
> +Date:		August 2019
> +Contact:	Nayna Jain <nayna at linux.ibm.com>
> +Description:	This directory is created if the POWER firmware supports OS
> +		secureboot, thereby secure variables. It exposes interface
> +		for reading/writing the secure variables
> +
> +What:		/sys/firmware/secvar/vars
> +Date:		August 2019
> +Contact:	Nayna Jain <nayna at linux.ibm.com>
> +Description:	This directory lists all the secure variables that are supported
> +		by the firmware.
> +
> +What:		/sys/firmware/secvar/vars/<variable name>
> +Date:		August 2019
> +Contact:	Nayna Jain <nayna at linux.ibm.com>
> +Description:	Each secure variable is represented as a directory named as
> +		<variable_name>. The variable name is unique and is in ASCII
> +		representation. The data and size can be determined by reading
> +		their respective attribute files.
> +
> +What:		/sys/firmware/secvar/vars/<variable_name>/size
> +Date:		August 2019
> +Contact:	Nayna Jain <nayna at linux.ibm.com>
> +Description:	An integer representation of the size of the content of the
> +		variable. In other words, it represents the size of the data.
> +
> +What:		/sys/firmware/secvar/vars/<variable_name>/data
> +Date:		August 2019
> +Contact:	Nayna Jain <nayna at linux.ibm.com>
> +Description:	A read-only file containing the value of the variable
> +
> +What:		/sys/firmware/secvar/vars/<variable_name>/update
> +Date:		August 2019
> +Contact:	Nayna Jain <nayna at linux.ibm.com>
> +Description:	A write-only file that is used to submit the new value for the
> +		variable.

How are the update mechanism's weird requirements communicated to
userspace? The design you've got for the OPAL bits is that the update
requirements depends on the secvar backend, but I see nothing plumbing
through what the secvar backend actually is.

> diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
> index deb19ec6ba3d..89084e4e5054 100644
> --- a/arch/powerpc/Kconfig
> +++ b/arch/powerpc/Kconfig
> @@ -946,6 +946,16 @@ config PPC_SECURE_BOOT
>  	  to enable OS secure boot on systems that have firmware support for
>  	  it. If in doubt say N.
>  
> +config SECVAR_SYSFS
that should probably be PPC_SECVAR_SYSFS since it's PPC specific

> +	tristate "Enable sysfs interface for POWER secure variables"
> +	depends on PPC_SECURE_BOOT
> +	depends on SYSFS
> +	help
> +	  POWER secure variables are managed and controlled by firmware.
> +	  These variables are exposed to userspace via sysfs to enable
> +	  read/write operations on these variables. Say Y if you have
> +	  secure boot enabled and want to expose variables to userspace.
> +
>  endmenu
>  
>  config ISA_DMA_API
> diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
> index 3cf26427334f..116a3a5c0557 100644
> --- a/arch/powerpc/kernel/Makefile
> +++ b/arch/powerpc/kernel/Makefile
> @@ -162,6 +162,7 @@ obj-y				+= ucall.o
>  endif
>  
>  obj-$(CONFIG_PPC_SECURE_BOOT)	+= secure_boot.o ima_arch.o secvar-ops.o
> +obj-$(CONFIG_SECVAR_SYSFS)	+= secvar-sysfs.o
>  
>  # Disable GCOV, KCOV & sanitizers in odd or sensitive code
>  GCOV_PROFILE_prom_init.o := n


> diff --git a/arch/powerpc/kernel/secvar-sysfs.c b/arch/powerpc/kernel/secvar-sysfs.c
> new file mode 100644
> index 000000000000..87a7cea41523
> --- /dev/null
> +++ b/arch/powerpc/kernel/secvar-sysfs.c
> @@ -0,0 +1,198 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) 2019 IBM Corporation <nayna at linux.ibm.com>
> + *
> + * This code exposes secure variables to user via sysfs
> + */

Adding a pr_fmt for this file would be a good idea. There's a few
pr_err()s in here that would benefit from some context.

> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/compat.h>
> +#include <linux/string.h>
> +#include <asm/secvar.h>

> +/*
> + * Since firmware checks the maximum allowed size, currently, it is default to
> + * 0. In future, it will be read from the device tree.
> + */
> +#define VARIABLE_MAX_SIZE  0

I don't see why you aren't reading it from the DT now...

> +/* Approximate value */
> +#define NAME_MAX_SIZE	   1024

Approximate?

> +static struct kobject *secvar_kobj;
> +static struct kset *secvar_kset;
> +
> +static ssize_t size_show(struct kobject *kobj, struct kobj_attribute *attr,
> +			 char *buf)
> +{
> +	uint64_t dsize;
> +	int rc;
> +
> +	rc = secvar_ops->get(kobj->name, strlen(kobj->name) + 1, NULL, &dsize);
> +	if (rc) {
> +		pr_err("Error retrieving variable size %d\n", rc);
> +		return rc;
> +	}
> +
> +	rc = sprintf(buf, "%llu\n", dsize);
> +
> +	return rc;
> +}
> +
> +static ssize_t data_read(struct file *filep, struct kobject *kobj,
> +			 struct bin_attribute *attr, char *buf, loff_t off,
> +			 size_t count)
> +{
> +	uint64_t dsize;
> +	int rc;
> +	char *data;

Can you swap the declarations of rc and data.

We try to keep declarations in reverse christmas tree style in
arch/powerpc/. We're pretty bad at enforcing that, but there's no
reason to be gratuitiously different.

> +
> +	rc = secvar_ops->get(kobj->name, strlen(kobj->name) + 1, NULL, &dsize);
> +	if (rc) {
> +		pr_err("Error getting variable size %d\n", rc);
> +		return rc;
> +	}
> +	pr_debug("dsize is %llu\n", dsize);
> +
> +	data = kzalloc(dsize, GFP_KERNEL);
> +	if (!data)
> +		return -ENOMEM;
> +
> +	rc = secvar_ops->get(kobj->name, strlen(kobj->name)+1, data, &dsize);
> +	if (rc) {
> +		pr_err("Error getting variable %d\n", rc);
> +		goto data_fail;
> +	}
> +
> +	rc = memory_read_from_buffer(buf, count, &off, data, dsize);
> +
> +data_fail:
> +	kfree(data);
> +	return rc;
> +}
> +
> +static ssize_t update_write(struct file *filep, struct kobject *kobj,
> +			    struct bin_attribute *attr, char *buf, loff_t off,
> +			    size_t count)
> +{
> +	int rc;
> +
> +	pr_debug("count is %ld\n", count);
> +	rc = secvar_ops->set(kobj->name, strlen(kobj->name)+1, buf, count);
> +	if (rc) {
> +		pr_err("Error setting the variable %s\n", kobj->name);
> +		return rc;
> +	}
> +
> +	return count;
> +}
> +

> +static struct kobj_attribute size_attr = __ATTR_RO(size);
> +
> +static struct bin_attribute data_attr = __BIN_ATTR_RO(data, VARIABLE_MAX_SIZE);
> +
> +static struct bin_attribute update_attr = __BIN_ATTR_WO(update,
> +							VARIABLE_MAX_SIZE);

Isn't this going to be all wrong if VARIABLE_MAX_SIZE is ever non-zero?

> +
> +static struct bin_attribute  *secvar_bin_attrs[] = {
> +	&data_attr,
> +	&update_attr,
> +	NULL,
> +};
> +
> +static struct attribute *secvar_attrs[] = {
> +	&size_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group secvar_attr_group = {
> +	.attrs = secvar_attrs,
> +	.bin_attrs = secvar_bin_attrs,
> +};
> +__ATTRIBUTE_GROUPS(secvar_attr);
> +
> +static struct kobj_type secvar_ktype = {
> +	.sysfs_ops	= &kobj_sysfs_ops,
> +	.default_groups = secvar_attr_groups,
> +};
> +
> +static int secvar_sysfs_load(void)
> +{
> +	char *name;
> +	uint64_t namesize = 0;
> +	struct kobject *kobj;
> +	int rc;
> +
> +	name = kzalloc(NAME_MAX_SIZE, GFP_KERNEL);
> +	if (!name)
> +		return -ENOMEM;
> +
> +	do {
> +		rc = secvar_ops->get_next(name, &namesize, NAME_MAX_SIZE);
> +		if (rc) {
> +			if (rc != -ENOENT)
> +				pr_err("error getting secvar from firmware %d\n",
> +					rc);
> +			break;
> +		}
> +
> +		kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
> +		if (!kobj)
> +			return -ENOMEM;
> +
> +		kobject_init(kobj, &secvar_ktype);
> +
> +		rc = kobject_add(kobj, &secvar_kset->kobj, "%s", name);
> +		if (rc) {
> +			pr_warn("kobject_add error %d for attribute: %s\n", rc,
> +				name);
> +			kobject_put(kobj);
> +			kobj = NULL;
> +		}
> +
> +		if (kobj)
> +			kobject_uevent(kobj, KOBJ_ADD);
> +
> +	} while (!rc);
> +
> +	kfree(name);
> +	return rc;
> +}
> +
> +static int secvar_sysfs_init(void)
> +{
> +	if (!secvar_ops) {
> +		pr_warn("secvar: failed to retrieve secvar operations.\n");
> +		return -ENODEV;
> +	}
> +
> +	secvar_kobj = kobject_create_and_add("secvar", firmware_kobj);
> +	if (!secvar_kobj) {
> +		pr_err("secvar: Failed to create firmware kobj\n");
> +		return -ENOMEM;
> +	}
> +
> +	secvar_kset = kset_create_and_add("vars", NULL, secvar_kobj);
> +	if (!secvar_kset) {
> +		pr_err("secvar: sysfs kobject registration failed.\n");
> +		kobject_put(secvar_kobj);
> +		return -ENOMEM;
> +	}
> +
> +	secvar_sysfs_load();
> +
> +	return 0;
> +}
> +
> +static void secvar_sysfs_exit(void)
> +{
> +	kset_unregister(secvar_kset);
> +	kobject_put(secvar_kobj);
> +}
> +
> +module_init(secvar_sysfs_init);
> +module_exit(secvar_sysfs_exit);
> +
> +MODULE_AUTHOR("Nayna Jain <nayna at linux.ibm.com>");
> +MODULE_DESCRIPTION("sysfs interface to POWER secure variables");
> +MODULE_LICENSE("GPL");

Is there anything that'll force the module to be loaded at runtime?

If not it might be worth making this builtin and turning the OPAL API
bit into a platform device driver. We can instantiate a platform device
from the DT node during opal_init() and the modalias based module
loading should handle the rest for you.

I would like to get people using platform device drivers for random
OPAL provided stuff. All the ~artisinal~hand~crafted~ device-tree
parsing in the powernv platform is getting a bit ridiculous...

Oliver



More information about the Linuxppc-dev mailing list