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

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

 



Thanks Hans

On 2022-03-17 07:21, Hans de Goede wrote:
> Hi Mark,
> 
> On 3/15/22 20:56, 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
>>  - 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>
>> ---
>> Changes in v2:
>>  - Combined set_signature with signature and moved save_signature under
>> the authorisation folder
>>  - utility function to strip CR from string
>>  - Clean up code as recommended from review
> 
> Thanks this is looking better now, I still have a few small
> remarks, see below.
> 
>>
>>  drivers/platform/x86/think-lmi.c | 560 +++++++++++++++++++++++++------
>>  drivers/platform/x86/think-lmi.h |   5 +
>>  2 files changed, 461 insertions(+), 104 deletions(-)
>>
>> diff --git a/drivers/platform/x86/think-lmi.c b/drivers/platform/x86/think-lmi.c
>> index 0b73e16cccea..1db34a6c94c2 100644
>> --- a/drivers/platform/x86/think-lmi.c
>> +++ b/drivers/platform/x86/think-lmi.c
>> @@ -16,6 +16,7 @@
<snip>
>> +
>> +	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);
> 
> The block starting here.
> 
>> +		if (!auth_str) {
>> +			kfree(new_cert);
>> +			return -ENOMEM;
>> +		}
>> +		ret = tlmi_simple_call(LENOVO_UPDATE_BIOS_CERT_GUID, auth_str);
>> +		kfree(auth_str);
> 
> And ending here, is identical in the if and else paths with the
> exception of the guid.
> 
>> +	} 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 */
> 
> This comment looks weird with it being placed above the error check + return and
> the code really is self explanatory, please drop it.
> 
>> +	if (ret) {
>> +		kfree(new_cert);
>> +		return ret;
>> +	}
>> +
> 
> Together with the if/else refactoring we then get:
> 
> 	if (setting->cert_installed) {
> 		/* Certificate is installed so this is an update */
> 		if (!setting->signature || !setting->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);
> 	} else {
> 		/* This is a fresh install */
> 		if (!setting->valid || !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 (!auth_str) {
> 		kfree(new_cert);
> 		return -ENOMEM;
> 	}
> 
> 	ret = tlmi_simple_call(LENOVO_UPDATE_BIOS_CERT_GUID, auth_str);
> 	kfree(auth_str);
> 	if (ret) {
> 		kfree(new_cert);
> 		return ret;
> 	}
> 
Agreed - much nicer. I'll get that implemented

>> +	kfree(setting->certificate);
>> +	setting->certificate = new_cert;
>> +	return count;
>> +}
>> +
<snip>
>> +
>> +static ssize_t signature_show(struct kobject *kobj, struct kobj_attribute *attr,
>> +			 char *buf)
>> +{
>> +	struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
>> +
>> +	if (!setting->signature)
>> +		return sysfs_emit(buf, "Not set\n");
>> +
>> +	return sysfs_emit(buf, "%s\n", setting->signature);
>> +}
>> +
>> +static struct kobj_attribute auth_signature = __ATTR_RW(signature);
> 
> So thinking about this more, having a show function at all is a bad
> idea, that will allow an attacker to potentially steal the signature.
> 
> I guess that the signature is specific to the setting being changed, 
> but this will still allow a replay attack, to restore a setting which
> an attacker has seen being changed in the past. E.g. the admin
> enables USB ports for some debugging and then disables them again,
> now an attacker who was able to read the signature file while the
> admin was enableing the USB ports might re-enable them later.
> 
> So it would be best to just make this __ATR_WO and not have
> a show function at all, like we do for the password.
> 
Fair enough. I'm good to remove it.
> 
> 
>> +
>> +static ssize_t save_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;
>> +	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 */
>> +	strip_cr(new_signature);
>> +
>> +	/* Free any previous signature */
>> +	kfree(setting->save_signature);
>> +	setting->save_signature = new_signature;
>> +
>> +	return ret ?: count;
>> +}
>> +
>> +static ssize_t save_signature_show(struct kobject *kobj, struct kobj_attribute *attr,
>> +			 char *buf)
>> +{
>> +	struct tlmi_pwd_setting *setting = to_tlmi_pwd_setting(kobj);
>> +
>> +	if (!setting->save_signature)
>> +		return sysfs_emit(buf, "Not set\n");
>> +
>> +	return sysfs_emit(buf, "%s\n", setting->save_signature);
>> +}
>> +
>> +static struct kobj_attribute auth_save_signature = __ATTR_RW(save_signature);
> 
> idem, make this __ATTR_WO too.
ack

> 
<snip>
>> @@ -896,8 +1230,15 @@ static void tlmi_release_attr(void)
>>  	sysfs_remove_file(&tlmi_priv.attribute_kset->kobj, &pending_reboot.attr);
>>  	if (tlmi_priv.can_debug_cmd && debug_support)
>>  		sysfs_remove_file(&tlmi_priv.attribute_kset->kobj, &debug_cmd.attr);
>> +
>>  	kset_unregister(tlmi_priv.attribute_kset);
>>  
>> +	if (tlmi_priv.certificate_support) {
>> +		kfree(tlmi_priv.pwd_admin->certificate);
>> +		kfree(tlmi_priv.pwd_admin->signature);
>> +		kfree(tlmi_priv.pwd_admin->save_signature);
>> +	}
>> +
> 
> 1. These kfree() calls should be done only after removing the
> sysfs group, otherwise here is a race where the could still
> be accessed after being free-ed
> 
> 2. kfree(NULL) is a no-op so the "if (tlmi_priv.certificate_support)"
> check is not necessary.

Ack - I'll fix these.
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