1 // SPDX-License-Identifier: BSD-2-Clause-Patent
3 * sbat.c - parse SBAT data from the .sbat section data
9 UINT32 previous_offset
;
11 } sbat_var_payload_header
;
14 parse_sbat_section(char *section_base
, size_t section_size
,
16 struct sbat_section_entry
***entriesp
)
18 struct sbat_section_entry
*entry
= NULL
, **entries
;
19 EFI_STATUS efi_status
= EFI_SUCCESS
;
20 list_t csv
, *pos
= NULL
;
21 char * end
= section_base
+ section_size
- 1;
26 if (!section_base
|| !section_size
|| !n_entries
|| !entriesp
) {
27 dprint(L
"section_base:0x%lx section_size:0x%lx\n",
28 section_base
, section_size
);
29 dprint(L
"n_entries:0x%lx entriesp:0x%lx\n",
31 return EFI_INVALID_PARAMETER
;
37 parse_csv_data(section_base
, end
, SBAT_SECTION_COLUMNS
, &csv
);
38 if (EFI_ERROR(efi_status
)) {
39 dprint(L
"parse_csv_data failed: %r\n", efi_status
);
44 list_for_each(pos
, &csv
) {
48 row
= list_entry(pos
, struct csv_row
, list
);
50 if (row
->n_columns
< SBAT_SECTION_COLUMNS
) {
51 efi_status
= EFI_INVALID_PARAMETER
;
52 dprint(L
"row->n_columns:%lu SBAT_SECTION_COLUMNS:%lu\n",
53 row
->n_columns
, SBAT_SECTION_COLUMNS
);
57 allocsz
+= sizeof(struct sbat_section_entry
*);
58 allocsz
+= sizeof(struct sbat_section_entry
);
59 for (i
= 0; i
< row
->n_columns
; i
++) {
60 if (row
->columns
[i
][0] == '\000') {
61 dprint(L
"row[%lu].columns[%lu][0] == '\\000'\n", n
, i
);
62 efi_status
= EFI_INVALID_PARAMETER
;
65 allocsz
+= strlen(row
->columns
[i
]) + 1;
70 strtab
= AllocateZeroPool(allocsz
);
72 efi_status
= EFI_OUT_OF_RESOURCES
;
76 entries
= (struct sbat_section_entry
**)strtab
;
77 strtab
+= sizeof(struct sbat_section_entry
*) * n
;
78 entry
= (struct sbat_section_entry
*)strtab
;
79 strtab
+= sizeof(struct sbat_section_entry
) * n
;
82 list_for_each(pos
, &csv
) {
85 const char **ptrs
[] = {
86 &entry
->component_name
,
87 &entry
->component_generation
,
89 &entry
->vendor_package_name
,
90 &entry
->vendor_version
,
95 row
= list_entry(pos
, struct csv_row
, list
);
96 for (i
= 0; i
< row
->n_columns
; i
++) {
98 strtab
= stpcpy(strtab
, row
->columns
[i
]) + 1;
112 cleanup_sbat_section_entries(size_t n
, struct sbat_section_entry
**entries
)
121 verify_single_entry(struct sbat_section_entry
*entry
, struct sbat_var_entry
*sbat_var_entry
, bool *found
)
123 UINT16 sbat_gen
, sbat_var_gen
;
125 if (strcmp((const char *)entry
->component_name
, (const char *)sbat_var_entry
->component_name
) == 0) {
126 dprint(L
"component %a has a matching SBAT variable entry, verifying\n",
127 entry
->component_name
);
131 * atoi returns zero for failed conversion, so essentially
132 * badly parsed component_generation will be treated as zero
134 sbat_gen
= atoi((const char *)entry
->component_generation
);
135 sbat_var_gen
= atoi((const char *)sbat_var_entry
->component_generation
);
137 if (sbat_gen
< sbat_var_gen
) {
138 dprint(L
"component %a, generation %d, was revoked by %s variable\n",
139 entry
->component_name
, sbat_gen
, SBAT_VAR_NAME
);
140 LogError(L
"image did not pass SBAT verification\n");
141 return EFI_SECURITY_VIOLATION
;
148 cleanup_sbat_var(list_t
*entries
)
150 list_t
*pos
= NULL
, *tmp
= NULL
;
151 struct sbat_var_entry
*entry
;
154 list_for_each_safe(pos
, tmp
, entries
) {
155 entry
= list_entry(pos
, struct sbat_var_entry
, list
);
157 if (first
== NULL
|| (uintptr_t)entry
< (uintptr_t)first
)
160 list_del(&entry
->list
);
167 verify_sbat_helper(list_t
*local_sbat_var
, size_t n
, struct sbat_section_entry
**entries
)
171 EFI_STATUS efi_status
= EFI_SUCCESS
;
172 struct sbat_var_entry
*sbat_var_entry
;
174 if (list_empty(local_sbat_var
)) {
175 dprint(L
"%s variable not present\n", SBAT_VAR_NAME
);
179 for (i
= 0; i
< n
; i
++) {
180 list_for_each(pos
, local_sbat_var
) {
182 sbat_var_entry
= list_entry(pos
, struct sbat_var_entry
, list
);
183 efi_status
= verify_single_entry(entries
[i
], sbat_var_entry
, &found
);
184 if (EFI_ERROR(efi_status
))
192 dprint(L
"finished verifying SBAT data: %r\n", efi_status
);
197 verify_sbat(size_t n
, struct sbat_section_entry
**entries
)
199 EFI_STATUS efi_status
;
201 efi_status
= verify_sbat_helper(&sbat_var
, n
, entries
);
206 parse_sbat_var_data(list_t
*entry_list
, UINT8
*data
, UINTN datasize
)
208 struct sbat_var_entry
*entry
= NULL
, **entries
;
209 EFI_STATUS efi_status
= EFI_SUCCESS
;
210 list_t csv
, *pos
= NULL
;
211 char * start
= (char *)data
;
212 char * end
= (char *)data
+ datasize
- 1;
217 if (!entry_list
|| !data
|| datasize
== 0)
218 return EFI_INVALID_PARAMETER
;
220 INIT_LIST_HEAD(&csv
);
222 efi_status
= parse_csv_data(start
, end
, SBAT_VAR_COLUMNS
, &csv
);
223 if (EFI_ERROR(efi_status
)) {
228 list_for_each(pos
, &csv
) {
229 struct csv_row
* row
;
232 row
= list_entry(pos
, struct csv_row
, list
);
234 if (row
->n_columns
< SBAT_VAR_REQUIRED_COLUMNS
) {
235 efi_status
= EFI_INVALID_PARAMETER
;
240 allocsz
+= sizeof(struct sbat_var_entry
*);
241 allocsz
+= sizeof(struct sbat_var_entry
);
242 for (i
= 0; i
< row
->n_columns
; i
++) {
243 if (!row
->columns
[i
][0]) {
244 efi_status
= EFI_INVALID_PARAMETER
;
247 allocsz
+= strlen(row
->columns
[i
]) + 1;
252 strtab
= AllocateZeroPool(allocsz
);
254 efi_status
= EFI_OUT_OF_RESOURCES
;
258 INIT_LIST_HEAD(entry_list
);
260 entry
= (struct sbat_var_entry
*)strtab
;
261 strtab
+= sizeof(struct sbat_var_entry
) * n
;
262 entries
= (struct sbat_var_entry
**)strtab
;
263 strtab
+= sizeof(struct sbat_var_entry
*) * n
;
266 list_for_each(pos
, &csv
) {
267 struct csv_row
* row
;
269 const char **ptrs
[] = {
270 &entry
->component_name
,
271 &entry
->component_generation
,
272 &entry
->sbat_datestamp
,
275 row
= list_entry(pos
, struct csv_row
, list
);
276 for (i
= 0; i
< row
->n_columns
; i
++) {
278 strtab
= stpcpy(strtab
, row
->columns
[i
]) + 1;
280 INIT_LIST_HEAD(&entry
->list
);
281 list_add_tail(&entry
->list
, entry_list
);
292 parse_sbat_var(list_t
*entries
)
296 EFI_STATUS efi_status
;
300 dprint(L
"entries is NULL\n");
301 return EFI_INVALID_PARAMETER
;
304 efi_status
= get_variable(SBAT_VAR_NAME
, &data
, &datasize
, SHIM_LOCK_GUID
);
305 if (EFI_ERROR(efi_status
)) {
306 LogError(L
"Failed to read SBAT variable\n", efi_status
);
311 * We've intentionally made sure there's a NUL byte on all variable
312 * allocations, so use that here.
314 efi_status
= parse_sbat_var_data(entries
, data
, datasize
+1);
315 if (EFI_ERROR(efi_status
))
318 dprint(L
"SBAT variable entries:\n");
319 list_for_each(pos
, entries
) {
320 struct sbat_var_entry
*entry
;
322 entry
= list_entry(pos
, struct sbat_var_entry
, list
);
323 dprint(L
"%a, %a, %a\n", entry
->component_name
,
324 entry
->component_generation
, entry
->sbat_datestamp
);
331 check_sbat_var_attributes(UINT32 attributes
)
333 #ifdef ENABLE_SHIM_DEVEL
334 return attributes
== UEFI_VAR_NV_BS_RT
;
336 return attributes
== UEFI_VAR_NV_BS
||
337 attributes
== UEFI_VAR_NV_BS_TIMEAUTH
;
342 nth_sbat_field(char *str
, size_t limit
, int n
)
345 for (i
= 0; i
< limit
&& str
[i
] != '\0'; i
++) {
355 preserve_sbat_uefi_variable(UINT8
*sbat
, UINTN sbatsize
, UINT32 attributes
,
358 char *sbatc
= (char *)sbat
;
359 char *current_version
, *new_version
,
360 *current_datestamp
, *new_datestamp
;
361 int current_version_len
, new_version_len
;
363 /* current metadata is not currupt somehow */
364 if (!check_sbat_var_attributes(attributes
) ||
365 sbatsize
< strlen(SBAT_VAR_ORIGINAL
) ||
366 strncmp(sbatc
, SBAT_VAR_SIG
, strlen(SBAT_VAR_SIG
)))
369 /* current metadata version not newer */
370 current_version
= nth_sbat_field(sbatc
, sbatsize
, 1);
371 new_version
= nth_sbat_field(sbat_var
, strlen(sbat_var
)+1, 1);
372 current_datestamp
= nth_sbat_field(sbatc
, sbatsize
, 2);
373 new_datestamp
= nth_sbat_field(sbat_var
, strlen(sbat_var
)+1, 2);
375 current_version_len
= current_datestamp
- current_version
- 1;
376 new_version_len
= new_datestamp
- new_version
- 1;
378 if (current_version_len
> new_version_len
||
379 (current_version_len
== new_version_len
&&
380 strncmp(current_version
, new_version
, new_version_len
) > 0))
383 /* current datestamp is not newer or idential */
384 if (strncmp(current_datestamp
, new_datestamp
,
385 strlen(SBAT_VAR_ORIGINAL_DATE
)) >= 0)
394 EFI_STATUS efi_status
= EFI_SUCCESS
;
396 efi_status
= del_variable(SBAT_POLICY
, SHIM_LOCK_GUID
);
397 if (EFI_ERROR(efi_status
))
398 console_error(L
"Could not reset SBAT Policy", efi_status
);
402 set_sbat_uefi_variable(void)
404 EFI_STATUS efi_status
= EFI_SUCCESS
;
405 UINT32 attributes
= 0;
407 char *sbat_var_previous
;
408 char *sbat_var_latest
;
411 UINT8
*sbat_policy
= NULL
;
413 UINTN sbat_policysize
= 0;
415 char *sbat_var
= NULL
;
416 bool reset_sbat
= false;
418 sbat_var_previous
= (char *)&sbat_var_payload_header
+ sbat_var_payload_header
.previous_offset
;
419 sbat_var_latest
= (char *)&sbat_var_payload_header
+ sbat_var_payload_header
.latest_offset
;
421 efi_status
= get_variable_attr(SBAT_POLICY
, &sbat_policy
,
422 &sbat_policysize
, SHIM_LOCK_GUID
,
424 if (EFI_ERROR(efi_status
)) {
425 dprint("Default sbat policy: previous\n");
426 sbat_var
= sbat_var_previous
;
428 switch (*sbat_policy
) {
429 case SBAT_POLICY_LATEST
:
430 dprint("Custom sbat policy: latest\n");
431 sbat_var
= sbat_var_latest
;
434 case SBAT_POLICY_PREVIOUS
:
435 dprint("Custom sbat policy: previous\n");
436 sbat_var
= sbat_var_previous
;
438 case SBAT_POLICY_RESET
:
440 console_print(L
"Cannot reset SBAT policy: Secure Boot is enabled.\n");
441 sbat_var
= sbat_var_previous
;
443 dprint(L
"Custom SBAT policy: reset OK\n");
445 sbat_var
= SBAT_VAR_ORIGINAL
;
450 console_error(L
"SBAT policy state %llu is invalid",
451 EFI_INVALID_PARAMETER
);
452 sbat_var
= sbat_var_previous
;
458 efi_status
= get_variable_attr(SBAT_VAR_NAME
, &sbat
, &sbatsize
,
459 SHIM_LOCK_GUID
, &attributes
);
461 * Always set the SbatLevel UEFI variable if it fails to read.
463 * Don't try to set the SbatLevel UEFI variable if attributes match
464 * and the signature matches.
466 if (EFI_ERROR(efi_status
)) {
467 dprint(L
"SBAT read failed %r\n", efi_status
);
468 } else if (preserve_sbat_uefi_variable(sbat
, sbatsize
, attributes
, sbat_var
)
470 dprint(L
"preserving %s variable it is %d bytes, attributes are 0x%08x\n",
471 SBAT_VAR_NAME
, sbatsize
, attributes
);
477 /* delete previous variable */
478 dprint("%s variable is %d bytes, attributes are 0x%08x\n",
479 SBAT_VAR_NAME
, sbatsize
, attributes
);
480 dprint("Deleting %s variable.\n", SBAT_VAR_NAME
);
481 efi_status
= set_variable(SBAT_VAR_NAME
, SHIM_LOCK_GUID
,
483 if (EFI_ERROR(efi_status
)) {
484 dprint(L
"%s variable delete failed %r\n", SBAT_VAR_NAME
,
491 efi_status
= set_variable(SBAT_VAR_NAME
, SHIM_LOCK_GUID
, SBAT_VAR_ATTRS
,
492 strlen(sbat_var
), sbat_var
);
493 if (EFI_ERROR(efi_status
)) {
494 dprint(L
"%s variable writing failed %r\n", SBAT_VAR_NAME
,
499 /* verify that the expected data is there */
500 efi_status
= get_variable(SBAT_VAR_NAME
, &sbat
, &sbatsize
,
502 if (EFI_ERROR(efi_status
)) {
503 dprint(L
"%s read failed %r\n", SBAT_VAR_NAME
, efi_status
);
507 if (sbatsize
!= strlen(sbat_var
) ||
508 strncmp((const char *)sbat
, sbat_var
, strlen(sbat_var
)) != 0) {
509 dprint("new sbatsize is %d, expected %d\n", sbatsize
,
511 efi_status
= EFI_INVALID_PARAMETER
;
513 dprint(L
"%s variable initialization succeeded\n", SBAT_VAR_NAME
);
521 // vim:fenc=utf-8:tw=75:noet