LCOV - code coverage report
Current view: top level - libstb/secvar/backend - edk2-compat.c (source / functions) Hit Total Coverage
Test: skiboot.info Lines: 101 120 84.2 %
Date: 2024-01-02 21:04:04 Functions: 4 4 100.0 %
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
       2                 :            : /* Copyright 2020 IBM Corp. */
       3                 :            : #ifndef pr_fmt
       4                 :            : #define pr_fmt(fmt) "EDK2_COMPAT: " fmt
       5                 :            : #endif
       6                 :            : 
       7                 :            : #include <opal.h>
       8                 :            : #include <string.h>
       9                 :            : #include <time.h>
      10                 :            : #include <unistd.h>
      11                 :            : #include <stdint.h>
      12                 :            : #include <skiboot.h>
      13                 :            : #include <ccan/endian/endian.h>
      14                 :            : #include <mbedtls/error.h>
      15                 :            : #include "libstb/crypto/pkcs7/pkcs7.h"
      16                 :            : #include "edk2.h"
      17                 :            : #include "../secvar.h"
      18                 :            : #include "edk2-compat-process.h"
      19                 :            : #include "edk2-compat-reset.h"
      20                 :            : 
      21                 :            : struct list_head staging_bank;
      22                 :            : 
      23                 :            : /*
      24                 :            :  * Initializes supported variables as empty if not loaded from
      25                 :            :  * storage. Variables are initialized as volatile if not found.
      26                 :            :  * Updates should clear this flag.
      27                 :            :  * Returns OPAL Error if anything fails in initialization
      28                 :            :  */
      29                 :          1 : static int edk2_compat_pre_process(struct list_head *variable_bank,
      30                 :            :                                    struct list_head *update_bank __unused)
      31                 :            : {
      32                 :            :         struct secvar *pkvar;
      33                 :            :         struct secvar *kekvar;
      34                 :            :         struct secvar *dbvar;
      35                 :            :         struct secvar *dbxvar;
      36                 :            :         struct secvar *tsvar;
      37                 :            : 
      38                 :          1 :         pkvar = find_secvar("PK", 3, variable_bank);
      39                 :          1 :         if (!pkvar) {
      40                 :          1 :                 pkvar = new_secvar("PK", 3, NULL, 0, SECVAR_FLAG_VOLATILE
      41                 :            :                                 | SECVAR_FLAG_PROTECTED);
      42                 :          1 :                 if (!pkvar)
      43                 :          0 :                         return OPAL_NO_MEM;
      44                 :            : 
      45                 :          1 :                 list_add_tail(variable_bank, &pkvar->link);
      46                 :            :         }
      47                 :          1 :         if (pkvar->data_size == 0)
      48                 :          1 :                 setup_mode = true;
      49                 :            :         else
      50                 :          0 :                 setup_mode = false;
      51                 :            : 
      52                 :          1 :         kekvar = find_secvar("KEK", 4, variable_bank);
      53                 :          1 :         if (!kekvar) {
      54                 :          1 :                 kekvar = new_secvar("KEK", 4, NULL, 0, SECVAR_FLAG_VOLATILE);
      55                 :          1 :                 if (!kekvar)
      56                 :          0 :                         return OPAL_NO_MEM;
      57                 :            : 
      58                 :          1 :                 list_add_tail(variable_bank, &kekvar->link);
      59                 :            :         }
      60                 :            : 
      61                 :          1 :         dbvar = find_secvar("db", 3, variable_bank);
      62                 :          1 :         if (!dbvar) {
      63                 :          1 :                 dbvar = new_secvar("db", 3, NULL, 0, SECVAR_FLAG_VOLATILE);
      64                 :          1 :                 if (!dbvar)
      65                 :          0 :                         return OPAL_NO_MEM;
      66                 :            : 
      67                 :          1 :                 list_add_tail(variable_bank, &dbvar->link);
      68                 :            :         }
      69                 :            : 
      70                 :          1 :         dbxvar = find_secvar("dbx", 4, variable_bank);
      71                 :          1 :         if (!dbxvar) {
      72                 :          1 :                 dbxvar = new_secvar("dbx", 4, NULL, 0, SECVAR_FLAG_VOLATILE);
      73                 :          1 :                 if (!dbxvar)
      74                 :          0 :                         return OPAL_NO_MEM;
      75                 :            : 
      76                 :          1 :                 list_add_tail(variable_bank, &dbxvar->link);
      77                 :            :         }
      78                 :            : 
      79                 :            :         /*
      80                 :            :          * Should only ever happen on first boot. Timestamp is
      81                 :            :          * initialized with all zeroes.
      82                 :            :          */
      83                 :          1 :         tsvar = find_secvar("TS", 3, variable_bank);
      84                 :          1 :         if (!tsvar) {
      85                 :          1 :                 tsvar = alloc_secvar(3, sizeof(struct efi_time) * 4);
      86                 :          1 :                 if (!tsvar)
      87                 :          0 :                         return OPAL_NO_MEM;
      88                 :            : 
      89                 :          1 :                 memcpy(tsvar->key, "TS", 3);
      90                 :          1 :                 tsvar->key_len = 3;
      91                 :          1 :                 tsvar->data_size = sizeof(struct efi_time) * 4;
      92                 :          1 :                 tsvar->flags = SECVAR_FLAG_PROTECTED;
      93                 :          1 :                 memset(tsvar->data, 0, tsvar->data_size);
      94                 :          1 :                 list_add_tail(variable_bank, &tsvar->link);
      95                 :            :         }
      96                 :            : 
      97                 :          1 :         return OPAL_SUCCESS;
      98                 :            : };
      99                 :            : 
     100                 :         29 : static int edk2_compat_process(struct list_head *variable_bank,
     101                 :            :                                struct list_head *update_bank)
     102                 :            : {
     103                 :         29 :         struct secvar *var = NULL;
     104                 :         29 :         struct secvar *tsvar = NULL;
     105                 :            :         struct efi_time timestamp;
     106                 :         29 :         char *newesl = NULL;
     107                 :            :         int neweslsize;
     108                 :         29 :         int rc = 0;
     109                 :            : 
     110                 :         29 :         prlog(PR_INFO, "Setup mode = %d\n", setup_mode);
     111                 :            : 
     112                 :            :         /* Check HW-KEY-HASH */
     113                 :         29 :         if (!setup_mode) {
     114                 :         21 :                 rc = verify_hw_key_hash();
     115                 :         21 :                 if (rc != OPAL_SUCCESS) {
     116                 :          1 :                         prlog(PR_ERR, "Hardware key hash verification mismatch. Keystore and update queue is reset.\n");
     117                 :          1 :                         rc = reset_keystore(variable_bank);
     118                 :          1 :                         if (rc)
     119                 :          0 :                                 goto cleanup;
     120                 :          1 :                         setup_mode = true;
     121                 :          1 :                         goto cleanup;
     122                 :            :                 }
     123                 :            :         }
     124                 :            : 
     125                 :            :         /* Return early if we have no updates to process */
     126                 :         28 :         if (list_empty(update_bank)) {
     127                 :          0 :                 return OPAL_EMPTY;
     128                 :            :         }
     129                 :            : 
     130                 :            :         /*
     131                 :            :          * Make a working copy of variable bank that is updated
     132                 :            :          * during process
     133                 :            :          */
     134                 :         28 :         list_head_init(&staging_bank);
     135                 :         28 :         copy_bank_list(&staging_bank, variable_bank);
     136                 :            : 
     137                 :            :         /*
     138                 :            :          * Loop through each command in the update bank.
     139                 :            :          * If any command fails, it just loops out of the update bank.
     140                 :            :          * It should also clear the update bank.
     141                 :            :          */
     142                 :            : 
     143                 :            :         /* Read the TS variable first time and then keep updating it in-memory */
     144                 :         28 :         tsvar = find_secvar("TS", 3, &staging_bank);
     145                 :            : 
     146                 :            :         /*
     147                 :            :          * We cannot find timestamp variable, did someone tamper it ?, return
     148                 :            :          * OPAL_PERMISSION
     149                 :            :          */
     150                 :         28 :         if (!tsvar)
     151                 :          0 :                 return OPAL_PERMISSION;
     152                 :            : 
     153                 :         40 :         list_for_each(update_bank, var, link) {
     154                 :            : 
     155                 :            :                 /*
     156                 :            :                  * Submitted data is auth_2 descriptor + new ESL data
     157                 :            :                  * Extract the auth_2 2 descriptor
     158                 :            :                  */
     159                 :         32 :                 prlog(PR_INFO, "Update for %s\n", var->key);
     160                 :            : 
     161                 :         32 :                 rc = process_update(var, &newesl,
     162                 :            :                                     &neweslsize, &timestamp,
     163                 :            :                                     &staging_bank,
     164                 :            :                                     tsvar->data);
     165                 :         32 :                 if (rc) {
     166                 :         20 :                         prlog(PR_ERR, "Update processing failed with rc %04x\n", rc);
     167                 :         20 :                         break;
     168                 :            :                 }
     169                 :            : 
     170                 :            :                 /*
     171                 :            :                  * If reached here means, signature is verified so update the
     172                 :            :                  * value in the variable bank
     173                 :            :                  */
     174                 :         12 :                 rc = update_variable_in_bank(var,
     175                 :            :                                              newesl,
     176                 :            :                                              neweslsize,
     177                 :            :                                              &staging_bank);
     178                 :         12 :                 if (rc) {
     179                 :          0 :                         prlog(PR_ERR, "Updating the variable data failed %04x\n", rc);
     180                 :          0 :                         break;
     181                 :            :                 }
     182                 :            : 
     183                 :         12 :                 free(newesl);
     184                 :         12 :                 newesl = NULL;
     185                 :            :                 /* Update the TS variable with the new timestamp */
     186                 :         12 :                 rc = update_timestamp(var->key,
     187                 :            :                                       &timestamp,
     188                 :            :                                       tsvar->data);
     189                 :         12 :                 if (rc) {
     190                 :          0 :                         prlog (PR_ERR, "Variable updated, but timestamp updated failed %04x\n", rc);
     191                 :          0 :                         break;
     192                 :            :                 }
     193                 :            : 
     194                 :            :                 /*
     195                 :            :                  * If the PK is updated, update the secure boot state of the
     196                 :            :                  * system at the end of processing
     197                 :            :                  */
     198                 :         12 :                 if (key_equals(var->key, "PK")) {
     199                 :            :                         /*
     200                 :            :                          * PK is tied to a particular firmware image by mapping it with
     201                 :            :                          * hw-key-hash of that firmware. When PK is updated, hw-key-hash
     202                 :            :                          * is updated. And when PK is deleted, delete hw-key-hash as well
     203                 :            :                          */
     204                 :          5 :                         if(neweslsize == 0) {
     205                 :          2 :                                 setup_mode = true;
     206                 :          2 :                                 delete_hw_key_hash(&staging_bank);
     207                 :            :                         } else  {
     208                 :          3 :                                 setup_mode = false;
     209                 :          3 :                                 add_hw_key_hash(&staging_bank);
     210                 :            :                         }
     211                 :          5 :                         prlog(PR_DEBUG, "setup mode is %d\n", setup_mode);
     212                 :            :                 }
     213                 :            :         }
     214                 :            : 
     215                 :         28 :         if (rc == 0) {
     216                 :            :                 /* Update the variable bank with updated working copy */
     217                 :          8 :                 clear_bank_list(variable_bank);
     218                 :          8 :                 copy_bank_list(variable_bank, &staging_bank);
     219                 :            :         }
     220                 :            : 
     221                 :         28 :         free(newesl);
     222                 :         28 :         clear_bank_list(&staging_bank);
     223                 :            : 
     224                 :            :         /* Set the global variable setup_mode as per final contents in variable_bank */
     225                 :         28 :         var = find_secvar("PK", 3, variable_bank);
     226                 :         28 :         if (!var) {
     227                 :            :                 /* This should not happen */
     228                 :          0 :                 rc = OPAL_INTERNAL_ERROR;
     229                 :          0 :                 goto cleanup;
     230                 :            :         }
     231                 :            : 
     232                 :         28 :         if (var->data_size == 0)
     233                 :          8 :                 setup_mode = true;
     234                 :            :         else
     235                 :         20 :                 setup_mode = false;
     236                 :            : 
     237                 :         29 : cleanup:
     238                 :            :         /*
     239                 :            :          * For any failure in processing update queue, we clear the update bank
     240                 :            :          * and return failure
     241                 :            :          */
     242                 :         29 :         clear_bank_list(update_bank);
     243                 :            : 
     244                 :         29 :         return rc;
     245                 :            : }
     246                 :            : 
     247                 :          6 : static int edk2_compat_post_process(struct list_head *variable_bank,
     248                 :            :                                     struct list_head *update_bank __unused)
     249                 :            : {
     250                 :            :         struct secvar *hwvar;
     251                 :          6 :         if (!setup_mode) {
     252                 :          2 :                 secvar_set_secure_mode();
     253                 :          2 :                 prlog(PR_INFO, "Enforcing OS secure mode\n");
     254                 :            :                 /*
     255                 :            :                  * HW KEY HASH is no more needed after this point. It is already
     256                 :            :                  * visible to userspace via device-tree, so exposing via sysfs is
     257                 :            :                  * just a duplication. Remove it from in-memory copy.
     258                 :            :                  */
     259                 :          2 :                 hwvar = find_secvar("HWKH", 5, variable_bank);
     260                 :          2 :                 if (!hwvar) {
     261                 :          0 :                         prlog(PR_ERR, "cannot find hw-key-hash, should not happen\n");
     262                 :          0 :                         return OPAL_INTERNAL_ERROR;
     263                 :            :                 }
     264                 :          2 :                 list_del(&hwvar->link);
     265                 :          2 :                 dealloc_secvar(hwvar);
     266                 :            :         }
     267                 :            : 
     268                 :          6 :         return OPAL_SUCCESS;
     269                 :            : }
     270                 :            : 
     271                 :         34 : static int edk2_compat_validate(struct secvar *var)
     272                 :            : {
     273                 :            : 
     274                 :            :         /*
     275                 :            :          * Checks if the update is for supported
     276                 :            :          * Non-volatile secure variables
     277                 :            :          */
     278                 :         34 :         if (!key_equals(var->key, "PK")
     279                 :         26 :                         && !key_equals(var->key, "KEK")
     280                 :         15 :                         && !key_equals(var->key, "db")
     281                 :          6 :                         && !key_equals(var->key, "dbx"))
     282                 :          0 :                 return OPAL_PARAMETER;
     283                 :            : 
     284                 :            :         /* Check that signature type is PKCS7 */
     285                 :         34 :         if (!is_pkcs7_sig_format(var->data))
     286                 :          0 :                 return OPAL_PARAMETER;
     287                 :            : 
     288                 :         34 :         return OPAL_SUCCESS;
     289                 :            : };
     290                 :            : 
     291                 :            : struct secvar_backend_driver edk2_compatible_v1 = {
     292                 :            :         .pre_process = edk2_compat_pre_process,
     293                 :            :         .process = edk2_compat_process,
     294                 :            :         .post_process = edk2_compat_post_process,
     295                 :            :         .validate = edk2_compat_validate,
     296                 :            :         .compatible = "ibm,edk2-compat-v1",
     297                 :            : };

Generated by: LCOV version 1.14