diff mbox series

[v2,4/6] secvar/edk2: change get_key_authority to return a list of variables instead of their names

Message ID 20211101220513.835940-5-erichte@linux.ibm.com
State Superseded
Headers show
Series Secvar adjustments and fixes | expand

Commit Message

Eric Richter Nov. 1, 2021, 10:05 p.m. UTC
The current get_key_authority function returns a list of variable names that
can authorize updates to a particular variable (e.g. "KEK" can authorize
updates to "db").

As the names will have to be fetched anyway via calls to find_secvar(), this
patch changes the behavior to operate on secvars themselves instead of just
the names. It takes in a secvar reference, and returns a list of secvar
references that hold the proper authority, thus cleaning up a few
extra unnecessary temporary variables.

Signed-off-by: Eric Richter <erichte@linux.ibm.com>
---
 libstb/secvar/backend/edk2-compat-process.c | 32 +++++++++------------
 1 file changed, 14 insertions(+), 18 deletions(-)

Comments

Daniel Axtens Nov. 3, 2021, 5:04 a.m. UTC | #1
Eric Richter <erichte@linux.ibm.com> writes:

> The current get_key_authority function returns a list of variable names that
> can authorize updates to a particular variable (e.g. "KEK" can authorize
> updates to "db").
>
> As the names will have to be fetched anyway via calls to find_secvar(), this
> patch changes the behavior to operate on secvars themselves instead of just
> the names. It takes in a secvar reference, and returns a list of secvar
> references that hold the proper authority, thus cleaning up a few
> extra unnecessary temporary variables.
>
> Signed-off-by: Eric Richter <erichte@linux.ibm.com>
> ---
>  libstb/secvar/backend/edk2-compat-process.c | 32 +++++++++------------
>  1 file changed, 14 insertions(+), 18 deletions(-)
>
> diff --git a/libstb/secvar/backend/edk2-compat-process.c b/libstb/secvar/backend/edk2-compat-process.c
> index d69e066f..cdc95737 100644
> --- a/libstb/secvar/backend/edk2-compat-process.c
> +++ b/libstb/secvar/backend/edk2-compat-process.c
> @@ -72,17 +72,17 @@ static char *char_to_wchar(const char *key, const size_t keylen)
>  }
>  
>  /* Returns the authority that can sign the given key update */
> -static void get_key_authority(const char *ret[3], const char *key)
> +static void get_key_authority(const struct secvar *ret[3], const struct secvar *update, struct list_head *bank)
>  {
>  	int i = 0;
>  
> -	if (key_equals(key, "PK")) {
> -		ret[i++] = "PK";
> -	} else if (key_equals(key, "KEK")) {
> -		ret[i++] = "PK";
> -	} else if (key_equals(key, "db") || key_equals(key, "dbx")) {
> -		ret[i++] = "KEK";
> -		ret[i++] = "PK";
> +	if (key_equals(update->key, "PK")) {
> +		ret[i++] = find_secvar("PK", 3, bank);
> +	} else if (key_equals(update->key, "KEK")) {
> +		ret[i++] = find_secvar("PK", 3, bank);
> +	} else if (key_equals(update->key, "db") || key_equals(update->key, "dbx")) {
> +		ret[i++] = find_secvar("KEK", 4, bank);
> +		ret[i++] = find_secvar("PK", 3, bank);
>  	}

If you have PK but not KEK, and you try to update db, ret will be:
{NULL, <secvar: PK>, NULL}. This will cause the for loop further down in
process_update to fall apart because it checks for NULLs.

>  
>  	ret[i] = NULL;
> @@ -701,9 +701,8 @@ int process_update(const struct secvar *update, char **newesl,
>  	struct efi_variable_authentication_2 *auth = NULL;
>  	void *auth_buffer = NULL;
>  	int auth_buffer_size = 0;
> -	const char *key_authority[3];
> +	const struct secvar *key_authority[3];
>  	char *hash = NULL;
> -	struct secvar *avar = NULL;
>  	int rc = 0;
>  	int i;
>  
> @@ -770,7 +769,7 @@ int process_update(const struct secvar *update, char **newesl,
>  	}
>  
>  	/* Get the authority to verify the signature */
> -	get_key_authority(key_authority, update->key);
> +	get_key_authority(key_authority, update, bank);
>  
>  	/*
>  	 * Try for all the authorities that are allowed to sign.
> @@ -778,19 +777,16 @@ int process_update(const struct secvar *update, char **newesl,
>  	 */
>  	for (i = 0; key_authority[i] != NULL; i++) {

As I mentioned above, this might go wrong if you're updating db(x) and
have PK but no KEK. I think you probably need to iterate over
ARRAY_SIZE(key_authority) and just `continue` if NULL?

>  		prlog(PR_DEBUG, "key is %s\n", update->key);
> -		prlog(PR_DEBUG, "key authority is %s\n", key_authority[i]);
> -		avar = find_secvar(key_authority[i],
> -				    strlen(key_authority[i]) + 1,
> -				    bank);
> -		if (!avar || !avar->data_size)
> +		prlog(PR_DEBUG, "key authority is %s\n", key_authority[i]->key);
> +		if (!key_authority[i] || !key_authority[i]->data_size)
>  			continue;

I thought you were doing an undefined behaviour there: dereferencing
key_authority[i] before you check whether or not it is NULL. However,
key_authority is already not NULL by virtue of the check in the for
loop. So really the test in the `if` can be simplified. It did make
sense before because avar lookup could give you NULL.

If you rejig the loop you'll need to reevaluate this, of course.

Kind regards,
Daniel
Eric Richter Nov. 3, 2021, 3:35 p.m. UTC | #2
On 11/3/21 12:04 AM, Daniel Axtens wrote:
> Eric Richter <erichte@linux.ibm.com> writes:
> 
>> The current get_key_authority function returns a list of variable names that
>> can authorize updates to a particular variable (e.g. "KEK" can authorize
>> updates to "db").
>>
>> As the names will have to be fetched anyway via calls to find_secvar(), this
>> patch changes the behavior to operate on secvars themselves instead of just
>> the names. It takes in a secvar reference, and returns a list of secvar
>> references that hold the proper authority, thus cleaning up a few
>> extra unnecessary temporary variables.
>>
>> Signed-off-by: Eric Richter <erichte@linux.ibm.com>
>> ---
>>  libstb/secvar/backend/edk2-compat-process.c | 32 +++++++++------------
>>  1 file changed, 14 insertions(+), 18 deletions(-)
>>
>> diff --git a/libstb/secvar/backend/edk2-compat-process.c b/libstb/secvar/backend/edk2-compat-process.c
>> index d69e066f..cdc95737 100644
>> --- a/libstb/secvar/backend/edk2-compat-process.c
>> +++ b/libstb/secvar/backend/edk2-compat-process.c
>> @@ -72,17 +72,17 @@ static char *char_to_wchar(const char *key, const size_t keylen)
>>  }
>>  
>>  /* Returns the authority that can sign the given key update */
>> -static void get_key_authority(const char *ret[3], const char *key)
>> +static void get_key_authority(const struct secvar *ret[3], const struct secvar *update, struct list_head *bank)
>>  {
>>  	int i = 0;
>>  
>> -	if (key_equals(key, "PK")) {
>> -		ret[i++] = "PK";
>> -	} else if (key_equals(key, "KEK")) {
>> -		ret[i++] = "PK";
>> -	} else if (key_equals(key, "db") || key_equals(key, "dbx")) {
>> -		ret[i++] = "KEK";
>> -		ret[i++] = "PK";
>> +	if (key_equals(update->key, "PK")) {
>> +		ret[i++] = find_secvar("PK", 3, bank);
>> +	} else if (key_equals(update->key, "KEK")) {
>> +		ret[i++] = find_secvar("PK", 3, bank);
>> +	} else if (key_equals(update->key, "db") || key_equals(update->key, "dbx")) {
>> +		ret[i++] = find_secvar("KEK", 4, bank);
>> +		ret[i++] = find_secvar("PK", 3, bank);
>>  	}
> 
> If you have PK but not KEK, and you try to update db, ret will be:
> {NULL, <secvar: PK>, NULL}. This will cause the for loop further down in
> process_update to fall apart because it checks for NULLs.
> 

Non-obvious quirk I will update with a comment: These variables always exist
in the bank due to .pre_process() generating empty ones if they aren't loaded
from storage.

The old code effectively did this anyway, I just moved the find_secvar() calls
in here to shorten the loop.

>>  
>>  	ret[i] = NULL;
>> @@ -701,9 +701,8 @@ int process_update(const struct secvar *update, char **newesl,
>>  	struct efi_variable_authentication_2 *auth = NULL;
>>  	void *auth_buffer = NULL;
>>  	int auth_buffer_size = 0;
>> -	const char *key_authority[3];
>> +	const struct secvar *key_authority[3];
>>  	char *hash = NULL;
>> -	struct secvar *avar = NULL;
>>  	int rc = 0;
>>  	int i;
>>  
>> @@ -770,7 +769,7 @@ int process_update(const struct secvar *update, char **newesl,
>>  	}
>>  
>>  	/* Get the authority to verify the signature */
>> -	get_key_authority(key_authority, update->key);
>> +	get_key_authority(key_authority, update, bank);
>>  
>>  	/*
>>  	 * Try for all the authorities that are allowed to sign.
>> @@ -778,19 +777,16 @@ int process_update(const struct secvar *update, char **newesl,
>>  	 */
>>  	for (i = 0; key_authority[i] != NULL; i++) {
> 
> As I mentioned above, this might go wrong if you're updating db(x) and
> have PK but no KEK. I think you probably need to iterate over
> ARRAY_SIZE(key_authority) and just `continue` if NULL?
> 
>>  		prlog(PR_DEBUG, "key is %s\n", update->key);
>> -		prlog(PR_DEBUG, "key authority is %s\n", key_authority[i]);
>> -		avar = find_secvar(key_authority[i],
>> -				    strlen(key_authority[i]) + 1,
>> -				    bank);
>> -		if (!avar || !avar->data_size)
>> +		prlog(PR_DEBUG, "key authority is %s\n", key_authority[i]->key);
>> +		if (!key_authority[i] || !key_authority[i]->data_size)
>>  			continue;
> 
> I thought you were doing an undefined behaviour there: dereferencing
> key_authority[i] before you check whether or not it is NULL. However,
> key_authority is already not NULL by virtue of the check in the for
> loop. So really the test in the `if` can be simplified. It did make
> sense before because avar lookup could give you NULL.
> 

Ah, yup. Find/replace at its best.

> If you rejig the loop you'll need to reevaluate this, of course.

I'll probably end up doing this. The variables won't be NULL, but I
won't leave that to chance in case they ever are.

> 
> Kind regards,
> Daniel
>
diff mbox series

Patch

diff --git a/libstb/secvar/backend/edk2-compat-process.c b/libstb/secvar/backend/edk2-compat-process.c
index d69e066f..cdc95737 100644
--- a/libstb/secvar/backend/edk2-compat-process.c
+++ b/libstb/secvar/backend/edk2-compat-process.c
@@ -72,17 +72,17 @@  static char *char_to_wchar(const char *key, const size_t keylen)
 }
 
 /* Returns the authority that can sign the given key update */
-static void get_key_authority(const char *ret[3], const char *key)
+static void get_key_authority(const struct secvar *ret[3], const struct secvar *update, struct list_head *bank)
 {
 	int i = 0;
 
-	if (key_equals(key, "PK")) {
-		ret[i++] = "PK";
-	} else if (key_equals(key, "KEK")) {
-		ret[i++] = "PK";
-	} else if (key_equals(key, "db") || key_equals(key, "dbx")) {
-		ret[i++] = "KEK";
-		ret[i++] = "PK";
+	if (key_equals(update->key, "PK")) {
+		ret[i++] = find_secvar("PK", 3, bank);
+	} else if (key_equals(update->key, "KEK")) {
+		ret[i++] = find_secvar("PK", 3, bank);
+	} else if (key_equals(update->key, "db") || key_equals(update->key, "dbx")) {
+		ret[i++] = find_secvar("KEK", 4, bank);
+		ret[i++] = find_secvar("PK", 3, bank);
 	}
 
 	ret[i] = NULL;
@@ -701,9 +701,8 @@  int process_update(const struct secvar *update, char **newesl,
 	struct efi_variable_authentication_2 *auth = NULL;
 	void *auth_buffer = NULL;
 	int auth_buffer_size = 0;
-	const char *key_authority[3];
+	const struct secvar *key_authority[3];
 	char *hash = NULL;
-	struct secvar *avar = NULL;
 	int rc = 0;
 	int i;
 
@@ -770,7 +769,7 @@  int process_update(const struct secvar *update, char **newesl,
 	}
 
 	/* Get the authority to verify the signature */
-	get_key_authority(key_authority, update->key);
+	get_key_authority(key_authority, update, bank);
 
 	/*
 	 * Try for all the authorities that are allowed to sign.
@@ -778,19 +777,16 @@  int process_update(const struct secvar *update, char **newesl,
 	 */
 	for (i = 0; key_authority[i] != NULL; i++) {
 		prlog(PR_DEBUG, "key is %s\n", update->key);
-		prlog(PR_DEBUG, "key authority is %s\n", key_authority[i]);
-		avar = find_secvar(key_authority[i],
-				    strlen(key_authority[i]) + 1,
-				    bank);
-		if (!avar || !avar->data_size)
+		prlog(PR_DEBUG, "key authority is %s\n", key_authority[i]->key);
+		if (!key_authority[i] || !key_authority[i]->data_size)
 			continue;
 
 		/* Verify the signature. sha256 is 32 bytes long. */
-		rc = verify_signature(auth, hash, 32, avar);
+		rc = verify_signature(auth, hash, 32, key_authority[i]);
 
 		/* Break if signature verification is successful */
 		if (rc == OPAL_SUCCESS) {
-			prlog(PR_INFO, "Key %s successfully verified by authority %s\n", update->key, key_authority[i]);
+			prlog(PR_INFO, "Key %s successfully verified by authority %s\n", update->key, key_authority[i]->key);
 			break;
 		}
 	}