]> git.proxmox.com Git - efi-boot-shim.git/blame - sbat.c
Update to the 15.4 release
[efi-boot-shim.git] / sbat.c
CommitLineData
031e5cce
SM
1// SPDX-License-Identifier: BSD-2-Clause-Patent
2/*
3 * sbat.c - parse SBAT data from the .sbat section data
4 */
5
6#include "shim.h"
7
8EFI_STATUS
9parse_sbat_section(char *section_base, size_t section_size,
10 size_t *n_entries,
11 struct sbat_section_entry ***entriesp)
12{
13 struct sbat_section_entry *entry = NULL, **entries;
14 EFI_STATUS efi_status = EFI_SUCCESS;
15 list_t csv, *pos = NULL;
16 char * end = section_base + section_size - 1;
17 size_t allocsz = 0;
18 size_t n;
19 char *strtab;
20
8119f718
SM
21 if (!section_base || !section_size || !n_entries || !entriesp) {
22 dprint(L"section_base:0x%lx section_size:0x%lx\n",
23 section_base, section_size);
24 dprint(L"n_entries:0x%lx entriesp:0x%lx\n",
25 n_entries, entriesp);
031e5cce 26 return EFI_INVALID_PARAMETER;
8119f718 27 }
031e5cce
SM
28
29 INIT_LIST_HEAD(&csv);
30
31 efi_status =
32 parse_csv_data(section_base, end, SBAT_SECTION_COLUMNS, &csv);
33 if (EFI_ERROR(efi_status)) {
8119f718 34 dprint(L"parse_csv_data failed: %r\n", efi_status);
031e5cce
SM
35 return efi_status;
36 }
37
38 n = 0;
39 list_for_each(pos, &csv) {
40 struct csv_row * row;
41 size_t i;
42
43 row = list_entry(pos, struct csv_row, list);
44
45 if (row->n_columns < SBAT_SECTION_COLUMNS) {
46 efi_status = EFI_INVALID_PARAMETER;
8119f718
SM
47 dprint(L"row->n_columns:%lu SBAT_SECTION_COLUMNS:%lu\n",
48 row->n_columns, SBAT_SECTION_COLUMNS);
031e5cce
SM
49 goto err;
50 }
51
52 allocsz += sizeof(struct sbat_section_entry *);
53 allocsz += sizeof(struct sbat_section_entry);
54 for (i = 0; i < row->n_columns; i++) {
55 if (row->columns[i][0] == '\000') {
8119f718 56 dprint(L"row[%lu].columns[%lu][0] == '\\000'\n", n, i);
031e5cce
SM
57 efi_status = EFI_INVALID_PARAMETER;
58 goto err;
59 }
60 allocsz += strlen(row->columns[i]) + 1;
61 }
62 n++;
63 }
64
65 strtab = AllocateZeroPool(allocsz);
66 if (!strtab) {
67 efi_status = EFI_OUT_OF_RESOURCES;
68 goto err;
69 }
70
71 entries = (struct sbat_section_entry **)strtab;
72 strtab += sizeof(struct sbat_section_entry *) * n;
73 entry = (struct sbat_section_entry *)strtab;
74 strtab += sizeof(struct sbat_section_entry) * n;
75 n = 0;
76
77 list_for_each(pos, &csv) {
78 struct csv_row * row;
79 size_t i;
80 const char **ptrs[] = {
81 &entry->component_name,
82 &entry->component_generation,
83 &entry->vendor_name,
84 &entry->vendor_package_name,
85 &entry->vendor_version,
86 &entry->vendor_url,
87 };
88
89
90 row = list_entry(pos, struct csv_row, list);
91 for (i = 0; i < row->n_columns; i++) {
92 *(ptrs[i]) = strtab;
93 strtab = stpcpy(strtab, row->columns[i]) + 1;
94 }
95 entries[n] = entry;
96 entry++;
97 n++;
98 }
99 *entriesp = entries;
100 *n_entries = n;
101err:
102 free_csv_list(&csv);
103 return efi_status;
104}
105
106void
107cleanup_sbat_section_entries(size_t n, struct sbat_section_entry **entries)
108{
109 if (!n || !entries)
110 return;
111
112 FreePool(entries);
113}
114
115EFI_STATUS
116verify_single_entry(struct sbat_section_entry *entry, struct sbat_var_entry *sbat_var_entry)
117{
118 UINT16 sbat_gen, sbat_var_gen;
119
120 if (strcmp((const char *)entry->component_name, (const char *)sbat_var_entry->component_name) == 0) {
121 dprint(L"component %a has a matching SBAT variable entry, verifying\n",
122 entry->component_name);
123
124 /*
125 * atoi returns zero for failed conversion, so essentially
126 * badly parsed component_generation will be treated as zero
127 */
128 sbat_gen = atoi((const char *)entry->component_generation);
129 sbat_var_gen = atoi((const char *)sbat_var_entry->component_generation);
130
131 if (sbat_gen < sbat_var_gen) {
8119f718
SM
132 dprint(L"component %a, generation %d, was revoked by %s variable\n",
133 entry->component_name, sbat_gen, SBAT_VAR_NAME);
031e5cce
SM
134 LogError(L"image did not pass SBAT verification\n");
135 return EFI_SECURITY_VIOLATION;
136 }
137 }
138 return EFI_SUCCESS;
139}
140
141void
142cleanup_sbat_var(list_t *entries)
143{
144 list_t *pos = NULL, *tmp = NULL;
145 struct sbat_var_entry *entry;
146 void *first = NULL;
147
148 list_for_each_safe(pos, tmp, entries) {
149 entry = list_entry(pos, struct sbat_var_entry, list);
150
8119f718 151 if (first == NULL || (uintptr_t)entry < (uintptr_t)first)
031e5cce
SM
152 first = entry;
153
154 list_del(&entry->list);
155 }
156 if (first)
157 FreePool(first);
158}
159
160EFI_STATUS
161verify_sbat_helper(list_t *local_sbat_var, size_t n, struct sbat_section_entry **entries)
162{
163 unsigned int i;
164 list_t *pos = NULL;
165 EFI_STATUS efi_status = EFI_SUCCESS;
166 struct sbat_var_entry *sbat_var_entry;
167
168 if (list_empty(local_sbat_var)) {
8119f718 169 dprint(L"%s variable not present\n", SBAT_VAR_NAME);
031e5cce
SM
170 return EFI_SUCCESS;
171 }
172
173 for (i = 0; i < n; i++) {
174 list_for_each(pos, local_sbat_var) {
175 sbat_var_entry = list_entry(pos, struct sbat_var_entry, list);
176 efi_status = verify_single_entry(entries[i], sbat_var_entry);
177 if (EFI_ERROR(efi_status))
178 goto out;
179 }
180 }
181
182out:
183 dprint(L"finished verifying SBAT data: %r\n", efi_status);
184 return efi_status;
185}
186
187EFI_STATUS
188verify_sbat(size_t n, struct sbat_section_entry **entries)
189{
190 EFI_STATUS efi_status;
191
192 efi_status = verify_sbat_helper(&sbat_var, n, entries);
193 return efi_status;
194}
195
196EFI_STATUS
197parse_sbat_var_data(list_t *entry_list, UINT8 *data, UINTN datasize)
198{
199 struct sbat_var_entry *entry = NULL, **entries;
200 EFI_STATUS efi_status = EFI_SUCCESS;
201 list_t csv, *pos = NULL;
202 char * start = (char *)data;
203 char * end = (char *)data + datasize - 1;
204 size_t allocsz = 0;
205 size_t n;
206 char *strtab;
207
208 if (!entry_list|| !data || datasize == 0)
209 return EFI_INVALID_PARAMETER;
210
211 INIT_LIST_HEAD(&csv);
212
213 efi_status = parse_csv_data(start, end, SBAT_VAR_COLUMNS, &csv);
214 if (EFI_ERROR(efi_status)) {
215 return efi_status;
216 }
217
218 n = 0;
219 list_for_each(pos, &csv) {
220 struct csv_row * row;
221 size_t i;
222
223 row = list_entry(pos, struct csv_row, list);
224
225 if (row->n_columns < SBAT_VAR_REQUIRED_COLUMNS) {
226 efi_status = EFI_INVALID_PARAMETER;
227 goto err;
228 }
229
230
231 allocsz += sizeof(struct sbat_var_entry *);
232 allocsz += sizeof(struct sbat_var_entry);
233 for (i = 0; i < row->n_columns; i++) {
234 if (!row->columns[i][0]) {
235 efi_status = EFI_INVALID_PARAMETER;
236 goto err;
237 }
238 allocsz += strlen(row->columns[i]) + 1;
239 }
240 n++;
241 }
242
243 strtab = AllocateZeroPool(allocsz);
244 if (!strtab) {
245 efi_status = EFI_OUT_OF_RESOURCES;
246 goto err;
247 }
248
249 INIT_LIST_HEAD(entry_list);
250
031e5cce
SM
251 entry = (struct sbat_var_entry *)strtab;
252 strtab += sizeof(struct sbat_var_entry) * n;
8119f718
SM
253 entries = (struct sbat_var_entry **)strtab;
254 strtab += sizeof(struct sbat_var_entry *) * n;
031e5cce
SM
255 n = 0;
256
257 list_for_each(pos, &csv) {
258 struct csv_row * row;
259 size_t i;
260 const char **ptrs[] = {
261 &entry->component_name,
262 &entry->component_generation,
263 &entry->sbat_datestamp,
264 };
265
266 row = list_entry(pos, struct csv_row, list);
267 for (i = 0; i < row->n_columns; i++) {
268 *(ptrs[i]) = strtab;
269 strtab = stpcpy(strtab, row->columns[i]) + 1;
270 }
271 INIT_LIST_HEAD(&entry->list);
272 list_add_tail(&entry->list, entry_list);
273 entries[n] = entry;
274 entry++;
275 n++;
276 }
277err:
278 free_csv_list(&csv);
279 return efi_status;
280}
281
282EFI_STATUS
283parse_sbat_var(list_t *entries)
284{
285 UINT8 *data = 0;
286 UINTN datasize;
287 EFI_STATUS efi_status;
288
8119f718
SM
289 if (!entries) {
290 dprint(L"entries is NULL\n");
031e5cce 291 return EFI_INVALID_PARAMETER;
8119f718 292 }
031e5cce
SM
293
294 efi_status = get_variable(SBAT_VAR_NAME, &data, &datasize, SHIM_LOCK_GUID);
295 if (EFI_ERROR(efi_status)) {
296 LogError(L"Failed to read SBAT variable\n", efi_status);
297 return efi_status;
298 }
299
300 /*
301 * We've intentionally made sure there's a NUL byte on all variable
302 * allocations, so use that here.
303 */
304 return parse_sbat_var_data(entries, data, datasize+1);
305}
306
307static bool
308check_sbat_var_attributes(UINT32 attributes)
309{
310#ifdef ENABLE_SHIM_DEVEL
311 return attributes == UEFI_VAR_NV_BS_RT;
312#else
313 return attributes == UEFI_VAR_NV_BS ||
314 attributes == UEFI_VAR_NV_BS_TIMEAUTH;
315#endif
316}
317
8119f718
SM
318bool
319preserve_sbat_uefi_variable(UINT8 *sbat, UINTN sbatsize, UINT32 attributes)
320{
321 return check_sbat_var_attributes(attributes) &&
322 sbatsize >= strlen(SBAT_VAR_SIG "1") &&
323 !strncmp((const char *)sbat, SBAT_VAR_SIG, strlen(SBAT_VAR_SIG));
324}
325
031e5cce
SM
326EFI_STATUS
327set_sbat_uefi_variable(void)
328{
329 EFI_STATUS efi_status = EFI_SUCCESS;
330 UINT32 attributes = 0;
331
332 UINT8 *sbat = NULL;
333 UINTN sbatsize = 0;
334
335 efi_status = get_variable_attr(SBAT_VAR_NAME, &sbat, &sbatsize,
336 SHIM_LOCK_GUID, &attributes);
337 /*
8119f718 338 * Always set the SbatLevel UEFI variable if it fails to read.
031e5cce 339 *
8119f718
SM
340 * Don't try to set the SbatLevel UEFI variable if attributes match
341 * and the signature matches.
031e5cce
SM
342 */
343 if (EFI_ERROR(efi_status)) {
344 dprint(L"SBAT read failed %r\n", efi_status);
8119f718
SM
345 } else if (preserve_sbat_uefi_variable(sbat, sbatsize, attributes)) {
346 dprint(L"%s variable is %d bytes, attributes are 0x%08x\n",
347 SBAT_VAR_NAME, sbatsize, attributes);
031e5cce
SM
348 FreePool(sbat);
349 return EFI_SUCCESS;
350 } else {
351 FreePool(sbat);
352
353 /* delete previous variable */
354 dprint("%s variable is %d bytes, attributes are 0x%08x\n",
355 SBAT_VAR_NAME, sbatsize, attributes);
356 dprint("Deleting %s variable.\n", SBAT_VAR_NAME);
357 efi_status = set_variable(SBAT_VAR_NAME, SHIM_LOCK_GUID,
358 attributes, 0, "");
359 if (EFI_ERROR(efi_status)) {
8119f718
SM
360 dprint(L"%s variable delete failed %r\n", SBAT_VAR_NAME,
361 efi_status);
031e5cce
SM
362 return efi_status;
363 }
364 }
365
366 /* set variable */
367 efi_status = set_variable(SBAT_VAR_NAME, SHIM_LOCK_GUID, SBAT_VAR_ATTRS,
368 sizeof(SBAT_VAR)-1, SBAT_VAR);
369 if (EFI_ERROR(efi_status)) {
8119f718
SM
370 dprint(L"%s variable writing failed %r\n", SBAT_VAR_NAME,
371 efi_status);
031e5cce
SM
372 return efi_status;
373 }
374
375 /* verify that the expected data is there */
376 efi_status = get_variable(SBAT_VAR_NAME, &sbat, &sbatsize,
377 SHIM_LOCK_GUID);
378 if (EFI_ERROR(efi_status)) {
8119f718 379 dprint(L"%s read failed %r\n", SBAT_VAR_NAME, efi_status);
031e5cce
SM
380 return efi_status;
381 }
382
383 if (sbatsize != strlen(SBAT_VAR) ||
384 strncmp((const char *)sbat, SBAT_VAR, strlen(SBAT_VAR)) != 0) {
385 dprint("new sbatsize is %d, expected %d\n", sbatsize,
386 strlen(SBAT_VAR));
387 efi_status = EFI_INVALID_PARAMETER;
388 } else {
8119f718 389 dprint(L"%s variable initialization succeeded\n", SBAT_VAR_NAME);
031e5cce
SM
390 }
391
392 FreePool(sbat);
393
394 return efi_status;
395}
396
397// vim:fenc=utf-8:tw=75:noet