1 // SPDX-License-Identifier: BSD-2-Clause-Patent
3 * sbat.c - parse SBAT data from the .sbat section data
9 parse_sbat_section(char *section_base
, size_t section_size
,
11 struct sbat_section_entry
***entriesp
)
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;
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",
26 return EFI_INVALID_PARAMETER
;
32 parse_csv_data(section_base
, end
, SBAT_SECTION_COLUMNS
, &csv
);
33 if (EFI_ERROR(efi_status
)) {
34 dprint(L
"parse_csv_data failed: %r\n", efi_status
);
39 list_for_each(pos
, &csv
) {
43 row
= list_entry(pos
, struct csv_row
, list
);
45 if (row
->n_columns
< SBAT_SECTION_COLUMNS
) {
46 efi_status
= EFI_INVALID_PARAMETER
;
47 dprint(L
"row->n_columns:%lu SBAT_SECTION_COLUMNS:%lu\n",
48 row
->n_columns
, SBAT_SECTION_COLUMNS
);
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') {
56 dprint(L
"row[%lu].columns[%lu][0] == '\\000'\n", n
, i
);
57 efi_status
= EFI_INVALID_PARAMETER
;
60 allocsz
+= strlen(row
->columns
[i
]) + 1;
65 strtab
= AllocateZeroPool(allocsz
);
67 efi_status
= EFI_OUT_OF_RESOURCES
;
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
;
77 list_for_each(pos
, &csv
) {
80 const char **ptrs
[] = {
81 &entry
->component_name
,
82 &entry
->component_generation
,
84 &entry
->vendor_package_name
,
85 &entry
->vendor_version
,
90 row
= list_entry(pos
, struct csv_row
, list
);
91 for (i
= 0; i
< row
->n_columns
; i
++) {
93 strtab
= stpcpy(strtab
, row
->columns
[i
]) + 1;
107 cleanup_sbat_section_entries(size_t n
, struct sbat_section_entry
**entries
)
116 verify_single_entry(struct sbat_section_entry
*entry
, struct sbat_var_entry
*sbat_var_entry
)
118 UINT16 sbat_gen
, sbat_var_gen
;
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
);
125 * atoi returns zero for failed conversion, so essentially
126 * badly parsed component_generation will be treated as zero
128 sbat_gen
= atoi((const char *)entry
->component_generation
);
129 sbat_var_gen
= atoi((const char *)sbat_var_entry
->component_generation
);
131 if (sbat_gen
< sbat_var_gen
) {
132 dprint(L
"component %a, generation %d, was revoked by %s variable\n",
133 entry
->component_name
, sbat_gen
, SBAT_VAR_NAME
);
134 LogError(L
"image did not pass SBAT verification\n");
135 return EFI_SECURITY_VIOLATION
;
142 cleanup_sbat_var(list_t
*entries
)
144 list_t
*pos
= NULL
, *tmp
= NULL
;
145 struct sbat_var_entry
*entry
;
148 list_for_each_safe(pos
, tmp
, entries
) {
149 entry
= list_entry(pos
, struct sbat_var_entry
, list
);
151 if (first
== NULL
|| (uintptr_t)entry
< (uintptr_t)first
)
154 list_del(&entry
->list
);
161 verify_sbat_helper(list_t
*local_sbat_var
, size_t n
, struct sbat_section_entry
**entries
)
165 EFI_STATUS efi_status
= EFI_SUCCESS
;
166 struct sbat_var_entry
*sbat_var_entry
;
168 if (list_empty(local_sbat_var
)) {
169 dprint(L
"%s variable not present\n", SBAT_VAR_NAME
);
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
))
183 dprint(L
"finished verifying SBAT data: %r\n", efi_status
);
188 verify_sbat(size_t n
, struct sbat_section_entry
**entries
)
190 EFI_STATUS efi_status
;
192 efi_status
= verify_sbat_helper(&sbat_var
, n
, entries
);
197 parse_sbat_var_data(list_t
*entry_list
, UINT8
*data
, UINTN datasize
)
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;
208 if (!entry_list
|| !data
|| datasize
== 0)
209 return EFI_INVALID_PARAMETER
;
211 INIT_LIST_HEAD(&csv
);
213 efi_status
= parse_csv_data(start
, end
, SBAT_VAR_COLUMNS
, &csv
);
214 if (EFI_ERROR(efi_status
)) {
219 list_for_each(pos
, &csv
) {
220 struct csv_row
* row
;
223 row
= list_entry(pos
, struct csv_row
, list
);
225 if (row
->n_columns
< SBAT_VAR_REQUIRED_COLUMNS
) {
226 efi_status
= EFI_INVALID_PARAMETER
;
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
;
238 allocsz
+= strlen(row
->columns
[i
]) + 1;
243 strtab
= AllocateZeroPool(allocsz
);
245 efi_status
= EFI_OUT_OF_RESOURCES
;
249 INIT_LIST_HEAD(entry_list
);
251 entry
= (struct sbat_var_entry
*)strtab
;
252 strtab
+= sizeof(struct sbat_var_entry
) * n
;
253 entries
= (struct sbat_var_entry
**)strtab
;
254 strtab
+= sizeof(struct sbat_var_entry
*) * n
;
257 list_for_each(pos
, &csv
) {
258 struct csv_row
* row
;
260 const char **ptrs
[] = {
261 &entry
->component_name
,
262 &entry
->component_generation
,
263 &entry
->sbat_datestamp
,
266 row
= list_entry(pos
, struct csv_row
, list
);
267 for (i
= 0; i
< row
->n_columns
; i
++) {
269 strtab
= stpcpy(strtab
, row
->columns
[i
]) + 1;
271 INIT_LIST_HEAD(&entry
->list
);
272 list_add_tail(&entry
->list
, entry_list
);
283 parse_sbat_var(list_t
*entries
)
287 EFI_STATUS efi_status
;
290 dprint(L
"entries is NULL\n");
291 return EFI_INVALID_PARAMETER
;
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
);
301 * We've intentionally made sure there's a NUL byte on all variable
302 * allocations, so use that here.
304 return parse_sbat_var_data(entries
, data
, datasize
+1);
308 check_sbat_var_attributes(UINT32 attributes
)
310 #ifdef ENABLE_SHIM_DEVEL
311 return attributes
== UEFI_VAR_NV_BS_RT
;
313 return attributes
== UEFI_VAR_NV_BS
||
314 attributes
== UEFI_VAR_NV_BS_TIMEAUTH
;
319 preserve_sbat_uefi_variable(UINT8
*sbat
, UINTN sbatsize
, UINT32 attributes
)
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
));
327 set_sbat_uefi_variable(void)
329 EFI_STATUS efi_status
= EFI_SUCCESS
;
330 UINT32 attributes
= 0;
335 efi_status
= get_variable_attr(SBAT_VAR_NAME
, &sbat
, &sbatsize
,
336 SHIM_LOCK_GUID
, &attributes
);
338 * Always set the SbatLevel UEFI variable if it fails to read.
340 * Don't try to set the SbatLevel UEFI variable if attributes match
341 * and the signature matches.
343 if (EFI_ERROR(efi_status
)) {
344 dprint(L
"SBAT read failed %r\n", efi_status
);
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
);
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
,
359 if (EFI_ERROR(efi_status
)) {
360 dprint(L
"%s variable delete failed %r\n", SBAT_VAR_NAME
,
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
)) {
370 dprint(L
"%s variable writing failed %r\n", SBAT_VAR_NAME
,
375 /* verify that the expected data is there */
376 efi_status
= get_variable(SBAT_VAR_NAME
, &sbat
, &sbatsize
,
378 if (EFI_ERROR(efi_status
)) {
379 dprint(L
"%s read failed %r\n", SBAT_VAR_NAME
, efi_status
);
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
,
387 efi_status
= EFI_INVALID_PARAMETER
;
389 dprint(L
"%s variable initialization succeeded\n", SBAT_VAR_NAME
);
397 // vim:fenc=utf-8:tw=75:noet