Many HP Commercial PC’s include a feature called Secure Platform Management (SPM), which replaces older password-based BIOS settings management with public key cryptography. PC secure product management begins when a target system is provisioned with cryptographic keys that are used to ensure the integrity of communications between system management utilities and the BIOS. The private key is used by system management utilities to sign payloads containing configuration changes. The BIOS on a target system uses the associated public key to verify the integrity of the payload and apply the changes. At the end of the PC’s lifecycle a signed deprovisioning command restores the factory default state. KEK Certificate (KEK) and Signing Key (SK) get provisioned, and status can be read either as text from the status file or binary from statusbin. /sys/devices/platform/hp-wmi/spm/kek /sys/devices/platform/hp-wmi/spm/sk /sys/devices/platform/hp-wmi/spm/status /sys/devices/platform/hp-wmi/spm/statusbin 'kek' is a write-only file that can be used to configure the RSA public key that will be used by the BIOS to verify signatures when setting the signing key. When written, the bytes should correspond to the KEK certificate (x509 .DER format containing an OU). The size of the certificate must be less than or equal to 4095 bytes. 'sk' is a write-only file that can be used to configure the RSA public key that will be used by the BIOS to verify signatures when configuring BIOS settings and security features. When written, the bytes should correspond to the modulus of the public key. This feature requires "Update hp_wmi_group to simplify feature addition" patch. All changes were validated on a HP ZBook Workstation, HP EliteBook x360, and HP EliteBook 850 G8 notebooks. Signed-off-by: Jorge Lopez <jorge.lopez2@xxxxxx> --- Based on the latest platform-drivers-x86.git/for-next --- drivers/platform/x86/hp-wmi.c | 195 ++++++++++++++++++++++++++++++++++ 1 file changed, 195 insertions(+) diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 0c7d863b8aab..139dc079c1fa 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -120,6 +120,12 @@ enum hp_wmi_commandtype { HPWMI_THERMAL_PROFILE_QUERY = 0x4c, }; +enum hp_wmi_spm_commandtype { + HPWMI_SECUREPLATFORM_GET_STATE = 0x10, + HPWMI_SECUREPLATFORM_SET_KEK = 0x11, + HPWMI_SECUREPLATFORM_SET_SK = 0x12, +}; + enum hp_wmi_gm_commandtype { HPWMI_FAN_SPEED_GET_QUERY = 0x11, HPWMI_SET_PERFORMANCE_MODE = 0x1A, @@ -133,6 +139,7 @@ enum hp_wmi_command { HPWMI_WRITE = 0x02, HPWMI_ODM = 0x03, HPWMI_GM = 0x20008, + HPWMI_SECUREPLATFORM = 0x20010, }; enum hp_wmi_hardware_mask { @@ -193,6 +200,20 @@ struct bios_rfkill2_device_state { u8 unknown[4]; }; +#pragma pack(1) +struct secureplatform_provisioning_data { + u8 state; + u8 version[2]; + u8 reserved1; + u32 features; + u32 nonce; + u8 reserved2[28]; + u8 sk_mod[256]; + u8 kek_mod[256]; +}; + +#pragma pack() + /* 7 devices fit into the 128 byte buffer */ #define HPWMI_MAX_RFKILL2_DEVICES 7 @@ -724,6 +745,179 @@ static ssize_t postcode_store(struct device *dev, struct device_attribute *attr, return count; } +/* Secure Platform Management (SPM) */ + +/* + * spm_statusbin_show - Reports SPM status in binary format + * + * @kobj: Pointer to a kernel object of things that show up as + * directory in the sysfs filesystem. + * @attr: Pointer to list of attributes for the operation + * @buf: Pointer to buffer + * + * Returns number of bytes read on success. Otherwise, + * an HP WMI query specific error code (which is positive) + * -ENODEV if the query was not successful at all + * + */ +static ssize_t spm_statusbin_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + int ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_GET_STATE, + HPWMI_SECUREPLATFORM, buf, 0, + sizeof(struct secureplatform_provisioning_data)); + + return ret ? -ENODEV : sizeof(struct secureplatform_provisioning_data); +} + +/* + * spm_status_show - Reads SPM status + * + * @kobj: Pointer to a kernel object of things that show up as + * directory in the sysfs filesystem. + * @attr: Pointer to list of attributes for the operation + * @buf: Pointer to buffer + * + * Returns number of bytes read on success. Otherwise, + * an HP WMI query specific error code (which is positive) + * -ENODEV if the query was not successful at all + * -ENOMEM if cannot allocate required memory size + * + */ +static ssize_t spm_status_show(struct kobject *kobj, struct kobj_attribute + *attr, char *buf) +{ + int ret, i; + struct secureplatform_provisioning_data *data = NULL; + + data = kmalloc(sizeof(struct secureplatform_provisioning_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = spm_statusbin_show(kobj, attr, (char *)data); + if (ret < 0) { + kfree(data); + return ret; + } + + snprintf(buf, PAGE_SIZE, "%sState: %d\n", buf, data->state); + snprintf(buf, PAGE_SIZE, "%sVersion: %d.%d\n", buf, data->version[0], data->version[1]); + + /* state == 0 means secureplatform feature is not configured in BIOS. */ + if (data->state == 0) + goto status_show_exit; + + snprintf(buf, PAGE_SIZE, "%sNonce: %d\n", buf, data->nonce); + snprintf(buf, PAGE_SIZE, "%sFeaturesInUse: %d\n", buf, data->features); + snprintf(buf, PAGE_SIZE, "%sEndorsementKeyMod: {", buf); + + for (i = 255; i >= 0; i--) + snprintf(buf, PAGE_SIZE, "%s %u", buf, data->kek_mod[i]); + + snprintf(buf, PAGE_SIZE, "%s }\n", buf); + snprintf(buf, PAGE_SIZE, "%sSigningKeyMod: {", buf); + + for (i = 255; i >= 0; i--) + snprintf(buf, PAGE_SIZE, "%s %u", buf, data->sk_mod[i]); + snprintf(buf, PAGE_SIZE, "%s }\n", buf); + +status_show_exit: + kfree(data); + return strnlen(buf, PAGE_SIZE); +} + +/* + * spm_kek_store: + * + * Function used to configure the RSA public key that will be used by + * the BIOS to verify signatures when setting the signing key. When + * written, the bytes should correspond to the KEK certificate (x509 + * .DER format containing an OU). The size of the certificate must be + * less than or equal to 4095 bytes. + * + * @kobj: Pointer to a kernel object of things that show up as + * directory in the sysfs filesystem. + * @attr: Pointer to list of attributes for the operation + * @buf: Pointer to buffer + * @count: buffer size in bytes + * + * Returns number of bytes written on success. Otherwise + * an HP WMI query specific error code (which is positive) + * -EINVAL if the query was not successful at all + * + */ +static ssize_t spm_kek_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_SET_KEK, + HPWMI_SECUREPLATFORM, (void *)buf, count, 0); + return ret ? -EINVAL : count; +} + +/* + * spm_sk_store: + * + * Function used to configure the RSA public key that will be used by the + * BIOS to verify signatures when configuring BIOS settings and security + * features. When written, the bytes should correspond to the modulus of + * the public key. The exponent is assumed to be 0x10001. + * + * @kobj: Pointer to a kernel object of things that show up as + * directory in the sysfs filesystem. + * @attr: Pointer to list of attributes for the operation + * @buf: Pointer to buffer + * @count: buffer size in bytes + * + * Returns number of bytes written on success. Otherwise + * an HP WMI query specific error code (which is positive) + * -EINVAL if the query was not successful at all + * + */ +static ssize_t spm_sk_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t count) +{ + int ret = hp_wmi_perform_query(HPWMI_SECUREPLATFORM_SET_SK, + HPWMI_SECUREPLATFORM, (void *)buf, count, 0); + return ret ? -EINVAL : count; +} + + +#define HPWMI_ATTR_RO(_group, _name) \ +static struct kobj_attribute _group##_##_name = \ +__ATTR(_name, 0444, _group##_##_name##_show, NULL) + +#define HPWMI_BINATTR_RO(_group, _name, _size) \ +static struct bin_attribute _group##_##_name = \ +__BIN_ATTR(_name, 0444, _group##_##_name##_read, NULL, _size) + +#define HPWMI_BINATTR_RW(_group, _name, _size) \ +static struct bin_attribute _group##_##_name = \ +__BIN_ATTR(_name, 0444 | 0200, _group##_##_name##_read, _group##_##_name##_write, _size) + +#define HPWMI_ATTR_WO(_group, _name, _command) \ +static struct kobj_attribute _group##_##_name = \ +__ATTR(_name, 0400 | 0200, NULL, _group##_##_name##_store) + +HPWMI_ATTR_RO(spm, status); +HPWMI_ATTR_RO(spm, statusbin); +HPWMI_ATTR_WO(spm, kek, HPWMI_SECUREPLATFORM_SET_KEK); +HPWMI_ATTR_WO(spm, sk, HPWMI_SECUREPLATFORM_SET_SK); + +static struct attribute *spm_attrs[] = { + &spm_status.attr, + &spm_statusbin.attr, + &spm_kek.attr, + &spm_sk.attr, + NULL, +}; + +struct kobject *spm_kobj; + +static const struct attribute_group spm_group = { + .name = "spm", + .attrs = spm_attrs, +}; + static DEVICE_ATTR_RO(display); static DEVICE_ATTR_RO(hddtemp); static DEVICE_ATTR_RW(als); @@ -747,6 +941,7 @@ static const struct attribute_group hp_wmi_group = { static const struct attribute_group *hp_wmi_groups[] = { &hp_wmi_group, + &spm_group, NULL, }; -- 2.25.1