diff mbox series

[4/4] platform/x86: think-lmi: Multi-certificate support

Message ID 20241021193837.7641-4-mpearson-lenovo@squebb.ca (mailing list archive)
State Changes Requested, archived
Headers show
Series [1/4] platform/x86: think-lmi: improve check if BIOS account security enabled | expand

Commit Message

Mark Pearson Oct. 21, 2024, 7:38 p.m. UTC
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>
---
 .../testing/sysfs-class-firmware-attributes   |   1 +
 drivers/platform/x86/think-lmi.c              | 141 ++++++++++++++----
 drivers/platform/x86/think-lmi.h              |   4 +
 3 files changed, 116 insertions(+), 30 deletions(-)

Comments

Ilpo Järvinen Oct. 22, 2024, 8:14 a.m. UTC | #1
On Mon, 21 Oct 2024, Mark Pearson wrote:

> 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>
> ---
>  .../testing/sysfs-class-firmware-attributes   |   1 +
>  drivers/platform/x86/think-lmi.c              | 141 ++++++++++++++----
>  drivers/platform/x86/think-lmi.h              |   4 +
>  3 files changed, 116 insertions(+), 30 deletions(-)
> 
> diff --git a/Documentation/ABI/testing/sysfs-class-firmware-attributes b/Documentation/ABI/testing/sysfs-class-firmware-attributes
> index 1a8b59f5d6e3..2713efa509b4 100644
> --- a/Documentation/ABI/testing/sysfs-class-firmware-attributes
> +++ b/Documentation/ABI/testing/sysfs-class-firmware-attributes
> @@ -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
> diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c
> index 751e351dfc42..fca190232c24 100644
> --- a/drivers/platform/x86/think-lmi.c
> +++ b/drivers/platform/x86/think-lmi.c
> @@ -169,11 +169,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},
> @@ -678,18 +679,35 @@ static ssize_t cert_thumbprint(char *buf, const char *arg, int count)
>  	return count;
>  }
>  
> +#define NUM_THUMBTYPES 3
> +static char *thumbtypes[NUM_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);
> -	int count = 0;
> +	char *wmistr;
> +	int count = 0, i;

Reverse xmas-tree order.

>  
>  	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 < NUM_THUMBTYPES; i++) {

Use ARRAY_SIZE() + add include for it.

These days, the usual custom is to use unsigned int for loop variables 
that are never meant to be negative.

> +		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;
>  }
>  
> @@ -720,8 +738,15 @@ static ssize_t cert_to_password_store(struct kobject *kobj,
>  	if (!passwd)
>  		return -ENOMEM;
>  
> -	/* Format: 'Password,Signature' */
> -	auth_str = kasprintf(GFP_KERNEL, "%s,%s", passwd, setting->signature);
> +	if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) {
> +		/* Format: 'SVC | SMC, password, signature' */
> +		auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s",
> +				     setting == tlmi_priv.pwd_admin ? "SVC" : "SMC",
> +				     passwd, setting->signature);
> +	} else {
> +		/* Format: 'Password,Signature' */
> +		auth_str = kasprintf(GFP_KERNEL, "%s,%s", passwd, setting->signature);
> +	}
>  	if (!auth_str) {
>  		kfree_sensitive(passwd);
>  		return -ENOMEM;
> @@ -735,12 +760,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;
>  
> @@ -756,10 +788,18 @@ static ssize_t certificate_store(struct kobject *kobj,
>  		if (!setting->signature || !setting->signature[0])
>  			return -EACCES;
>  
> -		/* Format: 'serial#, signature' */
> -		auth_str = kasprintf(GFP_KERNEL, "%s,%s",
> -				dmi_get_system_info(DMI_PRODUCT_SERIAL),
> -				setting->signature);
> +		if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) {
> +			/* Format: 'SVC | SMC, serial#, signature' */
> +			auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s",
> +					     setting == tlmi_priv.pwd_admin ? "SVC" : "SMC",
> +					     dmi_get_system_info(DMI_PRODUCT_SERIAL),
> +					     setting->signature);
> +		} else {
> +			/* Format: 'serial#, signature' */
> +			auth_str = kasprintf(GFP_KERNEL, "%s,%s",
> +					     dmi_get_system_info(DMI_PRODUCT_SERIAL),
> +					     setting->signature);
> +		}
>  		if (!auth_str)
>  			return -ENOMEM;
>  
> @@ -776,24 +816,59 @@ 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 it's 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);
> +		if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) {
> +			/* Format: 'SVC | SMC, certificate, signature' */
> +			auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s",
> +					     setting == tlmi_priv.pwd_admin ? "SVC" : "SMC",
> +					     new_cert, signature);
> +		} else {
> +			/* Format: 'Certificate,Signature' */
> +			auth_str = kasprintf(GFP_KERNEL, "%s,%s",
> +					     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);
> +		if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) {
> +			/* Format: 'SVC | SMC, Certificate, password' */
> +			auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s",
> +					     setting == tlmi_priv.pwd_admin ? "SVC" : "SMC",
> +					     new_cert, setting->password);
> +		} else {
> +			/* Format: 'Certificate, password' */
> +			auth_str = kasprintf(GFP_KERNEL, "%s,%s", new_cert, setting->password);
> +		}

Have you considered creating a helper for this if/else construct to create 
the string? (you've basically repeated it multiple times by now with 
limited change to only parameters AFAICT).

>  	}
>  	kfree(new_cert);
>  	if (!auth_str)
> @@ -873,14 +948,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)
> +		if (tlmi_priv.certificate_support) {
> +			if (setting == tlmi_priv.pwd_admin)

These two if()s combined look the same as the old ode, why are you redoing 
it?

> +				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,12 +1780,13 @@ 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;
> -

Stray change.
Mark Pearson Oct. 22, 2024, 7:30 p.m. UTC | #2
Hi Ilpo,

Thanks for the review.

On Tue, Oct 22, 2024, at 4:14 AM, Ilpo Järvinen wrote:
> On Mon, 21 Oct 2024, Mark Pearson wrote:
>
>> 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>
>> ---
>>  .../testing/sysfs-class-firmware-attributes   |   1 +
>>  drivers/platform/x86/think-lmi.c              | 141 ++++++++++++++----
>>  drivers/platform/x86/think-lmi.h              |   4 +
>>  3 files changed, 116 insertions(+), 30 deletions(-)
>> 
>> diff --git a/Documentation/ABI/testing/sysfs-class-firmware-attributes b/Documentation/ABI/testing/sysfs-class-firmware-attributes
>> index 1a8b59f5d6e3..2713efa509b4 100644
>> --- a/Documentation/ABI/testing/sysfs-class-firmware-attributes
>> +++ b/Documentation/ABI/testing/sysfs-class-firmware-attributes
>> @@ -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
>> diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c
>> index 751e351dfc42..fca190232c24 100644
>> --- a/drivers/platform/x86/think-lmi.c
>> +++ b/drivers/platform/x86/think-lmi.c
>> @@ -169,11 +169,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},
>> @@ -678,18 +679,35 @@ static ssize_t cert_thumbprint(char *buf, const char *arg, int count)
>>  	return count;
>>  }
>>  
>> +#define NUM_THUMBTYPES 3
>> +static char *thumbtypes[NUM_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);
>> -	int count = 0;
>> +	char *wmistr;
>> +	int count = 0, i;
>
> Reverse xmas-tree order.

Ack

>
>>  
>>  	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 < NUM_THUMBTYPES; i++) {
>
> Use ARRAY_SIZE() + add include for it.
>
> These days, the usual custom is to use unsigned int for loop variables 
> that are never meant to be negative.
>
Will update.

>> +		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;
>>  }
>>  
>> @@ -720,8 +738,15 @@ static ssize_t cert_to_password_store(struct kobject *kobj,
>>  	if (!passwd)
>>  		return -ENOMEM;
>>  
>> -	/* Format: 'Password,Signature' */
>> -	auth_str = kasprintf(GFP_KERNEL, "%s,%s", passwd, setting->signature);
>> +	if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) {
>> +		/* Format: 'SVC | SMC, password, signature' */
>> +		auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s",
>> +				     setting == tlmi_priv.pwd_admin ? "SVC" : "SMC",
>> +				     passwd, setting->signature);
>> +	} else {
>> +		/* Format: 'Password,Signature' */
>> +		auth_str = kasprintf(GFP_KERNEL, "%s,%s", passwd, setting->signature);
>> +	}
>>  	if (!auth_str) {
>>  		kfree_sensitive(passwd);
>>  		return -ENOMEM;
>> @@ -735,12 +760,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;
>>  
>> @@ -756,10 +788,18 @@ static ssize_t certificate_store(struct kobject *kobj,
>>  		if (!setting->signature || !setting->signature[0])
>>  			return -EACCES;
>>  
>> -		/* Format: 'serial#, signature' */
>> -		auth_str = kasprintf(GFP_KERNEL, "%s,%s",
>> -				dmi_get_system_info(DMI_PRODUCT_SERIAL),
>> -				setting->signature);
>> +		if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) {
>> +			/* Format: 'SVC | SMC, serial#, signature' */
>> +			auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s",
>> +					     setting == tlmi_priv.pwd_admin ? "SVC" : "SMC",
>> +					     dmi_get_system_info(DMI_PRODUCT_SERIAL),
>> +					     setting->signature);
>> +		} else {
>> +			/* Format: 'serial#, signature' */
>> +			auth_str = kasprintf(GFP_KERNEL, "%s,%s",
>> +					     dmi_get_system_info(DMI_PRODUCT_SERIAL),
>> +					     setting->signature);
>> +		}
>>  		if (!auth_str)
>>  			return -ENOMEM;
>>  
>> @@ -776,24 +816,59 @@ 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 it's 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);
>> +		if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) {
>> +			/* Format: 'SVC | SMC, certificate, signature' */
>> +			auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s",
>> +					     setting == tlmi_priv.pwd_admin ? "SVC" : "SMC",
>> +					     new_cert, signature);
>> +		} else {
>> +			/* Format: 'Certificate,Signature' */
>> +			auth_str = kasprintf(GFP_KERNEL, "%s,%s",
>> +					     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);
>> +		if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) {
>> +			/* Format: 'SVC | SMC, Certificate, password' */
>> +			auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s",
>> +					     setting == tlmi_priv.pwd_admin ? "SVC" : "SMC",
>> +					     new_cert, setting->password);
>> +		} else {
>> +			/* Format: 'Certificate, password' */
>> +			auth_str = kasprintf(GFP_KERNEL, "%s,%s", new_cert, setting->password);
>> +		}
>
> Have you considered creating a helper for this if/else construct to create 
> the string? (you've basically repeated it multiple times by now with 
> limited change to only parameters AFAICT).
>

I hadn't considered it, but yeah, it probably makes sense.
Not completely sure how I would do it to be honest - if you've got any suggestions let me know, but I will look into it and see if I can improve it.

>>  	}
>>  	kfree(new_cert);
>>  	if (!auth_str)
>> @@ -873,14 +948,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)
>> +		if (tlmi_priv.certificate_support) {
>> +			if (setting == tlmi_priv.pwd_admin)
>
> These two if()s combined look the same as the old ode, why are you redoing 
> it?

My indenting here is somehow messed up...not sure how that happened (will fix).
The block below should be indented too...and then I think it makes more sense.
Let me know if you disagree.

>
>> +				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,12 +1780,13 @@ 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;
>> -
>
> Stray change.

Oops. Will fix.

>
> -- 
>  i.
>
>>  fail_clear_attr:
>>  	for (i = 0; i < TLMI_SETTINGS_COUNT; ++i) {
>>  		if (tlmi_priv.setting[i]) {
>> diff --git a/drivers/platform/x86/think-lmi.h b/drivers/platform/x86/think-lmi.h
>> index 4728f40143a3..f267d8b46957 100644
>> --- a/drivers/platform/x86/think-lmi.h
>> +++ b/drivers/platform/x86/think-lmi.h
>> @@ -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;
>>

Mark
diff mbox series

Patch

diff --git a/Documentation/ABI/testing/sysfs-class-firmware-attributes b/Documentation/ABI/testing/sysfs-class-firmware-attributes
index 1a8b59f5d6e3..2713efa509b4 100644
--- a/Documentation/ABI/testing/sysfs-class-firmware-attributes
+++ b/Documentation/ABI/testing/sysfs-class-firmware-attributes
@@ -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
diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c
index 751e351dfc42..fca190232c24 100644
--- a/drivers/platform/x86/think-lmi.c
+++ b/drivers/platform/x86/think-lmi.c
@@ -169,11 +169,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},
@@ -678,18 +679,35 @@  static ssize_t cert_thumbprint(char *buf, const char *arg, int count)
 	return count;
 }
 
+#define NUM_THUMBTYPES 3
+static char *thumbtypes[NUM_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);
-	int count = 0;
+	char *wmistr;
+	int count = 0, i;
 
 	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 < NUM_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;
 }
 
