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]

 



Hi,

On 3/14/22 16:46, Mark Pearson wrote:
> 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.

Sounds good, thank you.

> Many thanks for the review

You're welcome.

Regards,

Hans




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

  Powered by Linux