]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
platform/x86: think-lmi: Multi-certificate support
authorMark Pearson <mpearson-lenovo@squebb.ca>
Thu, 24 Oct 2024 19:55:24 +0000 (15:55 -0400)
committerIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Tue, 29 Oct 2024 12:00:14 +0000 (14:00 +0200)
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>
Documentation/ABI/testing/sysfs-class-firmware-attributes
drivers/platform/x86/think-lmi.c
drivers/platform/x86/think-lmi.h

index 1a8b59f5d6e3a173c4faecc8e9d719adb8d8cbff..2713efa509b465a39bf014180794bf487e5b42d6 100644 (file)
@@ -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
index c316b1b592d610af6a0fc7805a9cd1a3c064a473..38de0cb20d7785d4e3b490e07edaf4d02f8f3370 100644 (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:
index 4728f40143a3802d07ee967e382b3d917e9bcadd..f267d8b46957e6bdc75e534e5fd18c94e6379208 100644 (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;