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