@@ -720,8 +738,15 @@  static ssize_t cert_to_password_store(struct kobject *kobj,
 	if (!passwd)
 		return -ENOMEM;
 
-	/* Format: 'Password,Signature' */
-	auth_str = kasprintf(GFP_KERNEL, "%s,%s", passwd, setting->signature);
+	if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) {
+		/* Format: 'SVC | SMC, password, signature' */
+		auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s",
+				     setting == tlmi_priv.pwd_admin ? "SVC" : "SMC",
+				     passwd, setting->signature);
+	} else {
+		/* Format: 'Password,Signature' */
+		auth_str = kasprintf(GFP_KERNEL, "%s,%s", passwd, setting->signature);
+	}
 	if (!auth_str) {
 		kfree_sensitive(passwd);
 		return -ENOMEM;
@@ -735,12 +760,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;
 
@@ -756,10 +788,18 @@  static ssize_t certificate_store(struct kobject *kobj,
 		if (!setting->signature || !setting->signature[0])
 			return -EACCES;
 
-		/* Format: 'serial#, signature' */
-		auth_str = kasprintf(GFP_KERNEL, "%s,%s",
-				dmi_get_system_info(DMI_PRODUCT_SERIAL),
-				setting->signature);
+		if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) {
+			/* Format: 'SVC | SMC, serial#, signature' */
+			auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s",
+					     setting == tlmi_priv.pwd_admin ? "SVC" : "SMC",
+					     dmi_get_system_info(DMI_PRODUCT_SERIAL),
+					     setting->signature);
+		} else {
+			/* Format: 'serial#, signature' */
+			auth_str = kasprintf(GFP_KERNEL, "%s,%s",
+					     dmi_get_system_info(DMI_PRODUCT_SERIAL),
+					     setting->signature);
+		}
 		if (!auth_str)
 			return -ENOMEM;
 
@@ -776,24 +816,59 @@  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 it's 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);
+		if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) {
+			/* Format: 'SVC | SMC, certificate, signature' */
+			auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s",
+					     setting == tlmi_priv.pwd_admin ? "SVC" : "SMC",
+					     new_cert, signature);
+		} else {
+			/* Format: 'Certificate,Signature' */
+			auth_str = kasprintf(GFP_KERNEL, "%s,%s",
+					     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);
+		if (tlmi_priv.pwdcfg.core.password_mode >= TLMI_PWDCFG_MODE_MULTICERT) {
+			/* Format: 'SVC | SMC, Certificate, password' */
+			auth_str = kasprintf(GFP_KERNEL, "%s,%s,%s",
+					     setting == tlmi_priv.pwd_admin ? "SVC" : "SMC",
+					     new_cert, setting->password);
+		} else {
+			/* Format: 'Certificate, password' */
+			auth_str = kasprintf(GFP_KERNEL, "%s,%s", new_cert, setting->password);
+		}
 	}
 	kfree(new_cert);
 	if (!auth_str)
@@ -873,14 +948,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)
+		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,12 +1780,13 @@  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:
 	for (i = 0; i < TLMI_SETTINGS_COUNT; ++i) {
 		if (tlmi_priv.setting[i]) {
diff --git a/drivers/platform/x86/think-lmi.h b/drivers/platform/x86/think-lmi.h
index 4728f40143a3..f267d8b46957 100644
--- a/drivers/platform/x86/think-lmi.h
+++ b/drivers/platform/x86/think-lmi.h
@@ -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;