LCOV - code coverage report
Current view: top level - libstb/secvar/backend - edk2-compat.c (source / functions) Coverage Total Hit
Test: skiboot.info Lines: 84.2 % 120 101
Test Date: 2025-01-14 15:10:59 Functions: 100.0 % 4 4
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 2.0-1