Re: [External] Re: [PATCH 2/2] platform/x86: think-lmi: Certificate authentication support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Thanks for the review Hans

On 2022-03-14 11:15, Hans de Goede wrote:
> Hi,
> 
> On 3/12/22 01:04, Mark Pearson wrote:
>> Implementation of certificate authentication feature for Lenovo
>> platforms. This allows for signed updates of BIOS settings.
>>
>> Functionality supported:
>>  - Cert support available check. At initialisation check if BIOS
>>    supports certification authentication and if a certificate is
>>    installed. Enable the sysfs nodes appropriately
>>  - certificate and signature authentication attributes to enable
>>    a user to install, update and delete a certificate using signed
>>    signatures
>>  - certificate_thumbprint to confirm installed certificate details
>>  - support to go from certificate to password based authentication
>>  - set_signature and save_signature attributes needed for setting
>>    BIOS attributes using certificate authentication
>>
>> Tested on X1 Carbon 10 with special trial BIOS. This feature is not
>> generally available yet but will be released later this year.
>>
>> Note, I also cleaned up the formating of the GUIDs when I was adding
>> the new defines. Hope that's OK to combine in this commit.
>>
>> Signed-off-by: Mark Pearson <markpearson@xxxxxxxxxx>
>> ---
>>  drivers/platform/x86/think-lmi.c | 560 ++++++++++++++++++++++++++-----
>>  drivers/platform/x86/think-lmi.h |   7 +
>>  2 files changed, 475 insertions(+), 92 deletions(-)
>>
>> diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c
>> index 0b73e16cccea..54ce71f97c37 100644
>> --- a/drivers/platform/x86/think-lmi.c
>> +++ b/drivers/platform/x86/think-lmi.c
>> @@ -16,6 +16,7 @@

<snip>
>>  
>> +static ssize_t cert_thumbprint(char *buf, const char *arg, int count)
>> +{
>> +	const struct acpi_buffer input = { strlen(arg), (char *)arg };
>> +	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
>> +	const union acpi_object *obj;
>> +	acpi_status status;
>> +
>> +	output.length = ACPI_ALLOCATE_BUFFER;
>> +	output.pointer = NULL;
> 
> This initialization of output is redundant, it is already initialized
> when it is declared.
Ack

<snip>
>> +
>> +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);
>> +	char *auth_str, *new_cert, *p;
>> +	int ret;
>> +
>> +	if (!capable(CAP_SYS_ADMIN))
>> +		return -EPERM;
>> +
>> +	if (!tlmi_priv.certificate_support)
>> +		return -EOPNOTSUPP;
>> +
>> +	new_cert = kstrdup(buf, GFP_KERNEL);
>> +	if (!new_cert)
>> +		return -ENOMEM;
>> +	/* Strip out CR if one is present */
>> +	p = strchrnul(new_cert, '\n');
>> +	*p = '\0';
>> +
>> +	/* If empty then clear installed certificate */
>> +	if (new_cert[0] == '\0') { /* Clear installed certificate */
> 
> You don't need new_cert anymore here, so do:
> 
> 		kfree(new_cert);
> 
> here.
> 
>> +		/* Check that signature is set */
>> +		if (!setting->signature || !setting->signature[0]) {
>> +			kfree(new_cert);
> 
> and drop it here,
> 
>> +			return -EACCES;
>> +		}
>> +		/* Format: 'serial#, signature' */
>> +		auth_str = kasprintf(GFP_KERNEL, "%s,%s",
>> +				dmi_get_system_info(DMI_PRODUCT_SERIAL),
>> +				setting->signature);
>> +		if (!auth_str) {
>> +			kfree(new_cert);
> 
> and here.
> 
>> +			return -ENOMEM;
>> +		}
>> +		ret = tlmi_simple_call(LENOVO_CLEAR_BIOS_CERT_GUID, auth_str);
>> +		kfree(auth_str);
> 
> Because you were missing a kfree(new_cert) here. Also you should free + clear
> setting->certificate here. So this if block should end up like this:
> 
> 	/* If empty then clear installed certificate */
> 	if (new_cert[0] == '\0') { /* Clear installed certificate */
> 		kfree(new_cert);
> 
> 		/* Check that signature is set */
> 		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 (!auth_str)
> 			return -ENOMEM;
> 
> 		ret = tlmi_simple_call(LENOVO_CLEAR_BIOS_CERT_GUID, auth_str);
> 		kfree(auth_str);
> 		if (ret)
> 			return ret;
> 
> 		kfree(setting->certificate);
> 		setting->certificate = NULL;
> 		return count;
> 	}
> 
> 
Agreed - that all makes sense. Will update.

>> +	}
>> +
>> +	if (setting->cert_installed) {
>> +		/* Certificate is installed so this is an update */
>> +		if (!setting->signature || !setting->signature[0]) {
>> +			kfree(new_cert);
>> +			return -EACCES;
>> +		}
>> +		/* Format: 'Certificate,Signature' */
>> +		auth_str = kasprintf(GFP_KERNEL, "%s,%s",
>> +				new_cert, setting->signature);> +		if (!auth_str) {
>> +			kfree(new_cert);
>> +			return -ENOMEM;
>> +		}
>> +		ret = tlmi_simple_call(LENOVO_UPDATE_BIOS_CERT_GUID, auth_str);
>> +		kfree(auth_str);
>> +	} else {
>> +		/* This is a fresh install */
>> +		if (!setting->valid || !setting->password[0]) {
>> +			kfree(new_cert);
>> +			return -EACCES;
>> +		}
>> +		/* Format: 'Certificate,Admin-password' */
>> +		auth_str = kasprintf(GFP_KERNEL, "%s,%s",
>> +				new_cert, setting->password);
>> +		if (!auth_str) {
>> +			kfree(new_cert);
>> +			return -ENOMEM;
>> +		}
>> +		ret = tlmi_simple_call(LENOVO_SET_BIOS_CERT_GUID, auth_str);
>> +		kfree(auth_str);
>> +	}
>> +
>> +	/* If successful update stored certificate */
>> +	if (!ret) {
>> +		kfree(setting->certificate);
>> +		setting->certificate = new_cert;
>> +	} else
>> +		kfree(new_cert);
>> +
>> +	return ret ?: count;
> 
> You have 2 "if (ret)" statements here (1 hidden in the return), please change this to:
> 
> 	if (ret) {
> 		kfree(new_cert);
> 		return ret;
> 	}
> 
> 	kfree(setting->certificate);
> 	setting->certificate = new_cert;
> 	return count;
> 
Will do

