// 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); }