platform/x86: think-lmi: Multi-certificate support

Lenovo are adding support for both Admin and System certificates to
the certificate based authentication feature

This commit adds the support for this.

Signed-off-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Link: https://lore.kernel.org/r/20241024195536.6992-4-mpearson-lenovo@squebb.ca
[ij: Added #include <linux/array_size.h> + comment grammar fix]
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
This commit is contained in:
Mark Pearson authored and Ilpo Järvinen committed 2024-10-29 14:00:14 +02:00
1 parent 7c0bbf1ae6
commit 5dcb5ef125
3 files changed
+95 -26

No files matched your search

@@ -303,6 +303,7 @@ Description:
being configured allowing anyone to make changes.
After any of these operations the system must reboot for the changes to
take effect.
Admin and System certificates are supported from 2025 systems onward.
certificate_thumbprint:
Read only attribute used to display the MD5, SHA1 and SHA256 thumbprints
+90 -26
View File
@@ -12,6 +12,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>
#include <linux/array_size.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/mutex.h>
@@ -169,11 +170,12 @@ MODULE_PARM_DESC(debug_support, "Enable debug command support");
*/
#define LENOVO_CERT_THUMBPRINT_GUID "C59119ED-1C0D-4806-A8E9-59AA318176C4"
#define TLMI_POP_PWD BIT(0) /* Supervisor */
#define TLMI_PAP_PWD BIT(1) /* Power-on */
#define TLMI_HDD_PWD BIT(2) /* HDD/NVME */
#define TLMI_SMP_PWD BIT(6) /* System Management */
#define TLMI_CERT BIT(7) /* Certificate Based */
#define TLMI_POP_PWD BIT(0) /* Supervisor */
#define TLMI_PAP_PWD BIT(1) /* Power-on */
#define TLMI_HDD_PWD BIT(2) /* HDD/NVME */
#define TLMI_SMP_PWD BIT(6) /* System Management */
#define TLMI_CERT_SVC BIT(7) /* Admin Certificate Based */
#define TLMI_CERT_SMC BIT(8) /* System Certificate Based */
static const struct tlmi_err_codes tlmi_errs[] = {
{"Success", 0},
@@ -653,6 +655,17 @@ static ssize_t level_store(struct kobject *kobj,
static struct kobj_attribute auth_level = __ATTR_RW(level);
static char *cert_command(struct tlmi_pwd_setting *setting, const char *arg1, const char *arg2)
{
/* Prepend with SVC or SMC if multicert supported */
if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT)
return kasprintf(GFP_KERNEL, "%s,%s,%s",
setting == tlmi_priv.pwd_admin ? "SVC" : "SMC",
arg1, arg2);
else
return kasprintf(GFP_KERNEL, "%s,%s", arg1, arg2);
}
static ssize_t cert_thumbprint(char *buf, const char *arg, int count)
{
const struct acpi_buffer input = { strlen(arg), (char *)arg };
@@ -678,18 +691,35 @@ static ssize_t cert_thumbprint(char *buf, const char *arg, int count)
return count;
}
static char *thumbtypes[] = {"Md5", "Sha1", "Sha256"};
static ssize_t certificate_thumbprint_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
unsigned int i;
int count = 0;
char *wmistr;
if (!tlmi_priv.certificate_support || !setting->cert_installed)
return -EOPNOTSUPP;
count += cert_thumbprint(buf, "Md5", count);
count += cert_thumbprint(buf, "Sha1", count);
count += cert_thumbprint(buf, "Sha256", count);
for (i = 0; i < ARRAY_SIZE(thumbtypes); i++) {
if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) {
/* Format: 'SVC | SMC, Thumbtype' */
wmistr = kasprintf(GFP_KERNEL, "%s,%s",
setting == tlmi_priv.pwd_admin ? "SVC" : "SMC",
thumbtypes[i]);
} else {
/* Format: 'Thumbtype' */
wmistr = kasprintf(GFP_KERNEL, "%s", thumbtypes[i]);
}
if (!wmistr)
return -ENOMEM;
count += cert_thumbprint(buf, wmistr, count);
kfree(wmistr);
}
return count;
}
@@ -721,7 +751,7 @@ static ssize_t cert_to_password_store(struct kobject *kobj,
return -ENOMEM;
/* Format: 'Password,Signature' */
auth_str = kasprintf(GFP_KERNEL, "%s,%s", passwd, setting->signature);
auth_str = cert_command(setting, passwd, setting->signature);
if (!auth_str) {
kfree_sensitive(passwd);
return -ENOMEM;
@@ -735,12 +765,19 @@ static ssize_t cert_to_password_store(struct kobject *kobj,
static struct kobj_attribute auth_cert_to_password = __ATTR_WO(cert_to_password);
enum cert_install_mode {
TLMI_CERT_INSTALL,
TLMI_CERT_UPDATE,
};
static ssize_t certificate_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf, size_t count)
{
struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
enum cert_install_mode install_mode = TLMI_CERT_INSTALL;
char *auth_str, *new_cert;
char *signature;
char *guid;
int ret;
@@ -757,9 +794,9 @@ static ssize_t certificate_store(struct kobject *kobj,
return -EACCES;
/* Format: 'serial#, signature' */
auth_str = kasprintf(GFP_KERNEL, "%s,%s",
dmi_get_system_info(DMI_PRODUCT_SERIAL),
setting->signature);
auth_str = cert_command(setting,
dmi_get_system_info(DMI_PRODUCT_SERIAL),
setting->signature);
if (!auth_str)
return -ENOMEM;
@@ -776,24 +813,44 @@ static ssize_t certificate_store(struct kobject *kobj,
if (setting->cert_installed) {
/* Certificate is installed so this is an update */
if (!setting->signature || !setting->signature[0]) {
install_mode = TLMI_CERT_UPDATE;
/* If admin account enabled - need to use its signature */
if (tlmi_priv.pwd_admin->pwd_enabled)
signature = tlmi_priv.pwd_admin->signature;
else
signature = setting->signature;
} else { /* Cert install */
/* Check if SMC and SVC already installed */
if ((setting == tlmi_priv.pwd_system) && tlmi_priv.pwd_admin->cert_installed) {
/* This gets treated as a cert update */
install_mode = TLMI_CERT_UPDATE;
signature = tlmi_priv.pwd_admin->signature;
} else { /* Regular cert install */
install_mode = TLMI_CERT_INSTALL;
signature = setting->signature;
}
}
if (install_mode == TLMI_CERT_UPDATE) {
/* This is a certificate update */
if (!signature || !signature[0]) {
kfree(new_cert);
return -EACCES;
}
guid = LENOVO_UPDATE_BIOS_CERT_GUID;
/* Format: 'Certificate,Signature' */
auth_str = kasprintf(GFP_KERNEL, "%s,%s",
new_cert, setting->signature);
auth_str = cert_command(setting, new_cert, signature);
} else {
/* This is a fresh install */
if (!setting->pwd_enabled || !setting->password[0]) {
/* To set admin cert, a password must be enabled */
if ((setting == tlmi_priv.pwd_admin) &&
(!setting->pwd_enabled || !setting->password[0])) {
kfree(new_cert);
return -EACCES;
}
guid = LENOVO_SET_BIOS_CERT_GUID;
/* Format: 'Certificate,Admin-password' */
auth_str = kasprintf(GFP_KERNEL, "%s,%s",
new_cert, setting->password);
/* Format: 'Certificate, password' */
auth_str = cert_command(setting, new_cert, setting->password);
}
kfree(new_cert);
if (!auth_str)
@@ -873,14 +930,19 @@ static umode_t auth_attr_is_visible(struct kobject *kobj,
return 0;
}
/* We only display certificates on Admin account, if supported */
/* We only display certificates, if supported */
if (attr == &auth_certificate.attr ||
attr == &auth_signature.attr ||
attr == &auth_save_signature.attr ||
attr == &auth_cert_thumb.attr ||
attr == &auth_cert_to_password.attr) {
if ((setting == tlmi_priv.pwd_admin) && tlmi_priv.certificate_support)
return attr->mode;
if (tlmi_priv.certificate_support) {
if (setting == tlmi_priv.pwd_admin)
return attr->mode;
if ((tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) &&
(setting == tlmi_priv.pwd_system))
return attr->mode;
}
return 0;
}
@@ -1700,10 +1762,12 @@ static int tlmi_analyze(void)
}
}
if (tlmi_priv.certificate_support &&
(tlmi_priv.pwdcfg.core.password_state & TLMI_CERT))
tlmi_priv.pwd_admin->cert_installed = true;
if (tlmi_priv.certificate_support) {
tlmi_priv.pwd_admin->cert_installed =
tlmi_priv.pwdcfg.core.password_state & TLMI_CERT_SVC;
tlmi_priv.pwd_system->cert_installed =
tlmi_priv.pwdcfg.core.password_state & TLMI_CERT_SMC;
}
return 0;
fail_clear_attr:
+4
View File
@@ -41,6 +41,10 @@ enum save_mode {
};
/* password configuration details */
#define TLMI_PWDCFG_MODE_LEGACY 0
#define TLMI_PWDCFG_MODE_PASSWORD 1
#define TLMI_PWDCFG_MODE_MULTICERT 3
struct tlmi_pwdcfg_core {
uint32_t password_mode;
uint32_t password_state;