> 
>> +}
>> +
>> +static ssize_t certificate_show(struct kobject *kobj, struct kobj_attribute *attr,
>> +			 char *buf)
>> +{
>> +	struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
>> +
> 
> setting->certificate may be NULL here, you need to check for that and in
> that case only emit a "\n" I guess.
Ack.

> 
>> +	return sysfs_emit(buf, "%s\n", setting->certificate);
>> +}
>> +
>> +static struct kobj_attribute auth_certificate = __ATTR_RW(certificate);
>> +
>> +static ssize_t signature_store(struct kobject *kobj,
>> +				  struct kobj_attribute *attr,
>> +				  const char *buf, size_t count)
>> +{
>> +	struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
>> +	char *new_signature, *p;
>> +	int ret;
>> +
>> +	if (!capable(CAP_SYS_ADMIN))
>> +		return -EPERM;
>> +
>> +	if (!tlmi_priv.certificate_support)
>> +		return -EOPNOTSUPP;
>> +
>> +	new_signature = kstrdup(buf, GFP_KERNEL);
>> +	if (!new_signature)
>> +		return -ENOMEM;
>> +
>> +	/* Strip out CR if one is present */
>> +	p = strchrnul(new_signature, '\n');
>> +	*p = '\0';
> 
> Idea for a follow-up clean-up patch: this pattern of kstrdup user-argument
> (buf) + strip '\n' is repeated all over the driver, maybe add a little helper
> for this?
> 
Yes - that would make sense. Will do.

>> +
>> +	/* Free any previous signature */
>> +	kfree(setting->signature);
>> +	setting->signature = new_signature;
>> +
>> +	return ret ?: count;
>> +}
>> +
>> +static ssize_t signature_show(struct kobject *kobj, struct kobj_attribute *attr,
>> +			 char *buf)
>> +{
>> +	struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
>> +
> 
> setting->signature can be NULL here.

Ack

<snip>

>> +
>> +static ssize_t set_signature_show(struct kobject *kobj, struct kobj_attribute *attr,
>> +			 char *buf)
>> +{
>> +	return sysfs_emit(buf, "%s\n", tlmi_priv.set_signature);
>> +}
>> +
>> +static ssize_t save_signature_show(struct kobject *kobj, struct kobj_attribute *attr,
>> +			 char *buf)
>> +{
>> +	return sysfs_emit(buf, "%s\n", tlmi_priv.save_signature);
>> +}
>> +
>> +static struct kobj_attribute attr_set_signature = __ATTR_RW(set_signature);
>> +static struct kobj_attribute attr_save_signature = __ATTR_RW(save_signature);
> 
> <note I missed this while reviewing the documentation patch...>
> 
> Why not just use the /sys/class/firmware-attributes/thinklmi/authentication/Admin/signature
> value here ?
> 
> /sys/class/firmware-attributes/thinklmi/authentication/Admin/current_password is
> what is used for password based setting of fw-attributes as well as for changing
> the password; and
> 
> /sys/class/firmware-attributes/thinklmi/authentication/Admin/signature is set
> for changing the certificate, so it would be much more consitent to also use
> that for setting attributes when using certificate based auth?
> 
> Can / will the set and save signature ever be different from one another ? If yes
> then I guess we may need 2 attributes for this, I guess maybe just signature +
> save_signature ? Either way IMHO these 2 attributes / the 1 extra attribute
> for a separate save-signature really belongs under
> /sys/class/firmware-attributes/thinklmi/authentication/Admin/ IMHO and
> not under /sys/class/firmware-attributes/thinklmi/attributes/
> 
> What do you think about moving these there ?
> 
I have no issues with moving them. I had originally intended to have
them in auth but as I needed two signatures (the set and save are
different) I decided to make it explicit and to keep them in the same
place as the attribute being modified. But I can see it making sense to
just keep those under Authentication instead.

I'll update and get rid of set_signature and move save_signature.

Many thanks for the review

Mark



[Index of Archives]     [Linux Kernel Development]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux