efi_loader: variable: keep temporary buffer during the authentication

This is a bug fix; Setting an authenticated variable may fail due to
a memory corruption in the authentication.

A temporary buffer will, if needed, be allocated to parse a variable's
authentication data, and some portion of buffer, specifically signer's
certificates, will be referenced by efi_signature_verify().

So the buffer should be kept valid until the authentication process
is finished.

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
Tested-by: Heinrich Schuchardt <xypron.glpk@gmx.de>
This commit is contained in:
AKASHI Takahiro 2020-08-12 09:37:50 +09:00 committed by Heinrich Schuchardt
parent bc78d22d0f
commit 0658bb29b0

View file

@ -37,16 +37,21 @@ static u8 pkcs7_hdr[] = {
* efi_variable_parse_signature - parse a signature in variable * efi_variable_parse_signature - parse a signature in variable
* @buf: Pointer to variable's value * @buf: Pointer to variable's value
* @buflen: Length of @buf * @buflen: Length of @buf
* @tmpbuf: Pointer to temporary buffer
* *
* Parse a signature embedded in variable's value and instantiate * Parse a signature embedded in variable's value and instantiate
* a pkcs7_message structure. Since pkcs7_parse_message() accepts only * a pkcs7_message structure. Since pkcs7_parse_message() accepts only
* pkcs7's signedData, some header needed be prepended for correctly * pkcs7's signedData, some header needed be prepended for correctly
* parsing authentication data, particularly for variable's. * parsing authentication data, particularly for variable's.
* A temporary buffer will be allocated if needed, and it should be
* kept valid during the authentication because some data in the buffer
* will be referenced by efi_signature_verify().
* *
* Return: Pointer to pkcs7_message structure on success, NULL on error * Return: Pointer to pkcs7_message structure on success, NULL on error
*/ */
static struct pkcs7_message *efi_variable_parse_signature(const void *buf, static struct pkcs7_message *efi_variable_parse_signature(const void *buf,
size_t buflen) size_t buflen,
u8 **tmpbuf)
{ {
u8 *ebuf; u8 *ebuf;
size_t ebuflen, len; size_t ebuflen, len;
@ -59,7 +64,9 @@ static struct pkcs7_message *efi_variable_parse_signature(const void *buf,
if (buflen > sizeof(pkcs7_hdr) && if (buflen > sizeof(pkcs7_hdr) &&
!memcmp(&((u8 *)buf)[4], &pkcs7_hdr[4], 11)) { !memcmp(&((u8 *)buf)[4], &pkcs7_hdr[4], 11)) {
msg = pkcs7_parse_message(buf, buflen); msg = pkcs7_parse_message(buf, buflen);
goto out; if (IS_ERR(msg))
return NULL;
return msg;
} }
/* /*
@ -94,12 +101,12 @@ static struct pkcs7_message *efi_variable_parse_signature(const void *buf,
msg = pkcs7_parse_message(ebuf, ebuflen); msg = pkcs7_parse_message(ebuf, ebuflen);
free(ebuf); if (IS_ERR(msg)) {
free(ebuf);
out:
if (IS_ERR(msg))
return NULL; return NULL;
}
*tmpbuf = ebuf;
return msg; return msg;
} }
@ -136,6 +143,7 @@ static efi_status_t efi_variable_authenticate(u16 *variable,
struct efi_time timestamp; struct efi_time timestamp;
struct rtc_time tm; struct rtc_time tm;
u64 new_time; u64 new_time;
u8 *ebuf;
enum efi_auth_var_type var_type; enum efi_auth_var_type var_type;
efi_status_t ret; efi_status_t ret;
@ -143,6 +151,7 @@ static efi_status_t efi_variable_authenticate(u16 *variable,
truststore = NULL; truststore = NULL;
truststore2 = NULL; truststore2 = NULL;
regs = NULL; regs = NULL;
ebuf = NULL;
ret = EFI_SECURITY_VIOLATION; ret = EFI_SECURITY_VIOLATION;
if (*data_size < sizeof(struct efi_variable_authentication_2)) if (*data_size < sizeof(struct efi_variable_authentication_2))
@ -204,9 +213,12 @@ static efi_status_t efi_variable_authenticate(u16 *variable,
/* variable's signature list */ /* variable's signature list */
if (auth->auth_info.hdr.dwLength < sizeof(auth->auth_info)) if (auth->auth_info.hdr.dwLength < sizeof(auth->auth_info))
goto err; goto err;
/* ebuf should be kept valid during the authentication */
var_sig = efi_variable_parse_signature(auth->auth_info.cert_data, var_sig = efi_variable_parse_signature(auth->auth_info.cert_data,
auth->auth_info.hdr.dwLength auth->auth_info.hdr.dwLength
- sizeof(auth->auth_info)); - sizeof(auth->auth_info),
&ebuf);
if (!var_sig) { if (!var_sig) {
EFI_PRINT("Parsing variable's signature failed\n"); EFI_PRINT("Parsing variable's signature failed\n");
goto err; goto err;
@ -262,6 +274,7 @@ err:
efi_sigstore_free(truststore); efi_sigstore_free(truststore);
efi_sigstore_free(truststore2); efi_sigstore_free(truststore2);
pkcs7_free_message(var_sig); pkcs7_free_message(var_sig);
free(ebuf);
free(regs); free(regs);
return ret; return ret;