diff options
Diffstat (limited to 'drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c')
| -rw-r--r-- | drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c | 381 | 
1 files changed, 381 insertions, 0 deletions
| diff --git a/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c b/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c new file mode 100644 index 000000000000..86f90238750c --- /dev/null +++ b/drivers/platform/x86/hp/hp-bioscfg/spmobj-attributes.c @@ -0,0 +1,381 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Functions corresponding to secure platform management object type + * attributes under BIOS PASSWORD for use with hp-bioscfg driver + * + * Copyright (c) 2022 HP Development Company, L.P. + */ + +#include "bioscfg.h" + +static const char * const spm_state_types[] = { +	"not provisioned", +	"provisioned", +	"provisioning in progress", +}; + +static const char * const spm_mechanism_types[] = { +	"not provisioned", +	"signing-key", +	"endorsement-key", +}; + +struct secureplatform_provisioning_data { +	u8 state; +	u8 version[2]; +	u8 reserved1; +	u32 features; +	u32 nonce; +	u8 reserved2[28]; +	u8 sk_mod[MAX_KEY_MOD_SIZE]; +	u8 kek_mod[MAX_KEY_MOD_SIZE]; +}; + +/** + * hp_calculate_security_buffer() - determines size of security buffer + * for authentication scheme + * + * @authentication: the authentication content + * + * Currently only supported type is Admin password + */ +size_t hp_calculate_security_buffer(const char *authentication) +{ +	size_t size, authlen; + +	if (!authentication) +		return sizeof(u16) * 2; + +	authlen = strlen(authentication); +	if (!authlen) +		return sizeof(u16) * 2; + +	size = sizeof(u16) + authlen * sizeof(u16); +	if (!strstarts(authentication, BEAM_PREFIX)) +		size += strlen(UTF_PREFIX) * sizeof(u16); + +	return size; +} + +/** + * hp_populate_security_buffer() - builds a security buffer for + * authentication scheme + * + * @authbuf: the security buffer + * @authentication: the authentication content + * + * Currently only supported type is PLAIN TEXT + */ +int hp_populate_security_buffer(u16 *authbuf, const char *authentication) +{ +	u16 *auth = authbuf; +	char *strprefix = NULL; +	int ret = 0; + +	if (strstarts(authentication, BEAM_PREFIX)) { +		/* +		 * BEAM_PREFIX is append to authbuf when a signature +		 * is provided and Sure Admin is enabled in BIOS +		 */ +		/* BEAM_PREFIX found, convert part to unicode */ +		auth = hp_ascii_to_utf16_unicode(auth, authentication); +		if (!auth) +			return -EINVAL; + +	} else { +		/* +		 * UTF-16 prefix is append to the * authbuf when a BIOS +		 * admin password is configured in BIOS +		 */ + +		/* append UTF_PREFIX to part and then convert it to unicode */ +		strprefix = kasprintf(GFP_KERNEL, "%s%s", UTF_PREFIX, +				      authentication); +		if (!strprefix) +			return -ENOMEM; + +		auth = hp_ascii_to_utf16_unicode(auth, strprefix); +		kfree(strprefix); + +		if (!auth) { +			ret = -EINVAL; +			goto out_buffer; +		} +	} + +out_buffer: +	return ret; +} + +static ssize_t update_spm_state(void) +{ +	struct secureplatform_provisioning_data data; +	int ret; + +	ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_GET_STATE, +				   HPWMI_SECUREPLATFORM, &data, 0, +				   sizeof(data)); +	if (ret < 0) +		return ret; + +	bioscfg_drv.spm_data.mechanism = data.state; +	if (bioscfg_drv.spm_data.mechanism) +		bioscfg_drv.spm_data.is_enabled = 1; + +	return 0; +} + +static ssize_t statusbin(struct kobject *kobj, +			 struct kobj_attribute *attr, +			 struct secureplatform_provisioning_data *buf) +{ +	int ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_GET_STATE, +				       HPWMI_SECUREPLATFORM, buf, 0, +				       sizeof(*buf)); + +	if (ret < 0) +		return ret; + +	return sizeof(struct secureplatform_provisioning_data); +} + +/* + * status_show - Reads SPM status + */ +static ssize_t status_show(struct kobject *kobj, struct kobj_attribute +			   *attr, char *buf) +{ +	int ret, i; +	int len = 0; +	struct secureplatform_provisioning_data data; + +	ret = statusbin(kobj, attr, &data); +	if (ret < 0) +		return ret; + +	/* +	 * 'status' is a read-only file that returns ASCII text in +	 * JSON format reporting the status information. +	 * +	 * "State": "not provisioned | provisioned | provisioning in progress ", +	 * "Version": " Major. Minor ", +	 * "Nonce": <16-bit unsigned number display in base 10>, +	 * "FeaturesInUse": <16-bit unsigned number display in base 10>, +	 * "EndorsementKeyMod": "<256 bytes in base64>", +	 * "SigningKeyMod": "<256 bytes in base64>" +	 */ + +	len += sysfs_emit_at(buf, len, "{\n"); +	len += sysfs_emit_at(buf, len, "\t\"State\": \"%s\",\n", +			     spm_state_types[data.state]); +	len += sysfs_emit_at(buf, len, "\t\"Version\": \"%d.%d\"", +			     data.version[0], data.version[1]); + +	/* +	 * state == 0 means secure platform management +	 * feature is not configured in BIOS. +	 */ +	if (data.state == 0) { +		len += sysfs_emit_at(buf, len, "\n"); +		goto status_exit; +	} else { +		len += sysfs_emit_at(buf, len, ",\n"); +	} + +	len += sysfs_emit_at(buf, len, "\t\"Nonce\": %d,\n", data.nonce); +	len += sysfs_emit_at(buf, len, "\t\"FeaturesInUse\": %d,\n", data.features); +	len += sysfs_emit_at(buf, len, "\t\"EndorsementKeyMod\": \""); + +	for (i = 255; i >= 0; i--) +		len += sysfs_emit_at(buf, len, " %u", data.kek_mod[i]); + +	len += sysfs_emit_at(buf, len, " \",\n"); +	len += sysfs_emit_at(buf, len, "\t\"SigningKeyMod\": \""); + +	for (i = 255; i >= 0; i--) +		len += sysfs_emit_at(buf, len, " %u", data.sk_mod[i]); + +	/* Return buf contents */ +	len += sysfs_emit_at(buf, len, " \"\n"); + +status_exit: +	len += sysfs_emit_at(buf, len, "}\n"); + +	return len; +} + +static struct kobj_attribute password_spm_status = __ATTR_RO(status); + +ATTRIBUTE_SPM_N_PROPERTY_SHOW(is_enabled, spm); +static struct kobj_attribute password_spm_is_key_enabled = __ATTR_RO(is_enabled); + +static ssize_t key_mechanism_show(struct kobject *kobj, struct kobj_attribute *attr, +				  char *buf) +{ +	return sysfs_emit(buf, "%s\n", +			  spm_mechanism_types[bioscfg_drv.spm_data.mechanism]); +} + +static struct kobj_attribute password_spm_key_mechanism = __ATTR_RO(key_mechanism); + +static ssize_t sk_store(struct kobject *kobj, +			struct kobj_attribute *attr, +			const char *buf, size_t count) +{ +	int ret; +	int length; + +	length = count; +	if (buf[length - 1] == '\n') +		length--; + +	/* allocate space and copy current signing key */ +	bioscfg_drv.spm_data.signing_key = kmemdup(buf, length, GFP_KERNEL); +	if (!bioscfg_drv.spm_data.signing_key) +		return -ENOMEM; + +	/* submit signing key payload */ +	ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_SET_SK, +				   HPWMI_SECUREPLATFORM, +				   (void *)bioscfg_drv.spm_data.signing_key, +				   count, 0); + +	if (!ret) { +		bioscfg_drv.spm_data.mechanism = SIGNING_KEY; +		hp_set_reboot_and_signal_event(); +	} + +	kfree(bioscfg_drv.spm_data.signing_key); +	bioscfg_drv.spm_data.signing_key = NULL; + +	return ret ? ret : count; +} + +static struct kobj_attribute password_spm_signing_key = __ATTR_WO(sk); + +static ssize_t kek_store(struct kobject *kobj, +			 struct kobj_attribute *attr, +			 const char *buf, size_t count) +{ +	int ret; +	int length; + +	length = count; +	if (buf[length - 1] == '\n') +		length--; + +	/* allocate space and copy current signing key */ +	bioscfg_drv.spm_data.endorsement_key = kmemdup(buf, length, GFP_KERNEL); +	if (!bioscfg_drv.spm_data.endorsement_key) { +		ret = -ENOMEM; +		goto exit_kek; +	} + +	ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_SET_KEK, +				   HPWMI_SECUREPLATFORM, +				   (void *)bioscfg_drv.spm_data.endorsement_key, +				   count, 0); + +	if (!ret) { +		bioscfg_drv.spm_data.mechanism = ENDORSEMENT_KEY; +		hp_set_reboot_and_signal_event(); +	} + +exit_kek: +	kfree(bioscfg_drv.spm_data.endorsement_key); +	bioscfg_drv.spm_data.endorsement_key = NULL; + +	return ret ? ret : count; +} + +static struct kobj_attribute password_spm_endorsement_key = __ATTR_WO(kek); + +static ssize_t role_show(struct kobject *kobj, struct kobj_attribute *attr, +			 char *buf) +{ +	return sysfs_emit(buf, "%s\n", BIOS_SPM); +} + +static struct kobj_attribute password_spm_role = __ATTR_RO(role); + +static ssize_t auth_token_store(struct kobject *kobj, +				struct kobj_attribute *attr, +				const char *buf, size_t count) +{ +	int ret = 0; +	int length; + +	length = count; +	if (buf[length - 1] == '\n') +		length--; + +	/* allocate space and copy current auth token */ +	bioscfg_drv.spm_data.auth_token = kmemdup(buf, length, GFP_KERNEL); +	if (!bioscfg_drv.spm_data.auth_token) { +		ret = -ENOMEM; +		goto exit_token; +	} + +	return count; + +exit_token: +	kfree(bioscfg_drv.spm_data.auth_token); +	bioscfg_drv.spm_data.auth_token = NULL; + +	return ret; +} + +static struct kobj_attribute password_spm_auth_token = __ATTR_WO(auth_token); + +static struct attribute *secure_platform_attrs[] = { +	&password_spm_is_key_enabled.attr, +	&password_spm_signing_key.attr, +	&password_spm_endorsement_key.attr, +	&password_spm_key_mechanism.attr, +	&password_spm_status.attr, +	&password_spm_role.attr, +	&password_spm_auth_token.attr, +	NULL, +}; + +static const struct attribute_group secure_platform_attr_group = { +	.attrs = secure_platform_attrs, +}; + +void hp_exit_secure_platform_attributes(void) +{ +	/* remove secure platform sysfs entry and free key data*/ + +	kfree(bioscfg_drv.spm_data.endorsement_key); +	bioscfg_drv.spm_data.endorsement_key = NULL; + +	kfree(bioscfg_drv.spm_data.signing_key); +	bioscfg_drv.spm_data.signing_key = NULL; + +	kfree(bioscfg_drv.spm_data.auth_token); +	bioscfg_drv.spm_data.auth_token = NULL; + +	if (bioscfg_drv.spm_data.attr_name_kobj) +		sysfs_remove_group(bioscfg_drv.spm_data.attr_name_kobj, +				   &secure_platform_attr_group); +} + +int hp_populate_secure_platform_data(struct kobject *attr_name_kobj) +{ +	/* Populate data for Secure Platform Management */ +	bioscfg_drv.spm_data.attr_name_kobj = attr_name_kobj; + +	strscpy(bioscfg_drv.spm_data.attribute_name, SPM_STR, +		sizeof(bioscfg_drv.spm_data.attribute_name)); + +	bioscfg_drv.spm_data.is_enabled = 0; +	bioscfg_drv.spm_data.mechanism = 0; +	bioscfg_drv.pending_reboot = false; +	update_spm_state(); + +	bioscfg_drv.spm_data.endorsement_key = NULL; +	bioscfg_drv.spm_data.signing_key = NULL; +	bioscfg_drv.spm_data.auth_token = NULL; + +	return sysfs_create_group(attr_name_kobj, &secure_platform_attr_group); +} |