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, ×tamp,
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 : : ×tamp,
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 : : };
|